slim-select 3.4.3 → 3.5.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.
- package/dist/classes.d.ts +46 -0
- package/dist/helpers.d.ts +5 -0
- package/dist/index.d.ts +53 -319
- package/dist/index.umd.d.ts +2 -0
- package/dist/react/index.d.ts +4 -319
- package/dist/react/index.js +1459 -1697
- package/dist/react/index.umd.js +2 -2
- package/dist/react/react.d.ts +17 -0
- package/dist/react/react.test.d.ts +1 -0
- package/dist/react/slim-select/react/index.d.ts +4 -0
- package/dist/render.d.ts +88 -0
- package/dist/select.d.ts +38 -0
- package/dist/settings.d.ts +39 -0
- package/dist/slimselect.cjs.js +2 -2
- package/dist/slimselect.cjs.js.map +1 -1
- package/dist/slimselect.css +1 -1
- package/dist/slimselect.css.map +1 -1
- package/dist/slimselect.es.js +1233 -1385
- package/dist/slimselect.es.js.map +1 -1
- package/dist/slimselect.iife.js +2 -2
- package/dist/slimselect.iife.js.map +1 -1
- package/dist/slimselect.js +2 -2
- package/dist/slimselect.js.map +1 -1
- package/dist/src/docs/main.d.ts +0 -0
- package/dist/src/docs/router.d.ts +2 -0
- package/dist/src/docs/store.d.ts +12 -0
- package/dist/store.d.ts +53 -0
- package/dist/vue/index.d.ts +4 -319
- package/dist/vue/index.js +1335 -1504
- package/dist/vue/index.umd.js +1 -1
- package/dist/vue/slim-select/vue/index.d.ts +4 -0
- package/package.json +30 -36
- package/src/slim-select/slimselect.scss +34 -5
- package/.nvmrc +0 -1
- package/.prettierignore +0 -1
- package/.prettierrc +0 -7
- package/CODE_OF_CONDUCT.md +0 -46
- package/CONTRIBUTING.md +0 -1
- package/cdn.html +0 -21
- package/docs.go +0 -12
- package/example.html +0 -25
- package/index.html +0 -76
- package/release.js +0 -191
- package/sitemap.js +0 -108
- package/src/slim-select/accessibility.test.ts +0 -782
- package/src/slim-select/classes.test.ts +0 -73
- package/src/slim-select/classes.ts +0 -117
- package/src/slim-select/helpers.test.ts +0 -162
- package/src/slim-select/helpers.ts +0 -61
- package/src/slim-select/index.test.ts +0 -1356
- package/src/slim-select/index.ts +0 -594
- package/src/slim-select/index.umd.ts +0 -8
- package/src/slim-select/react/index.tsx +0 -9
- package/src/slim-select/react/react.test.tsx +0 -498
- package/src/slim-select/react/react.tsx +0 -209
- package/src/slim-select/render.test.ts +0 -1927
- package/src/slim-select/render.ts +0 -1771
- package/src/slim-select/select.test.ts +0 -683
- package/src/slim-select/select.ts +0 -659
- package/src/slim-select/settings.test.ts +0 -285
- package/src/slim-select/settings.ts +0 -78
- package/src/slim-select/store.test.ts +0 -1007
- package/src/slim-select/store.ts +0 -433
- package/src/slim-select/vue/index.ts +0 -9
- package/src/slim-select/vue/vue.test.ts +0 -1091
- package/src/slim-select/vue/vue.vue +0 -224
- package/tsconfig.json +0 -26
- package/vite.config.docs.mts +0 -29
- package/vite.config.lib.es-cjs.ts +0 -40
- package/vite.config.lib.umd-iife.ts +0 -34
- package/vite.config.react.mts +0 -41
- package/vite.config.vue.mts +0 -40
- 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
|
-
})
|