Skip to content

Commit 8ae87d8

Browse files
samoussHaroenv
authored andcommitted
feat(connectAutocomplete): clear the state on dispose (#3815)
* test(connectAutocomplete): call unmount on dispose * test(connectAutocomplete): detach the DerivedHelpers on dispose * test(connectAutocomplete): rename jsHelper -> algoliasearchHelper * feat(connectAutocomplete): remove query on dispose * test(connectAutocomplete): remove only DerivedHelper created by autocomplete * feat(connectAutocomplete): do not throw without unmount function * feat(connectAutocomplete): remove TAG_PLACEHOLDER on dispose * feat(connectAutocomplete): remove TAG_PLACEHOLDER on dispose only with `escapeHTML` * test(connectAutocomplete): update test description
1 parent d7a5c89 commit 8ae87d8

File tree

2 files changed

+173
-16
lines changed

2 files changed

+173
-16
lines changed

src/connectors/autocomplete/__tests__/connectAutocomplete-test.js

Lines changed: 156 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import jsHelper, { SearchResults } from 'algoliasearch-helper';
1+
import algoliasearchHelper, { SearchResults } from 'algoliasearch-helper';
22
import connectAutocomplete from '../connectAutocomplete';
33
import { TAG_PLACEHOLDER } from '../../../lib/escape-highlight';
44

@@ -22,7 +22,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/autocomplet
2222

2323
expect(renderFn).toHaveBeenCalledTimes(0);
2424

25-
const helper = jsHelper(fakeClient, '', {});
25+
const helper = algoliasearchHelper(fakeClient, '', {});
2626
helper.search = jest.fn();
2727

2828
widget.init({
@@ -54,12 +54,12 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/autocomplet
5454
});
5555
});
5656

57-
it('creates derived helper', () => {
57+
it('creates DerivedHelper', () => {
5858
const renderFn = jest.fn();
5959
const makeWidget = connectAutocomplete(renderFn);
6060
const widget = makeWidget({ indices: [{ label: 'foo', value: 'foo' }] });
6161

62-
const helper = jsHelper(fakeClient, '', {});
62+
const helper = algoliasearchHelper(fakeClient, '', {});
6363
helper.search = jest.fn();
6464

6565
widget.init({ helper, instantSearchInstance: {} });
@@ -75,7 +75,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/autocomplet
7575
const makeWidget = connectAutocomplete(renderFn);
7676
const widget = makeWidget();
7777

78-
const helper = jsHelper(fakeClient, '', {});
78+
const helper = algoliasearchHelper(fakeClient, '', {});
7979
helper.search = jest.fn();
8080

8181
widget.init({ helper, instantSearchInstance: {} });
@@ -93,7 +93,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/autocomplet
9393
const makeWidget = connectAutocomplete(renderFn);
9494
const widget = makeWidget({ escapeHTML: true });
9595

96-
const helper = jsHelper(fakeClient, '', {});
96+
const helper = algoliasearchHelper(fakeClient, '', {});
9797
helper.search = jest.fn();
9898

9999
const hits = [
@@ -137,7 +137,7 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/autocomplet
137137
const makeWidget = connectAutocomplete(renderFn);
138138
const widget = makeWidget({ escapeHTML: false });
139139

140-
const helper = jsHelper(fakeClient, '', {});
140+
const helper = algoliasearchHelper(fakeClient, '', {});
141141
helper.search = jest.fn();
142142

143143
const hits = [
@@ -164,12 +164,154 @@ See documentation: https://www.algolia.com/doc/api-reference/widgets/autocomplet
164164
expect(rendering.indices[0].hits).toEqual(hits);
165165
});
166166

167-
it('does not throw without the unmount function', () => {
168-
const helper = jsHelper(fakeClient, '', {});
169-
const renderFn = () => {};
170-
const makeWidget = connectAutocomplete(renderFn);
171-
const widget = makeWidget({});
172-
widget.init({ helper, instantSearchInstance: {} });
173-
expect(() => widget.dispose({ helper, state: helper.state })).not.toThrow();
167+
describe('dispose', () => {
168+
it('calls the unmount function', () => {
169+
const helper = algoliasearchHelper(fakeClient, 'firstIndex');
170+
171+
const renderFn = () => {};
172+
const unmountFn = jest.fn();
173+
const makeWidget = connectAutocomplete(renderFn, unmountFn);
174+
const widget = makeWidget();
175+
176+
widget.init({ helper, instantSearchInstance: {} });
177+
178+
expect(unmountFn).toHaveBeenCalledTimes(0);
179+
180+
widget.dispose({ helper, state: helper.state });
181+
182+
expect(unmountFn).toHaveBeenCalledTimes(1);
183+
});
184+
185+
it('does not throw without the unmount function', () => {
186+
const helper = algoliasearchHelper(fakeClient, 'firstIndex');
187+
188+
const renderFn = () => {};
189+
const makeWidget = connectAutocomplete(renderFn);
190+
const widget = makeWidget();
191+
192+
widget.init({ helper, instantSearchInstance: {} });
193+
194+
expect(() =>
195+
widget.dispose({ helper, state: helper.state })
196+
).not.toThrow();
197+
});
198+
199+
it('removes the created DerivedHelper', () => {
200+
const detachDerivedHelper = jest.fn();
201+
const helper = algoliasearchHelper(fakeClient, 'firstIndex');
202+
helper.detachDerivedHelper = detachDerivedHelper;
203+
204+
const renderFn = () => {};
205+
const makeWidget = connectAutocomplete(renderFn);
206+
const widget = makeWidget({
207+
indices: [
208+
{ label: 'Second', value: 'secondIndex' },
209+
{ label: 'Third', value: 'thirdIndex' },
210+
],
211+
});
212+
213+
widget.init({ helper, instantSearchInstance: {} });
214+
215+
expect(detachDerivedHelper).toHaveBeenCalledTimes(0);
216+
217+
widget.dispose({ helper, state: helper.state });
218+
219+
expect(detachDerivedHelper).toHaveBeenCalledTimes(2);
220+
});
221+
222+
it('removes only the DerivedHelper created by autocomplete', () => {
223+
const detachDerivedHelper = jest.fn();
224+
const helper = algoliasearchHelper(fakeClient, 'firstIndex');
225+
helper.detachDerivedHelper = detachDerivedHelper;
226+
227+
const renderFn = () => {};
228+
const makeWidget = connectAutocomplete(renderFn);
229+
const widget = makeWidget({
230+
indices: [
231+
{ label: 'Second', value: 'secondIndex' },
232+
{ label: 'Third', value: 'thirdIndex' },
233+
],
234+
});
235+
236+
const derivedHelperOne = helper.derive(state => state);
237+
const derivedHelperTwo = helper.derive(state => state);
238+
239+
widget.init({ helper, instantSearchInstance: {} });
240+
241+
expect(detachDerivedHelper).toHaveBeenCalledTimes(0);
242+
243+
widget.dispose({ helper, state: helper.state });
244+
245+
expect(detachDerivedHelper).toHaveBeenCalledTimes(2);
246+
expect(helper.derivedHelpers).toEqual(
247+
expect.arrayContaining([derivedHelperOne, derivedHelperTwo])
248+
);
249+
});
250+
251+
it('removes the `query` from the `SearchParameters`', () => {
252+
const helper = algoliasearchHelper(fakeClient, 'firstIndex', {
253+
query: 'Apple',
254+
});
255+
256+
const renderFn = () => {};
257+
const makeWidget = connectAutocomplete(renderFn);
258+
const widget = makeWidget();
259+
260+
widget.init({ helper, instantSearchInstance: {} });
261+
262+
expect(helper.state.query).toBe('Apple');
263+
264+
const nextState = widget.dispose({ helper, state: helper.state });
265+
266+
expect(nextState.query).toBeUndefined();
267+
});
268+
269+
it('removes the TAG_PLACEHOLDER from the `SearchParameters`', () => {
270+
const helper = algoliasearchHelper(fakeClient, 'firstIndex', {
271+
...TAG_PLACEHOLDER,
272+
});
273+
274+
const renderFn = () => {};
275+
const makeWidget = connectAutocomplete(renderFn);
276+
const widget = makeWidget();
277+
278+
expect(helper.state.highlightPreTag).toBe(
279+
TAG_PLACEHOLDER.highlightPreTag
280+
);
281+
282+
expect(helper.state.highlightPostTag).toBe(
283+
TAG_PLACEHOLDER.highlightPostTag
284+
);
285+
286+
widget.init({ helper, instantSearchInstance: {} });
287+
288+
const nextState = widget.dispose({ helper, state: helper.state });
289+
290+
expect(nextState.highlightPreTag).toBeUndefined();
291+
expect(nextState.highlightPostTag).toBeUndefined();
292+
});
293+
294+
it('does not remove the TAG_PLACEHOLDER from the `SearchParameters` with `escapeHTML`', () => {
295+
const helper = algoliasearchHelper(fakeClient, 'firstIndex', {
296+
highlightPreTag: '<mark>',
297+
highlightPostTag: '</mark>',
298+
});
299+
300+
const renderFn = () => {};
301+
const makeWidget = connectAutocomplete(renderFn);
302+
const widget = makeWidget({
303+
escapeHTML: false,
304+
});
305+
306+
expect(helper.state.highlightPreTag).toBe('<mark>');
307+
expect(helper.state.highlightPostTag).toBe('</mark>');
308+
309+
widget.init({ helper, instantSearchInstance: {} });
310+
311+
const nextState = widget.dispose({ helper, state: helper.state });
312+
313+
expect(nextState.highlightPreTag).toBe('<mark>');
314+
expect(nextState.highlightPostTag).toBe('</mark>');
315+
});
174316
});
175317
});

src/connectors/autocomplete/connectAutocomplete.js

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,26 @@ export default function connectAutocomplete(renderFn, unmountFn = noop) {
142142
);
143143
},
144144

145-
dispose() {
146-
// detach every derived indices from the main helper instance
145+
dispose({ state }) {
147146
this.indices.slice(1).forEach(({ helper }) => helper.detach());
148147

149148
unmountFn();
149+
150+
const stateWithoutQuery = state.setQueryParameter('query', undefined);
151+
152+
if (!escapeHTML) {
153+
return stateWithoutQuery;
154+
}
155+
156+
return stateWithoutQuery.setQueryParameters(
157+
Object.keys(TAG_PLACEHOLDER).reduce(
158+
(acc, key) => ({
159+
...acc,
160+
[key]: undefined,
161+
}),
162+
{}
163+
)
164+
);
150165
},
151166
};
152167
};

0 commit comments

Comments
 (0)