Skip to content

Commit 2926feb

Browse files
dhayabsarahdayan
andauthored
fix(autocomplete-js): display warning when there are more than one instances of autocomplete (#1108)
Co-authored-by: Sarah Dayan <[email protected]>
1 parent 601e314 commit 2926feb

File tree

3 files changed

+71
-13
lines changed

3 files changed

+71
-13
lines changed

packages/autocomplete-js/src/__tests__/autocomplete.test.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,26 @@ beforeEach(() => {
2323
});
2424

2525
describe('autocomplete-js', () => {
26+
test('warns when more than one instance is detected in a document', () => {
27+
const firstContainer = document.createElement('div');
28+
expect(() =>
29+
autocomplete({
30+
container: firstContainer,
31+
})
32+
).not.toWarnDev();
33+
34+
const secondContainer = document.createElement('div');
35+
expect(() =>
36+
autocomplete({
37+
container: secondContainer,
38+
})
39+
).toWarnDev(
40+
`[Autocomplete] Autocomplete doesn't support multiple instances running at the same time. Make sure to destroy the previous instance before creating a new one.
41+
42+
See: https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-destroy`
43+
);
44+
});
45+
2646
test('renders with default options', () => {
2747
const container = document.createElement('div');
2848
autocomplete<{ label: string }>({

packages/autocomplete-js/src/__tests__/renderer.test.ts

Lines changed: 38 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ describe('renderer', () => {
2424
const panelContainer = document.createElement('div');
2525

2626
document.body.appendChild(panelContainer);
27-
autocomplete<{ label: string }>({
27+
const { destroy } = autocomplete<{ label: string }>({
2828
container,
2929
panelContainer,
3030
initialState: {
@@ -53,6 +53,8 @@ describe('renderer', () => {
5353
render(createElement(Fragment, null, 'testSource'), root);
5454
},
5555
});
56+
57+
destroy();
5658
});
5759

5860
test('accepts a custom renderer', () => {
@@ -66,7 +68,7 @@ describe('renderer', () => {
6668

6769
document.body.appendChild(panelContainer);
6870

69-
autocomplete<{ label: string }>({
71+
const { destroy } = autocomplete<{ label: string }>({
7072
container,
7173
panelContainer,
7274
initialState: {
@@ -110,6 +112,8 @@ describe('renderer', () => {
110112
render: mockRender,
111113
},
112114
});
115+
116+
destroy();
113117
});
114118

115119
test('defaults `render` when not specified in the renderer', () => {
@@ -122,7 +126,7 @@ describe('renderer', () => {
122126

123127
document.body.appendChild(panelContainer);
124128

125-
autocomplete<{ label: string }>({
129+
const { destroy } = autocomplete<{ label: string }>({
126130
container,
127131
panelContainer,
128132
initialState: {
@@ -153,6 +157,8 @@ describe('renderer', () => {
153157
Fragment: CustomFragment,
154158
},
155159
});
160+
161+
destroy();
156162
});
157163

158164
test('uses a custom `render` via `renderer`', async () => {
@@ -165,7 +171,7 @@ describe('renderer', () => {
165171
const mockCreateElement = jest.fn(preactCreateElement);
166172
const mockRender = jest.fn().mockImplementation(preactRender);
167173

168-
autocomplete<{ label: string }>({
174+
const { destroy } = autocomplete<{ label: string }>({
169175
container,
170176
panelContainer,
171177
id: 'autocomplete-0',
@@ -238,6 +244,8 @@ describe('renderer', () => {
238244
</div>
239245
`);
240246
});
247+
248+
destroy();
241249
});
242250

243251
test('warns about renderer mismatch when specifying an incomplete renderer', () => {
@@ -249,8 +257,10 @@ describe('renderer', () => {
249257

250258
document.body.appendChild(panelContainer);
251259

260+
let instance;
261+
252262
expect(() => {
253-
autocomplete<{ label: string }>({
263+
instance = autocomplete<{ label: string }>({
254264
container,
255265
panelContainer,
256266
initialState: {
@@ -280,9 +290,10 @@ describe('renderer', () => {
280290
'[Autocomplete] You provided an incomplete `renderer` (missing: `renderer.render`). This can cause rendering issues.' +
281291
'\nSee https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-renderer'
282292
);
293+
instance.destroy();
283294

284295
expect(() => {
285-
autocomplete<{ label: string }>({
296+
instance = autocomplete<{ label: string }>({
286297
container,
287298
panelContainer,
288299
initialState: {
@@ -315,9 +326,10 @@ describe('renderer', () => {
315326
'[Autocomplete] You provided an incomplete `renderer` (missing: `renderer.createElement`). This can cause rendering issues.' +
316327
'\nSee https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-renderer'
317328
);
329+
instance.destroy();
318330

319331
expect(() => {
320-
autocomplete<{ label: string }>({
332+
instance = autocomplete<{ label: string }>({
321333
container,
322334
panelContainer,
323335
initialState: {
@@ -350,9 +362,10 @@ describe('renderer', () => {
350362
'[Autocomplete] You provided an incomplete `renderer` (missing: `renderer.Fragment`). This can cause rendering issues.' +
351363
'\nSee https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-renderer'
352364
);
365+
instance.destroy();
353366

354367
expect(() => {
355-
autocomplete<{ label: string }>({
368+
instance = autocomplete<{ label: string }>({
356369
container,
357370
panelContainer,
358371
initialState: {
@@ -384,6 +397,7 @@ describe('renderer', () => {
384397
'[Autocomplete] You provided an incomplete `renderer` (missing: `renderer.Fragment`, `renderer.render`). This can cause rendering issues.' +
385398
'\nSee https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-renderer'
386399
);
400+
instance.destroy();
387401
});
388402

389403
test('warns about new `renderer.render` option when specifying an incomplete renderer and a `render` option', () => {
@@ -394,8 +408,9 @@ describe('renderer', () => {
394408

395409
document.body.appendChild(panelContainer);
396410

411+
let instance;
397412
function startAutocomplete() {
398-
autocomplete<{ label: string }>({
413+
instance = autocomplete<{ label: string }>({
399414
container,
400415
panelContainer,
401416
initialState: {
@@ -434,11 +449,13 @@ describe('renderer', () => {
434449
'\n- If you are using the `render` option to work with React 18, pass an empty `render` function into `renderer`.' +
435450
'\nSee https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-render'
436451
);
452+
instance.destroy();
437453

438454
expect(startAutocomplete).not.toWarnDev(
439455
'[Autocomplete] You provided an incomplete `renderer` (missing: `renderer.Fragment`, `renderer.render`). This can cause rendering issues.' +
440456
'\nSee https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-renderer'
441457
);
458+
instance.destroy();
442459
});
443460

444461
test('does not warn at all when only passing a `render` option', () => {
@@ -447,8 +464,9 @@ describe('renderer', () => {
447464

448465
document.body.appendChild(panelContainer);
449466

467+
let instance;
450468
expect(() => {
451-
autocomplete<{ label: string }>({
469+
instance = autocomplete<{ label: string }>({
452470
container,
453471
panelContainer,
454472
initialState: {
@@ -474,6 +492,7 @@ describe('renderer', () => {
474492
},
475493
});
476494
}).not.toWarnDev();
495+
instance.destroy();
477496
});
478497

479498
test('does not warn at all when passing an empty `renderer.render` function', () => {
@@ -484,8 +503,9 @@ describe('renderer', () => {
484503

485504
document.body.appendChild(panelContainer);
486505

506+
let instance;
487507
expect(() => {
488-
autocomplete<{ label: string }>({
508+
instance = autocomplete<{ label: string }>({
489509
container,
490510
panelContainer,
491511
initialState: {
@@ -513,6 +533,7 @@ describe('renderer', () => {
513533
},
514534
});
515535
}).not.toWarnDev();
536+
instance.destroy();
516537
});
517538

518539
test('does not warn at all when not passing a custom renderer', () => {
@@ -521,8 +542,9 @@ describe('renderer', () => {
521542

522543
document.body.appendChild(panelContainer);
523544

545+
let instance;
524546
expect(() => {
525-
autocomplete<{ label: string }>({
547+
instance = autocomplete<{ label: string }>({
526548
container,
527549
panelContainer,
528550
initialState: {
@@ -545,6 +567,7 @@ describe('renderer', () => {
545567
},
546568
});
547569
}).not.toWarnDev();
570+
instance.destroy();
548571
});
549572

550573
test('does not warn at all when passing a full custom renderer', () => {
@@ -556,8 +579,9 @@ describe('renderer', () => {
556579

557580
document.body.appendChild(panelContainer);
558581

582+
let instance;
559583
expect(() => {
560-
autocomplete<{ label: string }>({
584+
instance = autocomplete<{ label: string }>({
561585
container,
562586
panelContainer,
563587
initialState: {
@@ -585,5 +609,6 @@ describe('renderer', () => {
585609
},
586610
});
587611
}).not.toWarnDev();
612+
instance.destroy();
588613
});
589614
});

packages/autocomplete-js/src/autocomplete.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
createRef,
88
debounce,
99
getItemsCount,
10+
warn,
1011
} from '@algolia/autocomplete-shared';
1112
import htm from 'htm';
1213

@@ -27,6 +28,8 @@ import {
2728
import { userAgents } from './userAgents';
2829
import { mergeDeep, pickBy, setProperties } from './utils';
2930

31+
let instancesCount = 0;
32+
3033
export function autocomplete<TItem extends BaseItem>(
3134
options: AutocompleteOptions<TItem>
3235
): AutocompleteApi<TItem> {
@@ -336,6 +339,7 @@ export function autocomplete<TItem extends BaseItem>(
336339
});
337340

338341
function destroy() {
342+
instancesCount--;
339343
cleanupEffects();
340344
}
341345

@@ -397,6 +401,15 @@ export function autocomplete<TItem extends BaseItem>(
397401
});
398402
}
399403

404+
warn(
405+
instancesCount === 0,
406+
`Autocomplete doesn't support multiple instances running at the same time. Make sure to destroy the previous instance before creating a new one.
407+
408+
See: https://www.algolia.com/doc/ui-libraries/autocomplete/api-reference/autocomplete-js/autocomplete/#param-destroy`
409+
);
410+
411+
instancesCount++;
412+
400413
return {
401414
...autocompleteScopeApi,
402415
update,

0 commit comments

Comments
 (0)