Skip to content

Commit eace506

Browse files
tkirdatkirda-bisonclaude
authored
fix: fire onSearchComplete before onSelect when auto-selecting (#870)
When a search returns a single suggestion whose value exactly matches the current query and `triggerSelectOnValidInput` is true (the default), `suggest()` calls `select(0)` which fires `onSelect`. Previously, the three response paths (function lookup, cached/local, ajax) all fired `onSearchComplete` after `suggest()`, so consumers received the events in the wrong order — onSelect before onSearchComplete — making any cleanup logic in onSearchComplete clobber state set during onSelect. Move `onSearchComplete` ahead of `suggest()` (and `processResponse()` on the ajax path, which contains `suggest()`) so the events fire in the natural order: search-complete, then any resulting select. Closes #852. Co-authored-by: Tomas Kirda <tomas.kirda@bisoncommerce.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 85796a9 commit eace506

5 files changed

Lines changed: 33 additions & 10 deletions

File tree

dist/jquery.autocomplete.esm.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -400,8 +400,8 @@ var _Autocomplete = class _Autocomplete {
400400
if (typeof options.lookup === "function") {
401401
options.lookup(q, (data) => {
402402
this.suggestions = data.suggestions;
403-
this.suggest();
404403
options.onSearchComplete.call(this.element, q, data.suggestions);
404+
this.suggest();
405405
});
406406
return;
407407
}
@@ -416,8 +416,8 @@ var _Autocomplete = class _Autocomplete {
416416
}
417417
if (response && Array.isArray(response.suggestions)) {
418418
this.suggestions = response.suggestions;
419-
this.suggest();
420419
options.onSearchComplete.call(this.element, q, response.suggestions);
420+
this.suggest();
421421
} else if (!this.isBadQuery(q)) {
422422
this.abortAjax();
423423
const ajaxSettings = {
@@ -430,8 +430,8 @@ var _Autocomplete = class _Autocomplete {
430430
this.currentRequest = $.ajax(ajaxSettings).done((data) => {
431431
this.currentRequest = null;
432432
const result = options.transformResult(data, q);
433-
this.processResponse(result, q, cacheKey);
434433
options.onSearchComplete.call(this.element, q, result.suggestions);
434+
this.processResponse(result, q, cacheKey);
435435
}).fail((jqXHR, textStatus, errorThrown) => {
436436
options.onSearchError.call(this.element, q, jqXHR, textStatus, errorThrown);
437437
});

dist/jquery.autocomplete.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -408,8 +408,8 @@
408408
if (typeof options.lookup === "function") {
409409
options.lookup(q, (data) => {
410410
this.suggestions = data.suggestions;
411-
this.suggest();
412411
options.onSearchComplete.call(this.element, q, data.suggestions);
412+
this.suggest();
413413
});
414414
return;
415415
}
@@ -424,8 +424,8 @@
424424
}
425425
if (response && Array.isArray(response.suggestions)) {
426426
this.suggestions = response.suggestions;
427-
this.suggest();
428427
options.onSearchComplete.call(this.element, q, response.suggestions);
428+
this.suggest();
429429
} else if (!this.isBadQuery(q)) {
430430
this.abortAjax();
431431
const ajaxSettings = {
@@ -438,8 +438,8 @@
438438
this.currentRequest = $2.ajax(ajaxSettings).done((data) => {
439439
this.currentRequest = null;
440440
const result = options.transformResult(data, q);
441-
this.processResponse(result, q, cacheKey);
442441
options.onSearchComplete.call(this.element, q, result.suggestions);
442+
this.processResponse(result, q, cacheKey);
443443
}).fail((jqXHR, textStatus, errorThrown) => {
444444
options.onSearchError.call(this.element, q, jqXHR, textStatus, errorThrown);
445445
});

dist/jquery.autocomplete.min.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Autocomplete.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -416,8 +416,10 @@ export class Autocomplete {
416416
if (typeof options.lookup === "function") {
417417
(options.lookup as LookupCallback)(q, (data) => {
418418
this.suggestions = data.suggestions;
419-
this.suggest();
419+
// Fire onSearchComplete before suggest() so consumers see
420+
// "search complete" before any auto-select fires onSelect.
420421
options.onSearchComplete.call(this.element, q, data.suggestions);
422+
this.suggest();
421423
});
422424
return;
423425
}
@@ -434,8 +436,8 @@ export class Autocomplete {
434436

435437
if (response && Array.isArray(response.suggestions)) {
436438
this.suggestions = response.suggestions;
437-
this.suggest();
438439
options.onSearchComplete.call(this.element, q, response.suggestions);
440+
this.suggest();
439441
} else if (!this.isBadQuery(q)) {
440442
this.abortAjax();
441443

@@ -451,8 +453,8 @@ export class Autocomplete {
451453
.done((data) => {
452454
this.currentRequest = null;
453455
const result = options.transformResult(data, q);
454-
this.processResponse(result, q, cacheKey!);
455456
options.onSearchComplete.call(this.element, q, result.suggestions);
457+
this.processResponse(result, q, cacheKey!);
456458
})
457459
.fail((jqXHR, textStatus, errorThrown) => {
458460
options.onSearchError.call(this.element, q, jqXHR, textStatus, errorThrown);

test/autocomplete.test.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -708,6 +708,27 @@ describe("Autocomplete", () => {
708708
});
709709
});
710710

711+
describe("Autocomplete event ordering", () => {
712+
afterEach(() => {
713+
$(".autocomplete-suggestions").remove();
714+
});
715+
716+
it("fires onSearchComplete before onSelect when a single match auto-selects", () => {
717+
const input = document.createElement("input");
718+
const calls = [];
719+
const autocomplete = new $.Autocomplete(input, {
720+
lookup: [{ value: "Apple", data: 1 }],
721+
onSearchComplete: () => calls.push("searchComplete"),
722+
onSelect: () => calls.push("select"),
723+
});
724+
725+
input.value = "Apple";
726+
autocomplete.onValueChange();
727+
728+
expect(calls).toEqual(["searchComplete", "select"]);
729+
});
730+
});
731+
711732
describe("Autocomplete groupBy", () => {
712733
afterEach(() => {
713734
$(".autocomplete-suggestions").remove();

0 commit comments

Comments
 (0)