Skip to content

Commit 7acf22a

Browse files
fix(autocomplete): clear panel query in instantsearch.js
1 parent 4f1cabe commit 7acf22a

File tree

2 files changed

+91
-25
lines changed

2 files changed

+91
-25
lines changed

packages/instantsearch.js/src/widgets/autocomplete/autocomplete.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ function AutocompleteWrapper<TItem extends BaseHit>({
190190
indices,
191191
getSearchPageURL,
192192
onSelect: userOnSelect,
193-
refine,
193+
refine: refineAutocomplete,
194194
cssClasses,
195195
renderState,
196196
instantSearchInstance,
@@ -223,6 +223,7 @@ function AutocompleteWrapper<TItem extends BaseHit>({
223223
) ?? false;
224224

225225
const onRefine = (query: string) => {
226+
refineAutocomplete(query);
226227
instantSearchInstance.setUiState((uiState) => ({
227228
...uiState,
228229
[targetIndex!.getIndexId()]: {
@@ -388,9 +389,11 @@ function AutocompleteWrapper<TItem extends BaseHit>({
388389
...getInputProps(),
389390
// @ts-ignore - This clashes with some ambient React JSX declarations.
390391
onInput: (evt: JSXPreact.TargetedEvent<HTMLInputElement>) =>
391-
refine(evt.currentTarget.value),
392+
refineAutocomplete(evt.currentTarget.value),
393+
}}
394+
onClear={() => {
395+
onRefine('');
392396
}}
393-
onClear={() => onRefine('')}
394397
isSearchStalled={instantSearchInstance.status === 'stalled'}
395398
/>
396399
<AutocompletePanel {...getPanelProps()}>

tests/common/widgets/autocomplete/options.tsx

Lines changed: 85 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -139,25 +139,15 @@ export function createOptionsTests(
139139
await wait(0);
140140
});
141141

142-
const callTimes: Record<string, Record<SupportedFlavor, number>> = {
143-
initial: { javascript: 2, react: 2, vue: 0 },
144-
refined: { javascript: 1, react: 2, vue: 0 },
145-
};
146-
147-
expect(searchClient.search).toHaveBeenCalledTimes(
148-
callTimes.initial[flavor]
149-
);
150-
expect(searchClient.search).toHaveBeenNthCalledWith(
151-
callTimes.initial[flavor] - 1,
152-
[
153-
{
154-
indexName: 'query_suggestions',
155-
params: expect.objectContaining({
156-
query: '',
157-
}),
158-
},
159-
]
160-
);
142+
expect(searchClient.search).toHaveBeenCalledTimes(2);
143+
expect(searchClient.search).toHaveBeenNthCalledWith(1, [
144+
{
145+
indexName: 'query_suggestions',
146+
params: expect.objectContaining({
147+
query: '',
148+
}),
149+
},
150+
]);
161151
(searchClient.search as jest.Mock).mockClear();
162152

163153
expect(screen.getAllByRole('row', { hidden: true }).length).toBe(2);
@@ -174,9 +164,7 @@ export function createOptionsTests(
174164
await wait(0);
175165
});
176166

177-
expect(searchClient.search).toHaveBeenCalledTimes(
178-
callTimes.refined[flavor]
179-
);
167+
expect(searchClient.search).toHaveBeenCalledTimes(2);
180168
expect(searchClient.search).toHaveBeenLastCalledWith([
181169
{
182170
indexName: 'query_suggestions',
@@ -788,6 +776,81 @@ export function createOptionsTests(
788776
expect(input).toHaveFocus();
789777
});
790778

779+
test('clearing the query also clears internal autocomplete query', async () => {
780+
const searchClient = createMockedSearchClient(
781+
createMultiSearchResponse(
782+
createSingleSearchResponse({
783+
index: 'indexName',
784+
hits: [
785+
{ objectID: '1', name: 'Item 1' },
786+
{ objectID: '2', name: 'Item 2' },
787+
],
788+
})
789+
)
790+
);
791+
792+
await setup({
793+
instantSearchOptions: {
794+
indexName: 'indexName',
795+
searchClient,
796+
},
797+
widgetParams: {
798+
javascript: {
799+
indices: [
800+
{
801+
indexName: 'indexName',
802+
templates: {
803+
item: (props) => props.item.name,
804+
},
805+
},
806+
],
807+
},
808+
react: {
809+
indices: [
810+
{
811+
indexName: 'indexName',
812+
itemComponent: (props) => props.item.name,
813+
},
814+
],
815+
},
816+
vue: {},
817+
},
818+
});
819+
820+
await act(async () => {
821+
await wait(0);
822+
});
823+
824+
const input = screen.getByRole('combobox', { name: /submit/i });
825+
826+
await act(async () => {
827+
userEvent.click(input);
828+
userEvent.type(input, 'Item 3');
829+
userEvent.keyboard('{Enter}');
830+
await wait(0);
831+
userEvent.keyboard('{Enter}');
832+
await wait(0);
833+
});
834+
835+
expect(input).not.toHaveFocus();
836+
expect(input).toHaveAttribute('aria-expanded', 'false');
837+
expect(screen.getByRole('button', { name: /clear/i })).toBeVisible();
838+
839+
await act(async () => {
840+
userEvent.click(screen.getByRole('button', { name: /clear/i }));
841+
await wait(0);
842+
});
843+
844+
expect(searchClient.search).toHaveBeenLastCalledWith([
845+
{
846+
indexName: 'indexName',
847+
params: expect.objectContaining({
848+
query: '',
849+
}),
850+
},
851+
]);
852+
});
853+
791854
test('refocuses the input after clearing the query', async () => {
792855
const searchClient = createMockedSearchClient(
793856
createMultiSearchResponse(

0 commit comments

Comments
 (0)