slim-select 2.13.1 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +185 -44
  2. package/dist/index.d.ts +304 -52
  3. package/dist/react/index.d.ts +304 -0
  4. package/dist/react/index.js +1549 -0
  5. package/dist/react/index.umd.js +22 -0
  6. package/dist/slimselect.cjs.js +2 -2030
  7. package/dist/slimselect.cjs.js.map +1 -0
  8. package/dist/slimselect.css +1 -1
  9. package/dist/slimselect.css.map +1 -1
  10. package/dist/slimselect.es.js +1225 -2012
  11. package/dist/slimselect.es.js.map +1 -0
  12. package/dist/slimselect.iife.js +2 -0
  13. package/dist/slimselect.iife.js.map +1 -0
  14. package/dist/slimselect.js +2 -2036
  15. package/dist/slimselect.js.map +1 -0
  16. package/dist/vue/index.d.ts +304 -0
  17. package/dist/vue/index.js +1341 -0
  18. package/dist/vue/index.umd.js +1 -0
  19. package/index.html +1 -0
  20. package/package.json +63 -40
  21. package/release.js +0 -7
  22. package/src/slim-select/accessibility.test.ts +782 -0
  23. package/src/slim-select/classes.test.ts +7 -6
  24. package/src/slim-select/classes.ts +8 -6
  25. package/src/slim-select/helpers.test.ts +10 -8
  26. package/src/slim-select/index.test.ts +509 -14
  27. package/src/slim-select/index.ts +58 -31
  28. package/src/slim-select/react/index.tsx +9 -0
  29. package/src/slim-select/react/react.tsx +112 -0
  30. package/src/slim-select/render.test.ts +479 -119
  31. package/src/slim-select/render.ts +213 -91
  32. package/src/slim-select/select.test.ts +28 -10
  33. package/src/slim-select/select.ts +35 -12
  34. package/src/slim-select/settings.test.ts +19 -8
  35. package/src/slim-select/settings.ts +2 -4
  36. package/src/slim-select/slimselect.scss +30 -15
  37. package/src/slim-select/store.test.ts +13 -16
  38. package/src/slim-select/store.ts +97 -86
  39. package/src/slim-select/vue/index.ts +9 -0
  40. package/src/slim-select/vue/vue.test.ts +832 -0
  41. package/src/slim-select/{vue.vue → vue/vue.vue} +45 -23
  42. package/tsconfig.json +6 -4
  43. package/{vite.config.mts → vite.config.docs.mts} +10 -0
  44. package/vite.config.lib.mts +48 -0
  45. package/vite.config.react.mts +40 -0
  46. package/vite.config.vue.mts +39 -0
  47. package/vitest.config.ts +29 -0
  48. package/dist/classes.d.ts +0 -43
  49. package/dist/helpers.d.ts +0 -5
  50. package/dist/render.d.ts +0 -81
  51. package/dist/select.d.ts +0 -30
  52. package/dist/settings.d.ts +0 -35
  53. package/dist/slimselect.global.js +0 -2033
  54. package/dist/slimselect.min.js +0 -1
  55. package/dist/slimselect.umd.js +0 -2036
  56. package/dist/slimselect.umd.min.js +0 -1
  57. package/dist/store.d.ts +0 -84
  58. package/jest.config.js +0 -14
  59. package/src/slim-select/rollup.config.mjs +0 -127
  60. package/src/slim-select/tsconfig.json +0 -14
@@ -4,10 +4,8 @@
4
4
 
5
5
  'use strict'
6
6
 
7
- import { describe, expect, test } from '@jest/globals'
8
- import SlimSelect from './'
9
- import { OptionOptional } from './store'
10
- import { Config } from './index'
7
+ import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest'
8
+ import SlimSelect from '@/slim-select'
11
9
 
12
10
  describe('SlimSelect Module', () => {
13
11
  let slim: SlimSelect
@@ -20,9 +18,15 @@ describe('SlimSelect Module', () => {
20
18
  })
21
19
  })
22
20
 
21
+ afterEach(() => {
22
+ if (slim) {
23
+ slim.destroy()
24
+ }
25
+ })
26
+
23
27
  describe('constructor', () => {
24
28
  test('missing select element throws error', () => {
25
- const errorMock = jest.fn()
29
+ const errorMock = vi.fn()
26
30
  const slim = new SlimSelect({ select: '#invalid', events: { error: errorMock } })
27
31
 
28
32
  expect(slim.select).toBeUndefined()
@@ -35,7 +39,7 @@ describe('SlimSelect Module', () => {
35
39
  test('invalid element throws error', () => {
36
40
  document.body.innerHTML = '<div id="invalid"></div>'
37
41
 
38
- const errorMock = jest.fn()
42
+ const errorMock = vi.fn()
39
43
  const slim = new SlimSelect({ select: '#invalid', events: { error: errorMock } })
40
44
 
41
45
  expect(slim.select).toBeUndefined()
@@ -82,8 +86,8 @@ describe('SlimSelect Module', () => {
82
86
  test('enable', () => {
83
87
  slim.settings.disabled = true
84
88
 
85
- const selectEnableMock = jest.fn()
86
- const renderEnableMock = jest.fn()
89
+ const selectEnableMock = vi.fn()
90
+ const renderEnableMock = vi.fn()
87
91
 
88
92
  slim.select.enable = selectEnableMock
89
93
  slim.render.enable = renderEnableMock
@@ -98,8 +102,8 @@ describe('SlimSelect Module', () => {
98
102
  test('disable', () => {
99
103
  slim.settings.disabled = false
100
104
 
101
- const selectDisableMock = jest.fn()
102
- const renderDisableMock = jest.fn()
105
+ const selectDisableMock = vi.fn()
106
+ const renderDisableMock = vi.fn()
103
107
 
104
108
  slim.select.disable = selectDisableMock
105
109
  slim.render.disable = renderDisableMock
@@ -119,7 +123,7 @@ describe('SlimSelect Module', () => {
119
123
  <option id="3" value="3">Two</option>
120
124
  </select>`
121
125
 
122
- const options: OptionOptional[] = [
126
+ const options = [
123
127
  {
124
128
  id: '1',
125
129
  value: '1',
@@ -136,7 +140,7 @@ describe('SlimSelect Module', () => {
136
140
  text: 'Three'
137
141
  }
138
142
  ]
139
- const config: Config = {
143
+ const config = {
140
144
  select: '#test',
141
145
  settings: {
142
146
  allowDeselect: true
@@ -157,7 +161,7 @@ describe('SlimSelect Module', () => {
157
161
  <option id="3" value="3">Two</option>
158
162
  </select>`
159
163
 
160
- const options: OptionOptional[] = [
164
+ const options = [
161
165
  {
162
166
  id: '1',
163
167
  value: '1',
@@ -176,7 +180,7 @@ describe('SlimSelect Module', () => {
176
180
  text: 'Three'
177
181
  }
178
182
  ]
179
- const config: Config = {
183
+ const config = {
180
184
  select: '#test',
181
185
  settings: {
182
186
  allowDeselect: true
@@ -189,4 +193,495 @@ describe('SlimSelect Module', () => {
189
193
  expect(slimSelect.getSelected()).toHaveLength(2)
190
194
  expect(slimSelect.render.main.deselect.main.classList).not.toContain(slimSelect.render.classes.hide)
191
195
  })
196
+
197
+ describe('required attribute support', () => {
198
+ test('select with required attribute remains focusable for validation', () => {
199
+ document.body.innerHTML = '<select id="test" required></select>'
200
+
201
+ const slim = new SlimSelect({
202
+ select: '#test'
203
+ })
204
+
205
+ const selectEl = document.getElementById('test') as HTMLSelectElement
206
+
207
+ // Select should be hidden but still focusable (1px for validation popup)
208
+ expect(selectEl.style.position).toBe('absolute')
209
+ expect(selectEl.style.width).toBe('1px')
210
+ expect(selectEl.style.height).toBe('1px')
211
+ expect(selectEl.style.opacity).toBe('0')
212
+ expect(selectEl.style.margin).toBe('0px')
213
+ expect(selectEl.style.padding).toBe('0px')
214
+ expect(selectEl.style.clip).toContain('rect')
215
+
216
+ // Required attribute should still be present
217
+ expect(selectEl.hasAttribute('required')).toBe(true)
218
+
219
+ // Select should be able to receive focus programmatically
220
+ selectEl.focus()
221
+ expect(document.activeElement).toBe(selectEl)
222
+ })
223
+
224
+ test('form validation works with required select', () => {
225
+ // Create a form with required select
226
+ document.body.innerHTML = `
227
+ <form id="test-form">
228
+ <select id="test" name="test-select" required>
229
+ <option value="">Select an option</option>
230
+ <option value="1">Option 1</option>
231
+ <option value="2">Option 2</option>
232
+ </select>
233
+ <button type="submit">Submit</button>
234
+ </form>
235
+ `
236
+
237
+ const slim = new SlimSelect({
238
+ select: '#test'
239
+ })
240
+
241
+ const form = document.getElementById('test-form') as HTMLFormElement
242
+ const selectEl = document.getElementById('test') as HTMLSelectElement
243
+
244
+ // Form should be invalid when nothing is selected
245
+ expect(selectEl.required).toBe(true)
246
+ expect(selectEl.value).toBe('')
247
+ expect(form.checkValidity()).toBe(false)
248
+
249
+ // Select an option
250
+ slim.setSelected(['1'])
251
+
252
+ // Form should now be valid
253
+ expect(selectEl.value).toBe('1')
254
+ expect(form.checkValidity()).toBe(true)
255
+ })
256
+ })
257
+
258
+ describe('keepOrder setting', () => {
259
+ test('keepOrder: false returns values in DOM order (default)', () => {
260
+ document.body.innerHTML = `
261
+ <select id="test" multiple>
262
+ <option value="apple">Apple</option>
263
+ <option value="banana">Banana</option>
264
+ <option value="cherry">Cherry</option>
265
+ </select>
266
+ `
267
+
268
+ const slim = new SlimSelect({
269
+ select: '#test',
270
+ settings: {
271
+ keepOrder: false // DOM order (default)
272
+ }
273
+ })
274
+
275
+ // Select in reverse order: Cherry -> Apple -> Banana
276
+ slim.setSelected(['cherry', 'apple', 'banana'])
277
+
278
+ // Should return in DOM order (how they appear in HTML)
279
+ expect(slim.getSelected()).toEqual(['apple', 'banana', 'cherry'])
280
+ })
281
+
282
+ test('keepOrder: true returns values in selection order', () => {
283
+ document.body.innerHTML = `
284
+ <select id="test" multiple>
285
+ <option value="apple">Apple</option>
286
+ <option value="banana">Banana</option>
287
+ <option value="cherry">Cherry</option>
288
+ </select>
289
+ `
290
+
291
+ const slim = new SlimSelect({
292
+ select: '#test',
293
+ settings: {
294
+ keepOrder: true // Selection order
295
+ }
296
+ })
297
+
298
+ // Select in specific order: Cherry -> Apple -> Banana
299
+ slim.setSelected(['cherry', 'apple', 'banana'])
300
+
301
+ // Should return in the order they were selected (click order)
302
+ expect(slim.getSelected()).toEqual(['cherry', 'apple', 'banana'])
303
+ })
304
+
305
+ test('keepOrder: true preserves click order in getSelected', () => {
306
+ document.body.innerHTML = `
307
+ <select id="test" multiple>
308
+ <option value="1">Option 1</option>
309
+ <option value="2">Option 2</option>
310
+ <option value="3">Option 3</option>
311
+ <option value="4">Option 4</option>
312
+ <option value="5">Option 5</option>
313
+ </select>
314
+ `
315
+
316
+ const slim = new SlimSelect({
317
+ select: '#test',
318
+ settings: {
319
+ keepOrder: true
320
+ }
321
+ })
322
+
323
+ // Simulate user clicking options in random order
324
+ slim.setSelected(['5', '2', '4', '1'])
325
+
326
+ // Should maintain that exact order
327
+ expect(slim.getSelected()).toEqual(['5', '2', '4', '1'])
328
+ })
329
+ })
330
+
331
+ describe('maxValuesShown with deselection', () => {
332
+ test('can still deselect options via dropdown when maxValuesShown is exceeded', () => {
333
+ document.body.innerHTML = `
334
+ <select id="test" multiple>
335
+ <option value="1">Option 1</option>
336
+ <option value="2">Option 2</option>
337
+ <option value="3">Option 3</option>
338
+ <option value="4">Option 4</option>
339
+ <option value="5">Option 5</option>
340
+ </select>
341
+ `
342
+
343
+ const slim = new SlimSelect({
344
+ select: '#test',
345
+ settings: {
346
+ maxValuesShown: 2 // Show "X selected" when more than 2
347
+ }
348
+ })
349
+
350
+ // Select 3 options (exceeds maxValuesShown)
351
+ slim.setSelected(['1', '2', '3'])
352
+ expect(slim.getSelected()).toEqual(['1', '2', '3'])
353
+
354
+ // Verify counter is shown (not individual values)
355
+ const counter = document.querySelector('.ss-max')
356
+ expect(counter?.textContent).toBe('3 selected')
357
+
358
+ // Open dropdown
359
+ slim.open()
360
+
361
+ // Find and click on a selected option to deselect it
362
+ const options = document.querySelectorAll('[role="option"]')
363
+ const selectedOption = Array.from(options).find(
364
+ (opt) => opt.getAttribute('aria-selected') === 'true' && opt.textContent?.includes('Option 1')
365
+ ) as HTMLElement
366
+
367
+ expect(selectedOption).toBeTruthy()
368
+
369
+ // Click the selected option - should deselect it
370
+ selectedOption.dispatchEvent(new MouseEvent('click', { bubbles: true }))
371
+
372
+ // Should now only have 2 selected
373
+ expect(slim.getSelected()).toEqual(['2', '3'])
374
+ })
375
+
376
+ test('deselecting via dropdown updates maxValuesShown display', () => {
377
+ document.body.innerHTML = `
378
+ <select id="test" multiple>
379
+ <option value="1">Value 1</option>
380
+ <option value="2">Value 2</option>
381
+ <option value="3">Value 3</option>
382
+ </select>
383
+ `
384
+
385
+ const slim = new SlimSelect({
386
+ select: '#test',
387
+ settings: {
388
+ maxValuesShown: 2
389
+ }
390
+ })
391
+
392
+ // Select 3 options (shows "3 selected")
393
+ slim.setSelected(['1', '2', '3'])
394
+
395
+ // Open and deselect one
396
+ slim.open()
397
+ const options = document.querySelectorAll('[role="option"]')
398
+ const firstSelected = Array.from(options).find(
399
+ (opt) => opt.getAttribute('aria-selected') === 'true'
400
+ ) as HTMLElement
401
+ firstSelected.dispatchEvent(new MouseEvent('click', { bubbles: true }))
402
+
403
+ // Now only 2 selected - should show individual values again (not counter)
404
+ const individualValues = document.querySelectorAll('.ss-value')
405
+ expect(individualValues.length).toBeGreaterThan(0)
406
+
407
+ const counter = document.querySelector('.ss-max')
408
+ expect(counter).toBeNull()
409
+ })
410
+ })
411
+
412
+ describe('Search State Regression Tests', () => {
413
+ let slim: SlimSelect
414
+ let searchMock: ReturnType<typeof vi.fn>
415
+
416
+ beforeEach(() => {
417
+ document.body.innerHTML = '<select id="searchTest"></select>'
418
+
419
+ searchMock = vi.fn().mockImplementation((searchValue: string) => {
420
+ // Mock search results based on search value
421
+ if (searchValue.length >= 2) {
422
+ return [
423
+ { value: 'null', text: 'Null' },
424
+ { value: 'eins', text: 'Eins' },
425
+ { value: 'zwei', text: 'Zwei' },
426
+ { value: 'drei', text: 'Drei' },
427
+ { value: 'vier', text: 'Vier' },
428
+ { value: 'funf', text: 'Fünf' },
429
+ { value: 'sechs', text: 'Sechs' },
430
+ { value: 'sieben', text: 'Sieben' },
431
+ { value: 'acht', text: 'Acht' },
432
+ { value: 'neun', text: 'Neun' },
433
+ { value: 'zehn', text: 'Zehn' }
434
+ ]
435
+ }
436
+ return []
437
+ })
438
+
439
+ slim = new SlimSelect({
440
+ select: '#searchTest',
441
+ data: [
442
+ { value: 'a', text: 'A' },
443
+ { value: 'b', text: 'B' }
444
+ ],
445
+ events: {
446
+ search: searchMock
447
+ }
448
+ })
449
+ })
450
+
451
+ afterEach(() => {
452
+ // Clean up to avoid timeout errors
453
+ if (slim) {
454
+ slim.destroy()
455
+ }
456
+ })
457
+
458
+ test('should maintain search results when reopening dropdown', () => {
459
+ // 1. Open dropdown - should show static options [A, B]
460
+ slim.open()
461
+ let options = document.querySelectorAll('.ss-option')
462
+ expect(options).toHaveLength(2)
463
+ expect(options[0].textContent).toBe('A')
464
+ expect(options[1].textContent).toBe('B')
465
+ slim.close()
466
+
467
+ // 2. Search - should update store data with search results
468
+ slim.search('te')
469
+ options = document.querySelectorAll('.ss-option')
470
+ // Should have search results (11) - static options are replaced
471
+ expect(options.length).toBeGreaterThanOrEqual(11)
472
+
473
+ // 3. Close and reopen - search results persist in store
474
+ slim.close()
475
+ slim.open()
476
+ options = document.querySelectorAll('.ss-option')
477
+ // Store data should still contain search results
478
+ expect(options.length).toBeGreaterThanOrEqual(11)
479
+ })
480
+
481
+ test('should clear search input when closing dropdown', async () => {
482
+ // Open dropdown first
483
+ slim.open()
484
+
485
+ // Search for something
486
+ slim.search('te')
487
+ expect(slim.render.content.search.input.value).toBe('te')
488
+
489
+ // Close dropdown - this clears the search input
490
+ slim.close()
491
+
492
+ // Wait for close to complete (timeoutDelay is 200ms by default)
493
+ await new Promise((resolve) => setTimeout(resolve, 300))
494
+
495
+ // Search input should be cleared
496
+ expect(slim.render.content.search.input.value).toBe('')
497
+ })
498
+
499
+ test('should persist search results in store across close/open cycles', () => {
500
+ // Search for something
501
+ slim.search('te')
502
+
503
+ // Search results are now in the store
504
+ let options = document.querySelectorAll('.ss-option')
505
+ expect(options.length).toBeGreaterThanOrEqual(11)
506
+
507
+ // Close and reopen - store data persists
508
+ slim.close()
509
+ slim.open()
510
+ options = document.querySelectorAll('.ss-option')
511
+ expect(options.length).toBeGreaterThanOrEqual(11) // Store data maintained
512
+
513
+ slim.close()
514
+ slim.open()
515
+ options = document.querySelectorAll('.ss-option')
516
+ expect(options.length).toBeGreaterThanOrEqual(11) // Still maintained after multiple cycles
517
+ })
518
+
519
+ test('should handle selection from search results correctly', () => {
520
+ // Search for options
521
+ slim.search('te')
522
+
523
+ // Select first search result (should be from search results)
524
+ const options = document.querySelectorAll('.ss-option')
525
+ options[0].dispatchEvent(new MouseEvent('click'))
526
+
527
+ // Check that an option is selected
528
+ const selected = slim.getSelected()
529
+ expect(selected.length).toBe(1)
530
+
531
+ // Close and reopen
532
+ slim.close()
533
+ slim.open()
534
+
535
+ // Should still have search results in store
536
+ const reopenedOptions = document.querySelectorAll('.ss-option')
537
+ expect(reopenedOptions.length).toBeGreaterThanOrEqual(11)
538
+
539
+ // Selected value should be preserved
540
+ expect(slim.getSelected()).toEqual(selected)
541
+ })
542
+
543
+ test('should handle multiple selections from search results', () => {
544
+ // Configure for multi-select
545
+ slim = new SlimSelect({
546
+ select: '#searchTest',
547
+ data: [
548
+ { value: 'a', text: 'A' },
549
+ { value: 'b', text: 'B' }
550
+ ],
551
+ settings: {
552
+ isMultiple: true
553
+ },
554
+ events: {
555
+ search: searchMock
556
+ }
557
+ })
558
+
559
+ // Search for options
560
+ slim.search('te')
561
+
562
+ // Select first two search results (Null, Eins) using Cmd+Click
563
+ const options = document.querySelectorAll('.ss-option')
564
+ options[0].dispatchEvent(new MouseEvent('click', { metaKey: true })) // Cmd+Click to keep dropdown open
565
+ options[1].dispatchEvent(new MouseEvent('click', { metaKey: true })) // Cmd+Click to keep dropdown open
566
+
567
+ // Note: Search results are temporary and not added to the store
568
+ // So the selection won't persist in the store, but the search state should be maintained
569
+
570
+ // Close and reopen
571
+ slim.close()
572
+ slim.open()
573
+
574
+ // Should still show search results (search state maintained)
575
+ const reopenedOptions = document.querySelectorAll('.ss-option')
576
+ // Multi-select may have additional options like deselect all, so we check for at least 11
577
+ expect(reopenedOptions.length).toBeGreaterThanOrEqual(11)
578
+
579
+ // Search results are maintained but selections from search results don't persist in store
580
+ // This is expected behavior - search results are temporary
581
+ })
582
+
583
+ test('should handle new search after reopening', () => {
584
+ // Initial search
585
+ slim.search('te')
586
+ let options = document.querySelectorAll('.ss-option')
587
+ expect(options.length).toBeGreaterThanOrEqual(11)
588
+
589
+ // Close and reopen
590
+ slim.close()
591
+ slim.open()
592
+
593
+ // Should still have previous search results in store
594
+ options = document.querySelectorAll('.ss-option')
595
+ expect(options.length).toBeGreaterThanOrEqual(11)
596
+
597
+ // Perform new search - this updates the store with new results
598
+ slim.search('ei')
599
+ options = document.querySelectorAll('.ss-option')
600
+
601
+ // Should show new search results
602
+ expect(options.length).toBeGreaterThanOrEqual(11)
603
+ })
604
+
605
+ test('should handle search with no results correctly', () => {
606
+ // Search for something that returns no results
607
+ slim.search('xyz')
608
+
609
+ // Should show no results message (searchText)
610
+ const noResults = document.querySelector('.ss-search')
611
+ expect(noResults).toBeTruthy()
612
+
613
+ // Close and reopen
614
+ slim.close()
615
+ slim.open()
616
+
617
+ // Should still show no results (search state maintained)
618
+ const stillNoResults = document.querySelector('.ss-search')
619
+ expect(stillNoResults).toBeTruthy()
620
+ })
621
+
622
+ test('should handle async search results correctly', async () => {
623
+ // Mock async search
624
+ const asyncSearchMock = vi.fn().mockResolvedValue([
625
+ { value: 'async1', text: 'Async Result 1' },
626
+ { value: 'async2', text: 'Async Result 2' }
627
+ ])
628
+
629
+ slim = new SlimSelect({
630
+ select: '#searchTest',
631
+ data: [
632
+ { value: 'a', text: 'A' },
633
+ { value: 'b', text: 'B' }
634
+ ],
635
+ events: {
636
+ search: asyncSearchMock
637
+ }
638
+ })
639
+
640
+ // Trigger async search
641
+ slim.search('async')
642
+
643
+ // Wait for async results
644
+ await new Promise((resolve) => setTimeout(resolve, 100))
645
+
646
+ // Check results are displayed (should include the async results)
647
+ let options = document.querySelectorAll('.ss-option')
648
+ expect(options.length).toBeGreaterThanOrEqual(2)
649
+
650
+ // Find the async results in the options
651
+ const asyncOptions = Array.from(options).filter((option) => option.textContent?.includes('Async Result'))
652
+ expect(asyncOptions).toHaveLength(2)
653
+ expect(asyncOptions[0].textContent).toBe('Async Result 1')
654
+ expect(asyncOptions[1].textContent).toBe('Async Result 2')
655
+
656
+ // Close and reopen
657
+ slim.close()
658
+ slim.open()
659
+
660
+ // Should still show async search results
661
+ options = document.querySelectorAll('.ss-option')
662
+ const reopenedAsyncOptions = Array.from(options).filter((option) => option.textContent?.includes('Async Result'))
663
+ expect(reopenedAsyncOptions).toHaveLength(2)
664
+ expect(reopenedAsyncOptions[0].textContent).toBe('Async Result 1')
665
+ })
666
+
667
+ test('should update store data with search results', () => {
668
+ // Test that search updates the store
669
+ slim.search('te')
670
+ expect(searchMock).toHaveBeenCalledWith('te', expect.any(Array))
671
+
672
+ let options = document.querySelectorAll('.ss-option')
673
+ expect(options.length).toBeGreaterThanOrEqual(11)
674
+
675
+ // Search results are now in the store
676
+ const storeData = slim.getData()
677
+ expect(storeData.length).toBeGreaterThanOrEqual(11)
678
+
679
+ // Clear search - this returns empty array from mock
680
+ slim.search('')
681
+ options = document.querySelectorAll('.ss-option')
682
+
683
+ // Empty search clears results
684
+ expect(options.length).toBeLessThanOrEqual(2)
685
+ })
686
+ })
192
687
  })