slim-select 3.4.3 → 3.6.0

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 (73) hide show
  1. package/dist/classes.d.ts +47 -0
  2. package/dist/helpers.d.ts +5 -0
  3. package/dist/index.d.ts +53 -319
  4. package/dist/index.umd.d.ts +2 -0
  5. package/dist/react/index.d.ts +4 -319
  6. package/dist/react/index.js +1471 -1697
  7. package/dist/react/index.umd.js +2 -2
  8. package/dist/react/react.d.ts +17 -0
  9. package/dist/react/react.test.d.ts +1 -0
  10. package/dist/react/slim-select/react/index.d.ts +4 -0
  11. package/dist/render.d.ts +90 -0
  12. package/dist/select.d.ts +38 -0
  13. package/dist/settings.d.ts +40 -0
  14. package/dist/slimselect.cjs.js +2 -2
  15. package/dist/slimselect.cjs.js.map +1 -1
  16. package/dist/slimselect.css +1 -1
  17. package/dist/slimselect.css.map +1 -1
  18. package/dist/slimselect.es.js +1245 -1385
  19. package/dist/slimselect.es.js.map +1 -1
  20. package/dist/slimselect.iife.js +2 -2
  21. package/dist/slimselect.iife.js.map +1 -1
  22. package/dist/slimselect.js +2 -2
  23. package/dist/slimselect.js.map +1 -1
  24. package/dist/src/docs/main.d.ts +0 -0
  25. package/dist/src/docs/router.d.ts +2 -0
  26. package/dist/src/docs/store.d.ts +12 -0
  27. package/dist/store.d.ts +53 -0
  28. package/dist/vue/index.d.ts +4 -319
  29. package/dist/vue/index.js +1347 -1504
  30. package/dist/vue/index.umd.js +1 -1
  31. package/dist/vue/slim-select/vue/index.d.ts +4 -0
  32. package/package.json +30 -36
  33. package/src/slim-select/slimselect.scss +47 -5
  34. package/.nvmrc +0 -1
  35. package/.prettierignore +0 -1
  36. package/.prettierrc +0 -7
  37. package/CODE_OF_CONDUCT.md +0 -46
  38. package/CONTRIBUTING.md +0 -1
  39. package/cdn.html +0 -21
  40. package/docs.go +0 -12
  41. package/example.html +0 -25
  42. package/index.html +0 -76
  43. package/release.js +0 -191
  44. package/sitemap.js +0 -108
  45. package/src/slim-select/accessibility.test.ts +0 -782
  46. package/src/slim-select/classes.test.ts +0 -73
  47. package/src/slim-select/classes.ts +0 -117
  48. package/src/slim-select/helpers.test.ts +0 -162
  49. package/src/slim-select/helpers.ts +0 -61
  50. package/src/slim-select/index.test.ts +0 -1356
  51. package/src/slim-select/index.ts +0 -594
  52. package/src/slim-select/index.umd.ts +0 -8
  53. package/src/slim-select/react/index.tsx +0 -9
  54. package/src/slim-select/react/react.test.tsx +0 -498
  55. package/src/slim-select/react/react.tsx +0 -209
  56. package/src/slim-select/render.test.ts +0 -1927
  57. package/src/slim-select/render.ts +0 -1771
  58. package/src/slim-select/select.test.ts +0 -683
  59. package/src/slim-select/select.ts +0 -659
  60. package/src/slim-select/settings.test.ts +0 -285
  61. package/src/slim-select/settings.ts +0 -78
  62. package/src/slim-select/store.test.ts +0 -1007
  63. package/src/slim-select/store.ts +0 -433
  64. package/src/slim-select/vue/index.ts +0 -9
  65. package/src/slim-select/vue/vue.test.ts +0 -1091
  66. package/src/slim-select/vue/vue.vue +0 -224
  67. package/tsconfig.json +0 -26
  68. package/vite.config.docs.mts +0 -29
  69. package/vite.config.lib.es-cjs.ts +0 -40
  70. package/vite.config.lib.umd-iife.ts +0 -34
  71. package/vite.config.react.mts +0 -41
  72. package/vite.config.vue.mts +0 -40
  73. package/vitest.config.ts +0 -30
@@ -1,683 +0,0 @@
1
- /**
2
- * @jest-environment jsdom
3
- */
4
-
5
- 'use strict'
6
-
7
- import { describe, expect, vi, test, beforeEach } from 'vitest'
8
- import Select from './select'
9
- import Store, { Optgroup, Option } from './store'
10
-
11
- describe('select module', () => {
12
- let select: Select
13
-
14
- beforeEach(() => {
15
- document.body.innerHTML = `<select id="test" multiple>
16
- <optgroup label="test1">
17
- <option id="111" value="1">One</option>
18
- <option id="222" value="2">Two</option>
19
- </optgroup>
20
- <optgroup label="test2">
21
- <option id="333" value="3">Three</option>
22
- <option id="444" value="4">Four</option>
23
- <option id="555" value="5">Five</option>
24
- </optgroup>
25
- <option id="666" value="6">Six</option>
26
- </select>`
27
-
28
- const selectElement = document.getElementById('test') as HTMLSelectElement
29
- select = new Select(selectElement)
30
- })
31
-
32
- describe('constructor', () => {
33
- test('constructor works', () => {
34
- document.body.innerHTML = '<select id="test"></select>'
35
-
36
- const selectElement = document.getElementById('test') as HTMLSelectElement
37
- const select = new Select(selectElement)
38
-
39
- expect(select).toBeInstanceOf(Select)
40
- expect(select.select).toBeInstanceOf(HTMLSelectElement)
41
- })
42
- })
43
-
44
- describe('enable', () => {
45
- test('enable enables select element', () => {
46
- select.select.disabled = true
47
- select.enable()
48
-
49
- expect(select.select.disabled).toBe(false)
50
- })
51
- })
52
-
53
- describe('disable', () => {
54
- test('disable disables select element', () => {
55
- select.disable()
56
-
57
- expect(select.select.disabled).toBe(true)
58
- })
59
- })
60
-
61
- describe('hideUI', () => {
62
- test('correct HTML attributes get set', () => {
63
- select.hideUI()
64
-
65
- expect(select.select.tabIndex).toBe(-1)
66
- // Visually hidden but still focusable for form validation
67
- expect(select.select.style.position).toBe('absolute')
68
- expect(select.select.style.width).toBe('1px')
69
- expect(select.select.style.height).toBe('1px')
70
- expect(select.select.style.opacity).toBe('0')
71
- expect(select.select.style.pointerEvents).toBe('none')
72
- expect(select.select.style.margin).toBe('0px')
73
- expect(select.select.style.padding).toBe('0px')
74
- // clip property is deprecated and may not be accessible in all test environments
75
- // The code sets it, but some browsers/test environments may ignore or clear it
76
- // Check if clip is set OR if the other hiding properties are sufficient
77
- const clipValue = select.select.style.clip
78
- if (clipValue) {
79
- expect(clipValue).toContain('rect')
80
- }
81
- // If clip is empty, the other hiding properties (position, width, height, opacity) are sufficient
82
- expect(select.select.getAttribute('aria-hidden')).toBe('true')
83
- })
84
- })
85
-
86
- describe('showUI', () => {
87
- test('HTML attributes get reset', () => {
88
- select.select.tabIndex = -1
89
- select.select.style.position = 'absolute'
90
- select.select.style.width = '0'
91
- select.select.style.opacity = '0'
92
- select.select.style.margin = '0'
93
- select.select.style.padding = '0'
94
- select.select.style.borderWidth = '0'
95
- select.select.setAttribute('aria-hidden', 'true')
96
-
97
- select.showUI()
98
-
99
- expect(select.select.tabIndex).toBeFalsy()
100
- expect(select.select.style.position).toBeFalsy()
101
- expect(select.select.style.width).toBeFalsy()
102
- expect(select.select.style.opacity).toBeFalsy()
103
- expect(select.select.style.margin).toBeFalsy()
104
- expect(select.select.style.padding).toBeFalsy()
105
- // borderWidth gets reset
106
- expect(select.select.getAttribute('aria-hidden')).toBeNull()
107
- })
108
- })
109
-
110
- describe('getData', () => {
111
- test('get data from select options', () => {
112
- document.body.innerHTML = `<select id="test">
113
- <option id="value1" value="1">One</option>
114
- <option value="2">Two</option>
115
- </select>`
116
-
117
- const selectElement = document.getElementById('test') as HTMLSelectElement
118
- const select = new Select(selectElement)
119
- const data = select.getData() as Option[]
120
-
121
- expect(data).toHaveLength(2)
122
- expect(data[0].id).toBe('value1')
123
- expect(data[0].value).toBe('1')
124
- expect(data[0].text).toBe('One')
125
- expect(data[1].value).toBe('2')
126
- expect(data[1].text).toBe('Two')
127
- })
128
-
129
- test('get data from select optgroups', () => {
130
- document.body.innerHTML = `<select id="test">
131
- <optgroup label="test1">
132
- <option id="111" value="1">One</option>
133
- <option id="222" value="2">Two</option>
134
- </optgroup>
135
- <optgroup label="test2">
136
- <option id="333" value="3">Three</option>
137
- <option id="444" value="4">Four</option>
138
- <option id="555" value="5">Five</option>
139
- </optgroup>
140
- </select>`
141
-
142
- const selectElement = document.getElementById('test') as HTMLSelectElement
143
- const select = new Select(selectElement)
144
- const data = select.getData() as Optgroup[]
145
-
146
- expect(data).toHaveLength(2)
147
-
148
- expect(data[0].label).toBe('test1')
149
- expect(data[0].options).toHaveLength(2)
150
- expect(data[0].options[0].id).toBe('111')
151
- expect(data[0].options[0].value).toBe('1')
152
- expect(data[0].options[0].text).toBe('One')
153
- expect(data[0].options[1].id).toBe('222')
154
- expect(data[0].options[1].value).toBe('2')
155
- expect(data[0].options[1].text).toBe('Two')
156
-
157
- expect(data[1].label).toBe('test2')
158
- expect(data[1].options).toHaveLength(3)
159
- expect(data[1].options[0].id).toBe('333')
160
- expect(data[1].options[0].value).toBe('3')
161
- expect(data[1].options[0].text).toBe('Three')
162
- expect(data[1].options[1].id).toBe('444')
163
- expect(data[1].options[1].value).toBe('4')
164
- expect(data[1].options[1].text).toBe('Four')
165
- expect(data[1].options[2].id).toBe('555')
166
- expect(data[1].options[2].value).toBe('5')
167
- expect(data[1].options[2].text).toBe('Five')
168
- })
169
- })
170
-
171
- describe('setSelected', () => {
172
- test('single option gets selected correctly', () => {
173
- // get id of the first option in the first optgroup
174
- const id = select.select.querySelector<HTMLOptionElement>('optgroup option')?.id
175
- expect(id).toBe('111')
176
-
177
- select.setSelected([id as string])
178
- expect(select.select.querySelector<HTMLOptionElement>('option[value="1"]')?.selected).toBe(true)
179
- })
180
-
181
- test('mix of options get selected correctly', () => {
182
- select.setSelected(['111', '222', '333'])
183
-
184
- expect(select.select.querySelector<HTMLOptionElement>('option[value="1"]')?.selected).toBe(true)
185
- expect(select.select.querySelector<HTMLOptionElement>('option[value="2"]')?.selected).toBe(true)
186
- expect(select.select.querySelector<HTMLOptionElement>('option[value="3"]')?.selected).toBe(true)
187
- })
188
- })
189
-
190
- describe('setSelectedByValue', () => {
191
- test('single value get selected correctly', () => {
192
- select.setSelectedByValue(['6'])
193
-
194
- expect(select.select.querySelector<HTMLOptionElement>('option[value="6"]')?.selected).toBe(true)
195
- })
196
-
197
- test('opt group value gets selected correctly', () => {
198
- select.setSelectedByValue(['4'])
199
-
200
- expect(select.select.querySelector<HTMLOptionElement>('option[value="4"]')?.selected).toBe(true)
201
- })
202
-
203
- test('mix of options get selected correctly', () => {
204
- select.setSelectedByValue(['2', '3', '6'])
205
-
206
- expect(select.select.querySelector<HTMLOptionElement>('option[value="2"]')?.selected).toBe(true)
207
- expect(select.select.querySelector<HTMLOptionElement>('option[value="3"]')?.selected).toBe(true)
208
- expect(select.select.querySelector<HTMLOptionElement>('option[value="6"]')?.selected).toBe(true)
209
- })
210
- })
211
-
212
- describe('updateSelected', () => {
213
- test('id gets updated correctly', () => {
214
- select.select.dataset.id = 'old_id'
215
-
216
- select.updateSelect('new_id')
217
-
218
- expect(select.select.dataset.id).toBe('new_id')
219
- })
220
-
221
- test('new styles are set correctly', () => {
222
- select.updateSelect(undefined, 'color: red')
223
-
224
- expect(select.select.style.color).toBe('red')
225
- })
226
-
227
- test("setting styles doesn't override id", () => {
228
- select.select.dataset.id = 'set_id'
229
-
230
- select.updateSelect(undefined, 'color: red')
231
-
232
- expect(select.select.dataset.id).toBe('set_id')
233
- })
234
-
235
- test('classes are set correctly', () => {
236
- select.updateSelect(undefined, undefined, ['class0', 'class1'])
237
-
238
- expect(select.select.classList.contains('class0')).toBe(true)
239
- expect(select.select.classList.contains('class1')).toBe(true)
240
- })
241
- })
242
-
243
- test('update select from data', () => {
244
- document.body.innerHTML = '<select id="test"></select>'
245
-
246
- let selectElement = document.getElementById('test') as HTMLSelectElement
247
- let select = new Select(selectElement)
248
- let store = new Store('single', [
249
- {
250
- id: '1',
251
- value: '1',
252
- text: 'One',
253
- selected: false
254
- },
255
- {
256
- id: '2',
257
- value: '2',
258
- text: 'Two',
259
- selected: false
260
- }
261
- ])
262
- let data = store.getData()
263
- select.updateOptions(data)
264
-
265
- expect(selectElement.outerHTML).toBe(
266
- '<select id="test"><option id="1" value="1">One</option><option id="2" value="2">Two</option></select>'
267
- )
268
- })
269
-
270
- describe('onValueChange', () => {
271
- let select: Select
272
- let selectElement: HTMLSelectElement
273
-
274
- beforeEach(() => {
275
- document.body.innerHTML = `<select id="test">
276
- <option value="1">One</option>
277
- <option value="2">Two</option>
278
- <option value="3">Three</option>
279
- </select>`
280
-
281
- selectElement = document.getElementById('test') as HTMLSelectElement
282
- select = new Select(selectElement)
283
- })
284
-
285
- test('listener is triggered when the select value changes', async () => {
286
- // make sure the first element is selected as standard
287
- let data = select.getData() as Option[]
288
- expect(data[0].selected).toBe(true)
289
-
290
- const onValueMock = vi.fn()
291
- select.onValueChange = onValueMock
292
-
293
- // Change the value
294
- selectElement.value = '2'
295
- selectElement.dispatchEvent(new Event('change'))
296
-
297
- await new Promise((r) => setTimeout(r, 50))
298
- expect(onValueMock).toHaveBeenCalled()
299
-
300
- // Get selected data
301
- const selected = select.getSelectedValues()
302
- expect(selected[0]).toBe('2')
303
-
304
- // Check all data
305
- data = select.getData() as Option[]
306
- expect(data[0].selected).toBe(false)
307
- expect(data[1].selected).toBe(true)
308
- })
309
-
310
- test('listener is triggered when inner HTML is replaced with new options', async () => {
311
- const onValueMock = vi.fn()
312
- select.onValueChange = onValueMock
313
-
314
- selectElement.innerHTML = `<option value="4">Four</option>
315
- <option value="5" selected>Five</option>
316
- <option value="6">Six</option>`
317
-
318
- await new Promise((r) => setTimeout(r, 50))
319
- expect(onValueMock).toHaveBeenCalled()
320
-
321
- // Give the mutation observer time to run
322
- const data = select.getData() as Option[]
323
- expect(data[1].value).toBe('5')
324
- expect(data[1].selected).toBe(true)
325
- })
326
- })
327
-
328
- describe('onOptionsChange', () => {
329
- test('listener triggers when select options change without changing the select value', async () => {
330
- document.body.innerHTML = `<select id="test">
331
- <option value="1">One</option>
332
- <option value="2">Two</option>
333
- <option value="3">Three</option>
334
- </select>`
335
-
336
- const selectElement = document.getElementById('test') as HTMLSelectElement
337
- const select = new Select(selectElement)
338
-
339
- let data = select.getData() as Option[]
340
- expect(data).toHaveLength(3)
341
-
342
- const onOptionsMock = vi.fn()
343
- select.onOptionsChange = onOptionsMock
344
-
345
- selectElement.innerHTML = '<option value="1">One</option><option value="2">Two</option>'
346
-
347
- await new Promise((r) => setTimeout(r, 50))
348
- expect(onOptionsMock).toHaveBeenCalled()
349
-
350
- data = select.getData() as Option[]
351
- expect(data).toHaveLength(2)
352
- })
353
-
354
- test('listener triggers when optgroup options change', async () => {
355
- document.body.innerHTML = `<select id="test">
356
- <optgroup id="test_optgroup" label="test1">
357
- <option value="1">One</option>
358
- <option value="2">Two</option>
359
- </optgroup>
360
- <optgroup label="test2">
361
- <option value="3">Three</option>
362
- <option value="4">Four</option>
363
- <option value="5">Five</option>
364
- </optgroup>
365
- </select>`
366
-
367
- const selectElement = document.getElementById('test') as HTMLSelectElement
368
- const select = new Select(selectElement)
369
-
370
- const optGroups = select.getData() as Optgroup[]
371
- expect(optGroups).toHaveLength(2)
372
-
373
- const onOptionsMock = vi.fn()
374
- select.onOptionsChange = onOptionsMock
375
-
376
- const selectOptgroup = document.getElementById('test_optgroup') as HTMLOptGroupElement
377
- selectOptgroup.innerHTML = '<option value="8">Eight</option><option value="9">Nine</option>'
378
-
379
- await new Promise((r) => setTimeout(r, 50))
380
- expect(onOptionsMock).toHaveBeenCalled()
381
-
382
- const options = select.getData() as Option[]
383
- expect(options).toHaveLength(2)
384
-
385
- // get selected data
386
- const selected = select.getSelectedValues()
387
- expect(selected).toHaveLength(1)
388
- })
389
-
390
- test('listener triggers when select option text changes', async () => {
391
- document.body.innerHTML = `<select id="test">
392
- <option value="1">One</option>
393
- <option value="2">Two</option>
394
- <option value="3">Three</option>
395
- </select>`
396
-
397
- const selectElement = document.getElementById('test') as HTMLSelectElement
398
- const select = new Select(selectElement)
399
-
400
- let data = select.getData() as Option[]
401
- expect(data[0].text).toBe('One')
402
-
403
- const onOptionsMock = vi.fn()
404
- select.onOptionsChange = onOptionsMock
405
-
406
- let option = selectElement.options[0]
407
- option.text = 'New One'
408
-
409
- await new Promise((r) => setTimeout(r, 50))
410
- expect(onOptionsMock).toHaveBeenCalled()
411
-
412
- data = select.getData() as Option[]
413
- expect(data[0].text).toBe('New One')
414
- })
415
-
416
- test('listener triggers when select optgroup option text changes', async () => {
417
- document.body.innerHTML = `<select id="test">
418
- <optgroup id="test_optgroup" label="test1">
419
- <option id=test_option value="1">One</option>
420
- <option value="2">Two</option>
421
- </optgroup>
422
- <optgroup label="test2">
423
- <option value="3">Three</option>
424
- <option value="4">Four</option>
425
- <option value="5">Five</option>
426
- </optgroup>
427
- </select>`
428
-
429
- const selectElement = document.getElementById('test') as HTMLSelectElement
430
- const select = new Select(selectElement)
431
-
432
- let dataOptgroup = select.getData() as Optgroup[]
433
-
434
- expect(dataOptgroup[0].options[0].text).toBe('One')
435
-
436
- const onOptionsMock = vi.fn()
437
- select.onOptionsChange = onOptionsMock
438
-
439
- let selectOption = document.getElementById('test_option') as HTMLOptionElement
440
- selectOption.text = 'New One'
441
-
442
- await new Promise((r) => setTimeout(r, 50))
443
- expect(onOptionsMock).toHaveBeenCalled()
444
-
445
- let dataOption = select.getData() as Optgroup[]
446
- expect(dataOption[0].options[0].text).toBe('New One')
447
- })
448
- })
449
-
450
- describe('label handling', () => {
451
- test('setupLabelHandlers finds label with for attribute and adds click handler', async () => {
452
- document.body.innerHTML = `
453
- <label for="test-select">Select Label</label>
454
- <select id="test-select">
455
- <option value="1">One</option>
456
- <option value="2">Two</option>
457
- </select>
458
- `
459
-
460
- const selectElement = document.getElementById('test-select') as HTMLSelectElement
461
- const select = new Select(selectElement)
462
- select.hideUI()
463
-
464
- const onLabelClickMock = vi.fn()
465
- select.onLabelClick = onLabelClickMock
466
-
467
- select.setupLabelHandlers()
468
-
469
- const label = document.querySelector('label[for="test-select"]') as HTMLLabelElement
470
- expect(label).toBeTruthy()
471
-
472
- // Click the label
473
- label.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
474
-
475
- // Wait for setTimeout in the label handler
476
- await new Promise((r) => setTimeout(r, 10))
477
-
478
- // Should have prevented default and called the callback
479
- expect(onLabelClickMock).toHaveBeenCalled()
480
- })
481
-
482
- test('setupLabelHandlers finds wrapped label and adds click handler', async () => {
483
- document.body.innerHTML = `
484
- <label>
485
- Select Label
486
- <select id="test-select">
487
- <option value="1">One</option>
488
- <option value="2">Two</option>
489
- </select>
490
- </label>
491
- `
492
-
493
- const selectElement = document.getElementById('test-select') as HTMLSelectElement
494
- const select = new Select(selectElement)
495
- select.hideUI()
496
-
497
- const onLabelClickMock = vi.fn()
498
- select.onLabelClick = onLabelClickMock
499
-
500
- select.setupLabelHandlers()
501
-
502
- const label = selectElement.parentElement as HTMLLabelElement
503
- expect(label.tagName).toBe('LABEL')
504
-
505
- // Click the label
506
- label.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
507
-
508
- // Wait for setTimeout in the label handler
509
- await new Promise((r) => setTimeout(r, 10))
510
-
511
- // Should have called the callback
512
- expect(onLabelClickMock).toHaveBeenCalled()
513
- })
514
-
515
- test('setupLabelHandlers handles multiple labels', async () => {
516
- document.body.innerHTML = `
517
- <label for="test-select">Label 1</label>
518
- <label for="test-select">Label 2</label>
519
- <select id="test-select">
520
- <option value="1">One</option>
521
- </select>
522
- `
523
-
524
- const selectElement = document.getElementById('test-select') as HTMLSelectElement
525
- const select = new Select(selectElement)
526
- select.hideUI()
527
-
528
- const onLabelClickMock = vi.fn()
529
- select.onLabelClick = onLabelClickMock
530
-
531
- select.setupLabelHandlers()
532
-
533
- const labels = document.querySelectorAll('label[for="test-select"]')
534
- expect(labels).toHaveLength(2)
535
-
536
- // Click first label
537
- labels[0].dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
538
- await new Promise((r) => setTimeout(r, 10))
539
- expect(onLabelClickMock).toHaveBeenCalledTimes(1)
540
-
541
- // Click second label
542
- labels[1].dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
543
- await new Promise((r) => setTimeout(r, 10))
544
- expect(onLabelClickMock).toHaveBeenCalledTimes(2)
545
- })
546
-
547
- test('removeLabelHandlers removes click handlers from labels', () => {
548
- document.body.innerHTML = `
549
- <label for="test-select">Select Label</label>
550
- <select id="test-select">
551
- <option value="1">One</option>
552
- </select>
553
- `
554
-
555
- const selectElement = document.getElementById('test-select') as HTMLSelectElement
556
- const select = new Select(selectElement)
557
- select.hideUI()
558
-
559
- const onLabelClickMock = vi.fn()
560
- select.onLabelClick = onLabelClickMock
561
-
562
- select.setupLabelHandlers()
563
- select.removeLabelHandlers()
564
-
565
- const label = document.querySelector('label[for="test-select"]') as HTMLLabelElement
566
- label.dispatchEvent(new MouseEvent('click', { bubbles: true }))
567
-
568
- // Should not have been called after removal
569
- expect(onLabelClickMock).not.toHaveBeenCalled()
570
- })
571
-
572
- test('hideUI prevents native select from opening on click', () => {
573
- document.body.innerHTML = '<select id="test"><option value="1">One</option></select>'
574
-
575
- const selectElement = document.getElementById('test') as HTMLSelectElement
576
- const select = new Select(selectElement)
577
- select.hideUI()
578
-
579
- const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true })
580
- const prevented = !selectElement.dispatchEvent(clickEvent)
581
-
582
- // The event should be prevented by our handler
583
- expect(clickEvent.defaultPrevented).toBe(true)
584
- })
585
-
586
- test('hideUI prevents native select from opening on focus', () => {
587
- document.body.innerHTML = '<select id="test"><option value="1">One</option></select>'
588
-
589
- const selectElement = document.getElementById('test') as HTMLSelectElement
590
- const select = new Select(selectElement)
591
- select.hideUI()
592
-
593
- const focusEvent = new FocusEvent('focus', { bubbles: true, cancelable: true })
594
- selectElement.dispatchEvent(focusEvent)
595
-
596
- // The event should be prevented by our handler
597
- expect(focusEvent.defaultPrevented).toBe(true)
598
- })
599
-
600
- test('hideUI prevents native select from opening on mousedown', () => {
601
- document.body.innerHTML = '<select id="test"><option value="1">One</option></select>'
602
-
603
- const selectElement = document.getElementById('test') as HTMLSelectElement
604
- const select = new Select(selectElement)
605
- select.hideUI()
606
-
607
- const mousedownEvent = new MouseEvent('mousedown', { bubbles: true, cancelable: true })
608
- selectElement.dispatchEvent(mousedownEvent)
609
-
610
- // The event should be prevented by our handler
611
- expect(mousedownEvent.defaultPrevented).toBe(true)
612
- })
613
-
614
- test('showUI removes event handlers that prevent native select', () => {
615
- document.body.innerHTML = '<select id="test"><option value="1">One</option></select>'
616
-
617
- const selectElement = document.getElementById('test') as HTMLSelectElement
618
- const select = new Select(selectElement)
619
- select.hideUI()
620
- select.showUI()
621
-
622
- // After showUI, events should not be prevented
623
- const clickEvent = new MouseEvent('click', { bubbles: true, cancelable: true })
624
- selectElement.dispatchEvent(clickEvent)
625
-
626
- // Note: The event may still propagate normally, but defaultPrevented should be false
627
- // We test this by ensuring the select doesn't have our prevent handlers attached
628
- // The actual behavior depends on browser implementation
629
- })
630
-
631
- test('destroy removes label handlers', async () => {
632
- document.body.innerHTML = `
633
- <label for="test-select">Select Label</label>
634
- <select id="test-select">
635
- <option value="1">One</option>
636
- </select>
637
- `
638
-
639
- const selectElement = document.getElementById('test-select') as HTMLSelectElement
640
- const select = new Select(selectElement)
641
- select.hideUI()
642
-
643
- const onLabelClickMock = vi.fn()
644
- select.onLabelClick = onLabelClickMock
645
-
646
- select.setupLabelHandlers()
647
-
648
- // Verify handler works
649
- const label = document.querySelector('label[for="test-select"]') as HTMLLabelElement
650
- label.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
651
- await new Promise((r) => setTimeout(r, 10))
652
- expect(onLabelClickMock).toHaveBeenCalledTimes(1)
653
-
654
- // Destroy and verify handler removed
655
- select.destroy()
656
- onLabelClickMock.mockClear()
657
-
658
- label.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }))
659
- await new Promise((r) => setTimeout(r, 10))
660
- expect(onLabelClickMock).not.toHaveBeenCalled()
661
- })
662
-
663
- test('setupLabelHandlers does nothing if select has no id and no wrapping label', () => {
664
- document.body.innerHTML = `
665
- <div>
666
- <select>
667
- <option value="1">One</option>
668
- </select>
669
- </div>
670
- `
671
-
672
- const selectElement = document.querySelector('select') as HTMLSelectElement
673
- const select = new Select(selectElement)
674
- select.hideUI()
675
-
676
- const onLabelClickMock = vi.fn()
677
- select.onLabelClick = onLabelClickMock
678
-
679
- // Should not throw or error
680
- expect(() => select.setupLabelHandlers()).not.toThrow()
681
- })
682
- })
683
- })