@oslokommune/punkt-elements 18.7.1 → 18.7.2
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/CHANGELOG.md +17 -0
- package/dist/{accordion-NO9LYXaN.cjs → accordion-CsrLQXEA.cjs} +1 -1
- package/dist/{accordion-BI-Qiznz.js → accordion-DoxhiwK-.js} +5 -5
- package/dist/{alert-HFqn-cvb.cjs → alert-DH3VqNqh.cjs} +1 -1
- package/dist/{alert-CayKowTW.js → alert-DxAGxSX_.js} +6 -6
- package/dist/{async-directive-CAZoZMez.js → async-directive-BSwJ8-GL.js} +2 -2
- package/dist/{async-directive-DlB0Sf4j.cjs → async-directive-CfPXShXu.cjs} +1 -1
- package/dist/{backlink-DYO8qA9C.cjs → backlink-CDH824db.cjs} +1 -1
- package/dist/{backlink-BHtxOlTw.js → backlink-CGc7r9CS.js} +3 -3
- package/dist/{breadcrumbs-HG9wzGtr.cjs → breadcrumbs-B37tsIoh.cjs} +1 -1
- package/dist/{breadcrumbs-BlRDuZbZ.js → breadcrumbs-DRGhIXmN.js} +2 -2
- package/dist/{button-CIb0IxTg.cjs → button-Cx6Xdp6t.cjs} +1 -1
- package/dist/{button-DWMHtdWk.js → button-D6QhHPWH.js} +5 -5
- package/dist/{calendar-CBghTpkZ.js → calendar-42o_q-jI.js} +3 -3
- package/dist/{calendar-CA1mz04B.cjs → calendar-CAwIY7x_.cjs} +1 -1
- package/dist/{card-CsSNvjZV.cjs → card-CB72gwBC.cjs} +1 -1
- package/dist/{card-PXDm9JXv.js → card-cyZE94EO.js} +6 -6
- package/dist/{checkbox-DReZOEy2.js → checkbox-C6JdANel.js} +4 -4
- package/dist/{checkbox-CMu4pF8-.cjs → checkbox-CddODhna.cjs} +1 -1
- package/dist/{class-map-BL_YL_Ed.js → class-map-DSoj1J68.js} +1 -1
- package/dist/{class-map-BEib48YL.cjs → class-map-DupQbnhE.cjs} +1 -1
- package/dist/{combobox-DcBFiuwP.js → combobox-Cqqokx73.js} +172 -128
- package/dist/combobox-N3NP77le.cjs +137 -0
- package/dist/components/combobox/combobox-base.d.ts +14 -12
- package/dist/components/combobox/combobox-handlers.d.ts +1 -0
- package/dist/components/combobox/combobox-utils.d.ts +3 -1
- package/dist/components/combobox/combobox-value.d.ts +5 -0
- package/dist/components/combobox/combobox.attributes.test.d.ts +0 -0
- package/dist/components/combobox/combobox.d.ts +31 -2
- package/dist/components/combobox/combobox.preselection.test.d.ts +0 -0
- package/dist/components/combobox/index.d.ts +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/components/listbox/listbox.d.ts +3 -1
- package/dist/{consent-Yt1MpkUy.js → consent-CsgKWDsR.js} +2 -2
- package/dist/{consent-D1SMUJLs.cjs → consent-DnE6IPEj.cjs} +1 -1
- package/dist/{datepicker-NSWMVoGi.cjs → datepicker-BzRhOIkG.cjs} +1 -1
- package/dist/{datepicker-DboWQcrh.js → datepicker-CtSYgxMs.js} +11 -11
- package/dist/{directive-helpers-Ddr8wUxL.cjs → directive-helpers-2mcpDDv5.cjs} +1 -1
- package/dist/{directive-helpers-CohhRNQz.js → directive-helpers-ChEduoup.js} +1 -1
- package/dist/{element-DRc10X2R.cjs → element-D_mCyFeX.cjs} +1 -1
- package/dist/{element-e2MAMNEP.js → element-J8nynuVk.js} +1 -0
- package/dist/{element-with-slot-GwTT3WRD.js → element-with-slot-BOuXlWSY.js} +2 -2
- package/dist/{element-with-slot-DgtT8lCH.cjs → element-with-slot-C66Jck5e.cjs} +1 -1
- package/dist/{fileupload-BYVtnSaI.cjs → fileupload-3OtW4Xlo.cjs} +1 -1
- package/dist/{fileupload-CqxWUH0y.js → fileupload-PJWEBD88.js} +8 -8
- package/dist/{footer-YrlXRq7e.js → footer-ByAxlj9_.js} +4 -4
- package/dist/{footer-CLSihvYS.cjs → footer-Ca3DM544.cjs} +1 -1
- package/dist/{header-DsaPCLjJ.js → header-CxfDtS3Y.js} +11 -11
- package/dist/{header-DzVHM7gZ.cjs → header-D2SD2JLt.cjs} +1 -1
- package/dist/{header-menu-wcnhBSzs.cjs → header-menu-B9ABa0WW.cjs} +1 -1
- package/dist/{header-menu-DKAcUFzh.js → header-menu-CIuu8_UC.js} +4 -4
- package/dist/{heading-OfJKjUi2.cjs → heading-4ZHACOeL.cjs} +1 -1
- package/dist/{heading-C23xQS4l.js → heading-BHWdVbvN.js} +2 -2
- package/dist/{helptext-DyHzv2ZG.cjs → helptext-6HsH-I6a.cjs} +1 -1
- package/dist/{helptext-DqdOdYRj.js → helptext-BESB9Vlq.js} +4 -4
- package/dist/{icon-DBFTFd-J.cjs → icon-BzxBkRyE.cjs} +1 -1
- package/dist/{icon-jEFbKTB_.js → icon-DkVqRWbC.js} +1 -1
- package/dist/{if-defined-r8tKY4tG.js → if-defined-BILLSM7i.js} +1 -1
- package/dist/if-defined-BbUZeoaQ.cjs +1 -0
- package/dist/{input-element-kvI_5er5.cjs → input-element-DhIEFeuP.cjs} +1 -1
- package/dist/{input-element-BiUfK_gU.js → input-element-Qb0w3ChW.js} +2 -2
- package/dist/{input-wrapper-Bwh720ke.js → input-wrapper-Bg06k-cj.js} +6 -6
- package/dist/{input-wrapper-wyvCnWPp.cjs → input-wrapper-D64Qn-QH.cjs} +1 -1
- package/dist/{link-BWAhHUku.cjs → link-Bk7aDZCC.cjs} +1 -1
- package/dist/{link-BvjWWWlc.js → link-C7fOEv66.js} +4 -4
- package/dist/{linkcard-6CbGf4I_.js → linkcard-Bguo9oCv.js} +4 -4
- package/dist/{linkcard-D6fRaZSc.cjs → linkcard-rZpQuO1Z.cjs} +1 -1
- package/dist/{listbox-CsP0qbU1.js → listbox-CrQboPG6.js} +80 -63
- package/dist/listbox-kV4OO1Qa.cjs +111 -0
- package/dist/{loader-B5t78Yk7.cjs → loader-BAn68gBb.cjs} +1 -1
- package/dist/{loader-BS7EJqxD.js → loader-C3gwO0i3.js} +4 -4
- package/dist/{messagebox-Bt0IvUlD.js → messagebox-DAE59dMX.js} +4 -4
- package/dist/{messagebox-DBVUz-DN.cjs → messagebox-WH2HjT5I.cjs} +1 -1
- package/dist/{modal-C1g2fiUq.cjs → modal-DZm_fn3K.cjs} +1 -1
- package/dist/{modal-z5RzZ-fr.js → modal-DdC1_qT3.js} +5 -5
- package/dist/pkt-accordion.cjs +1 -1
- package/dist/pkt-accordion.js +1 -1
- package/dist/pkt-alert.cjs +1 -1
- package/dist/pkt-alert.js +1 -1
- package/dist/pkt-backlink.cjs +1 -1
- package/dist/pkt-backlink.js +1 -1
- package/dist/pkt-breadcrumbs.cjs +1 -1
- package/dist/pkt-breadcrumbs.js +1 -1
- package/dist/pkt-button.cjs +1 -1
- package/dist/pkt-button.js +1 -1
- package/dist/pkt-calendar.cjs +1 -1
- package/dist/pkt-calendar.js +1 -1
- package/dist/pkt-card.cjs +1 -1
- package/dist/pkt-card.js +1 -1
- package/dist/pkt-checkbox.cjs +1 -1
- package/dist/pkt-checkbox.js +1 -1
- package/dist/pkt-combobox.cjs +1 -1
- package/dist/pkt-combobox.js +1 -1
- package/dist/pkt-consent.cjs +1 -1
- package/dist/pkt-consent.js +1 -1
- package/dist/pkt-datepicker.cjs +1 -1
- package/dist/pkt-datepicker.js +1 -1
- package/dist/pkt-fileupload.cjs +1 -1
- package/dist/pkt-fileupload.js +1 -1
- package/dist/pkt-footer.cjs +1 -1
- package/dist/pkt-footer.js +1 -1
- package/dist/pkt-header-menu.cjs +1 -1
- package/dist/pkt-header-menu.js +1 -1
- package/dist/pkt-header.cjs +1 -1
- package/dist/pkt-header.js +1 -1
- package/dist/pkt-heading.cjs +1 -1
- package/dist/pkt-heading.js +1 -1
- package/dist/pkt-helptext.cjs +1 -1
- package/dist/pkt-helptext.js +1 -1
- package/dist/pkt-icon.cjs +1 -1
- package/dist/pkt-icon.js +1 -1
- package/dist/pkt-index.cjs +1 -1
- package/dist/pkt-index.js +34 -34
- package/dist/pkt-input-wrapper.cjs +1 -1
- package/dist/pkt-input-wrapper.js +1 -1
- package/dist/pkt-link.cjs +1 -1
- package/dist/pkt-link.js +1 -1
- package/dist/pkt-linkcard.cjs +1 -1
- package/dist/pkt-linkcard.js +1 -1
- package/dist/pkt-listbox.cjs +1 -1
- package/dist/pkt-listbox.js +1 -1
- package/dist/pkt-loader.cjs +1 -1
- package/dist/pkt-loader.js +1 -1
- package/dist/pkt-messagebox.cjs +1 -1
- package/dist/pkt-messagebox.js +1 -1
- package/dist/pkt-modal.cjs +1 -1
- package/dist/pkt-modal.js +1 -1
- package/dist/{pkt-options-controller-AFYyv3-b.cjs → pkt-options-controller-DXAAVcBJ.cjs} +1 -1
- package/dist/{pkt-options-controller-DKkmDDzT.js → pkt-options-controller-XcIgx_v1.js} +1 -1
- package/dist/pkt-progressbar.cjs +1 -1
- package/dist/pkt-progressbar.js +1 -1
- package/dist/pkt-radiobutton.cjs +1 -1
- package/dist/pkt-radiobutton.js +1 -1
- package/dist/pkt-searchinput.cjs +1 -1
- package/dist/pkt-searchinput.js +1 -1
- package/dist/pkt-select.cjs +1 -1
- package/dist/pkt-select.js +1 -1
- package/dist/pkt-tabs.cjs +1 -1
- package/dist/pkt-tabs.js +1 -1
- package/dist/pkt-tag.cjs +1 -1
- package/dist/pkt-tag.js +1 -1
- package/dist/pkt-textarea.cjs +1 -1
- package/dist/pkt-textarea.js +1 -1
- package/dist/pkt-textinput.cjs +1 -1
- package/dist/pkt-textinput.js +1 -1
- package/dist/pkt-timepicker.cjs +1 -1
- package/dist/pkt-timepicker.js +1 -1
- package/dist/{progressbar-BPHLC7ce.js → progressbar-D1x6MwdY.js} +5 -5
- package/dist/{progressbar-BpcsDCnG.cjs → progressbar-DsE4-qaW.cjs} +1 -1
- package/dist/{radiobutton-bGdAEz26.js → radiobutton-B2P48MMf.js} +4 -4
- package/dist/{radiobutton-B6pzZQZV.cjs → radiobutton-CdwYbHnx.cjs} +1 -1
- package/dist/{ref-BkV4Cd8U.cjs → ref-AuNEkS5a.cjs} +1 -1
- package/dist/{ref-lE1mGUOL.js → ref-DNowxdYH.js} +2 -2
- package/dist/{repeat-uBgVG8Ch.cjs → repeat-B3ksuY2F.cjs} +1 -1
- package/dist/{repeat-CCnTfedv.js → repeat-CZ1oMXnW.js} +2 -2
- package/dist/{searchinput-DrUcPajl.js → searchinput-CmyWnNr6.js} +4 -4
- package/dist/{searchinput-CiPB8xvh.cjs → searchinput-vi7twt5b.cjs} +1 -1
- package/dist/{select-ay8WHI7p.cjs → select-TAwBNhhf.cjs} +1 -1
- package/dist/{select-D7r2s7Cw.js → select-c15YAQbr.js} +7 -7
- package/dist/shared-strings/nb.d.ts +1 -0
- package/dist/{tabs-DqzPOdi9.cjs → tabs-hiTrWsXq.cjs} +1 -1
- package/dist/{tabs-DHvCjUi4.js → tabs-kxsX1ZEf.js} +4 -4
- package/dist/{tag-Cojf4jgA.cjs → tag-CGlxtBOH.cjs} +1 -1
- package/dist/{tag-D2Xvsy1i.js → tag-bztxjcF0.js} +5 -5
- package/dist/{textarea-s4IDLsBi.cjs → textarea-BHGZhMN4.cjs} +1 -1
- package/dist/{textarea-CzAL8e6L.js → textarea-BPQVrOp9.js} +9 -9
- package/dist/{textinput-B9w4jhIP.cjs → textinput-Cv4tal5W.cjs} +1 -1
- package/dist/{textinput-DT3HR9eU.js → textinput-DHGUOsIA.js} +8 -8
- package/dist/{timepicker-C-Ip5Jgl.cjs → timepicker-BBUYnYzM.cjs} +1 -1
- package/dist/{timepicker-BE0UouVr.js → timepicker-C8DHbOpM.js} +8 -8
- package/package.json +2 -2
- package/src/components/combobox/combobox-base.ts +42 -28
- package/src/components/combobox/combobox-handlers.ts +11 -5
- package/src/components/combobox/combobox-utils.ts +9 -1
- package/src/components/combobox/combobox-value.ts +32 -16
- package/src/components/combobox/combobox.accessibility.test.ts +29 -3
- package/src/components/combobox/combobox.attributes.test.ts +82 -0
- package/src/components/combobox/combobox.core.test.ts +46 -1
- package/src/components/combobox/combobox.interaction.test.ts +167 -0
- package/src/components/combobox/combobox.preselection.test.ts +271 -0
- package/src/components/combobox/combobox.ts +143 -79
- package/src/components/combobox/index.ts +6 -1
- package/src/components/index.ts +1 -0
- package/src/components/listbox/listbox.ts +56 -29
- package/dist/combobox-BZm_12qR.cjs +0 -140
- package/dist/if-defined-BeVQnl3C.cjs +0 -1
- package/dist/listbox-D95b5zAd.cjs +0 -106
|
@@ -175,6 +175,37 @@ describe('PktCombobox', () => {
|
|
|
175
175
|
expect(input.value).toBe('Apple')
|
|
176
176
|
})
|
|
177
177
|
|
|
178
|
+
test('closes the list when focus leaves the combobox', async () => {
|
|
179
|
+
const outside = document.createElement('button')
|
|
180
|
+
document.body.appendChild(outside)
|
|
181
|
+
|
|
182
|
+
const container = await createCombobox('id="test" name="test" label="Test"')
|
|
183
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
184
|
+
combobox.isOpen = true
|
|
185
|
+
await combobox.updateComplete
|
|
186
|
+
|
|
187
|
+
expect(combobox['_isOptionsOpen']).toBe(true)
|
|
188
|
+
|
|
189
|
+
const trigger = combobox.querySelector('[role="combobox"]') as HTMLElement
|
|
190
|
+
trigger.dispatchEvent(new FocusEvent('focusout', { bubbles: true, relatedTarget: outside }))
|
|
191
|
+
await combobox.updateComplete
|
|
192
|
+
|
|
193
|
+
expect(combobox['_isOptionsOpen']).toBe(false)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
test('keeps the list open when focus moves within the combobox', async () => {
|
|
197
|
+
const container = await createCombobox('id="test" name="test" label="Test"')
|
|
198
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
199
|
+
combobox.isOpen = true
|
|
200
|
+
await combobox.updateComplete
|
|
201
|
+
|
|
202
|
+
const trigger = combobox.querySelector('[role="combobox"]') as HTMLElement
|
|
203
|
+
trigger.dispatchEvent(new FocusEvent('focusout', { bubbles: true, relatedTarget: trigger }))
|
|
204
|
+
await combobox.updateComplete
|
|
205
|
+
|
|
206
|
+
expect(combobox['_isOptionsOpen']).toBe(true)
|
|
207
|
+
})
|
|
208
|
+
|
|
178
209
|
test('handles blur correctly', async () => {
|
|
179
210
|
const container = await createCombobox('id="test" name="test" label="Test" allow-user-input')
|
|
180
211
|
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
@@ -271,6 +302,142 @@ describe('PktCombobox', () => {
|
|
|
271
302
|
})
|
|
272
303
|
})
|
|
273
304
|
|
|
305
|
+
describe('Focus on open', () => {
|
|
306
|
+
const openAndSettle = async (combobox: PktCombobox) => {
|
|
307
|
+
const arrowButton = combobox.querySelector('.pkt-combobox__input')
|
|
308
|
+
fireEvent.click(arrowButton!)
|
|
309
|
+
await combobox.updateComplete
|
|
310
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
test('moves focus to the first option when the dropdown opens', async () => {
|
|
314
|
+
const container = await createCombobox('id="test" name="test" label="Test"')
|
|
315
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
316
|
+
combobox.options = [...defaultOptions]
|
|
317
|
+
await combobox.updateComplete
|
|
318
|
+
|
|
319
|
+
await openAndSettle(combobox)
|
|
320
|
+
|
|
321
|
+
const active = document.activeElement as HTMLElement
|
|
322
|
+
expect(active?.getAttribute('role')).toBe('option')
|
|
323
|
+
expect(active?.dataset.value).toBe('apple')
|
|
324
|
+
})
|
|
325
|
+
|
|
326
|
+
test('moves focus to the selected option when the dropdown opens', async () => {
|
|
327
|
+
const container = await createCombobox('id="test" name="test" label="Test" value="cherry"')
|
|
328
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
329
|
+
combobox.options = [...defaultOptions]
|
|
330
|
+
await combobox.updateComplete
|
|
331
|
+
|
|
332
|
+
await openAndSettle(combobox)
|
|
333
|
+
|
|
334
|
+
const active = document.activeElement as HTMLElement
|
|
335
|
+
expect(active?.getAttribute('role')).toBe('option')
|
|
336
|
+
expect(active?.dataset.value).toBe('cherry')
|
|
337
|
+
})
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
describe('Focus after committing a value', () => {
|
|
341
|
+
const commit = async (combobox: PktCombobox, typed: string) => {
|
|
342
|
+
const input = combobox.querySelector('input[type="text"]') as HTMLInputElement
|
|
343
|
+
fireEvent.focus(input)
|
|
344
|
+
await combobox.updateComplete
|
|
345
|
+
input.value = typed
|
|
346
|
+
fireEvent.input(input)
|
|
347
|
+
await combobox.updateComplete
|
|
348
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
349
|
+
await combobox.updateComplete
|
|
350
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
test('moves focus out of the text input after adding a user value', async () => {
|
|
354
|
+
const container = await createCombobox('id="test" name="test" label="Test" allow-user-input')
|
|
355
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
356
|
+
combobox.options = [...defaultOptions]
|
|
357
|
+
await combobox.updateComplete
|
|
358
|
+
|
|
359
|
+
await commit(combobox, 'NewFruit')
|
|
360
|
+
|
|
361
|
+
const input = combobox.querySelector('input[type="text"]')
|
|
362
|
+
expect(document.activeElement).not.toBe(input)
|
|
363
|
+
expect(document.activeElement).toBe(combobox.querySelector('.pkt-combobox__input'))
|
|
364
|
+
expect(combobox['_isOptionsOpen']).toBe(false)
|
|
365
|
+
})
|
|
366
|
+
|
|
367
|
+
test('moves focus out of the text input after selecting a typeahead match', async () => {
|
|
368
|
+
const container = await createCombobox('id="test" name="test" label="Test" typeahead')
|
|
369
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
370
|
+
combobox.options = [...defaultOptions]
|
|
371
|
+
await combobox.updateComplete
|
|
372
|
+
|
|
373
|
+
await commit(combobox, 'apple')
|
|
374
|
+
|
|
375
|
+
const input = combobox.querySelector('input[type="text"]')
|
|
376
|
+
expect(document.activeElement).not.toBe(input)
|
|
377
|
+
expect(document.activeElement).toBe(combobox.querySelector('.pkt-combobox__input'))
|
|
378
|
+
expect(combobox['_value']).toContain('apple')
|
|
379
|
+
})
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
describe('Selection announcement', () => {
|
|
383
|
+
const liveText = (container: HTMLElement) =>
|
|
384
|
+
container.querySelector('[aria-live="polite"]')?.textContent?.trim()
|
|
385
|
+
|
|
386
|
+
test('announces the displayed label, not the form value', async () => {
|
|
387
|
+
const container = await createCombobox('id="test" name="test" label="Test" typeahead')
|
|
388
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
389
|
+
combobox.options = [{ value: 'NO', label: 'Norge' }]
|
|
390
|
+
await combobox.updateComplete
|
|
391
|
+
|
|
392
|
+
const input = combobox.querySelector('input[type="text"]') as HTMLInputElement
|
|
393
|
+
input.focus()
|
|
394
|
+
input.value = 'NO'
|
|
395
|
+
fireEvent.input(input)
|
|
396
|
+
await combobox.updateComplete
|
|
397
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
398
|
+
await combobox.updateComplete
|
|
399
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
400
|
+
|
|
401
|
+
expect(input.value).toBe('Norge')
|
|
402
|
+
expect(liveText(container)).toBe('Norge er valgt')
|
|
403
|
+
})
|
|
404
|
+
|
|
405
|
+
test('announces the raw value when displayValueAs is value', async () => {
|
|
406
|
+
const container = await createCombobox(
|
|
407
|
+
'id="test" name="test" label="Test" typeahead display-value-as="value"',
|
|
408
|
+
)
|
|
409
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
410
|
+
combobox.options = [{ value: 'NO', label: 'Norge' }]
|
|
411
|
+
await combobox.updateComplete
|
|
412
|
+
|
|
413
|
+
const input = combobox.querySelector('input[type="text"]') as HTMLInputElement
|
|
414
|
+
input.focus()
|
|
415
|
+
input.value = 'NO'
|
|
416
|
+
fireEvent.input(input)
|
|
417
|
+
await combobox.updateComplete
|
|
418
|
+
fireEvent.keyDown(input, { key: 'Enter' })
|
|
419
|
+
await combobox.updateComplete
|
|
420
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
421
|
+
|
|
422
|
+
expect(liveText(container)).toBe('NO er valgt')
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
test('announces the prefix in select-only mode, matching the display', async () => {
|
|
426
|
+
const container = await createCombobox(
|
|
427
|
+
'id="test" name="test" label="Test" display-value-as="prefixAndValue"',
|
|
428
|
+
)
|
|
429
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
430
|
+
combobox.options = [{ value: 'NO', label: 'Norge', prefix: '+47' }]
|
|
431
|
+
await combobox.updateComplete
|
|
432
|
+
|
|
433
|
+
combobox.toggleValue('NO')
|
|
434
|
+
await combobox.updateComplete
|
|
435
|
+
|
|
436
|
+
expect(combobox.querySelector('.pkt-combobox__value')?.textContent?.trim()).toBe('+47 NO')
|
|
437
|
+
expect(liveText(container)).toBe('+47 NO er valgt')
|
|
438
|
+
})
|
|
439
|
+
})
|
|
440
|
+
|
|
274
441
|
describe('Search and filtering', () => {
|
|
275
442
|
test('filters options when typing in typeahead mode', async () => {
|
|
276
443
|
const container = await createCombobox('id="test" name="test" label="Test" typeahead')
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
|
|
3
|
+
import './combobox'
|
|
4
|
+
import { PktCombobox } from './combobox'
|
|
5
|
+
import type { IPktComboboxOption } from './combobox'
|
|
6
|
+
|
|
7
|
+
const bydeler: IPktComboboxOption[] = [
|
|
8
|
+
{ value: 'bfr', label: 'Frogner' },
|
|
9
|
+
{ value: 'bga', label: 'Grünerløkka' },
|
|
10
|
+
{ value: 'bsa', label: 'Sagene' },
|
|
11
|
+
{ value: 'bgo', label: 'Gamle Oslo' },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
const createCombobox = async (attrs: string, options?: IPktComboboxOption[]) => {
|
|
15
|
+
const container = document.createElement('div')
|
|
16
|
+
container.innerHTML = `<pkt-combobox ${attrs}></pkt-combobox>`
|
|
17
|
+
document.body.appendChild(container)
|
|
18
|
+
await customElements.whenDefined('pkt-combobox')
|
|
19
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
20
|
+
if (options) combobox.options = options.map((o) => ({ ...o }))
|
|
21
|
+
await combobox.updateComplete
|
|
22
|
+
return combobox
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Values the listbox renders as selected. */
|
|
26
|
+
const selectedInList = (combobox: PktCombobox) =>
|
|
27
|
+
Array.from(combobox.querySelectorAll('[role="option"]'))
|
|
28
|
+
.filter((el) => el.getAttribute('aria-selected') === 'true')
|
|
29
|
+
.map((el) => el.getAttribute('data-value'))
|
|
30
|
+
|
|
31
|
+
const tagLabels = (combobox: PktCombobox) =>
|
|
32
|
+
Array.from(combobox.querySelectorAll('pkt-tag')).map((el) => el.textContent?.trim())
|
|
33
|
+
|
|
34
|
+
afterEach(() => {
|
|
35
|
+
document.body.innerHTML = ''
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
describe('PktCombobox preselection', () => {
|
|
39
|
+
describe('Preselected via the value attribute', () => {
|
|
40
|
+
test('marks the preselected options in both the tag list and the listbox', async () => {
|
|
41
|
+
const combobox = await createCombobox(
|
|
42
|
+
'id="t" name="t" label="L" multiple tag-placement="outside" value="bfr,bga,bsa"',
|
|
43
|
+
bydeler,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
expect(combobox['_value']).toEqual(['bfr', 'bga', 'bsa'])
|
|
47
|
+
expect(selectedInList(combobox)).toEqual(['bfr', 'bga', 'bsa'])
|
|
48
|
+
expect(tagLabels(combobox)).toHaveLength(3)
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
test('keeps the existing selection in the listbox when another option is picked', async () => {
|
|
52
|
+
const combobox = await createCombobox(
|
|
53
|
+
'id="t" name="t" label="L" multiple tag-placement="outside" value="bfr,bga,bsa"',
|
|
54
|
+
bydeler,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
combobox.toggleValue('bgo')
|
|
58
|
+
await combobox.updateComplete
|
|
59
|
+
|
|
60
|
+
expect(combobox['_value']).toEqual(['bfr', 'bga', 'bsa', 'bgo'])
|
|
61
|
+
expect(selectedInList(combobox)).toEqual(['bfr', 'bga', 'bsa', 'bgo'])
|
|
62
|
+
expect(tagLabels(combobox)).toHaveLength(4)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('keeps the listbox in sync when an option is deselected', async () => {
|
|
66
|
+
const combobox = await createCombobox(
|
|
67
|
+
'id="t" name="t" label="L" multiple value="bfr,bga,bsa"',
|
|
68
|
+
bydeler,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
combobox.toggleValue('bga')
|
|
72
|
+
await combobox.updateComplete
|
|
73
|
+
|
|
74
|
+
expect(combobox['_value']).toEqual(['bfr', 'bsa'])
|
|
75
|
+
expect(selectedInList(combobox)).toEqual(['bfr', 'bsa'])
|
|
76
|
+
})
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
describe('Preselected via options.selected', () => {
|
|
80
|
+
test('seeds the value from options marked selected', async () => {
|
|
81
|
+
const combobox = await createCombobox('id="t" name="t" label="L" multiple typeahead', [
|
|
82
|
+
{ value: 'HODEPINE', selected: true },
|
|
83
|
+
{ value: 'KVALME' },
|
|
84
|
+
{ value: 'INGEN', selected: true },
|
|
85
|
+
])
|
|
86
|
+
|
|
87
|
+
expect(combobox['_value']).toEqual(['HODEPINE', 'INGEN'])
|
|
88
|
+
expect(selectedInList(combobox)).toEqual(['HODEPINE', 'INGEN'])
|
|
89
|
+
expect(tagLabels(combobox)).toHaveLength(2)
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
test('adds to the preselected values rather than replacing them', async () => {
|
|
93
|
+
const combobox = await createCombobox('id="t" name="t" label="L" multiple typeahead', [
|
|
94
|
+
{ value: 'HODEPINE', selected: true },
|
|
95
|
+
{ value: 'KVALME' },
|
|
96
|
+
{ value: 'INGEN', selected: true },
|
|
97
|
+
])
|
|
98
|
+
|
|
99
|
+
combobox.toggleValue('KVALME')
|
|
100
|
+
await combobox.updateComplete
|
|
101
|
+
|
|
102
|
+
expect(combobox['_value']).toEqual(['HODEPINE', 'INGEN', 'KVALME'])
|
|
103
|
+
expect(selectedInList(combobox)).toEqual(['HODEPINE', 'KVALME', 'INGEN'])
|
|
104
|
+
expect(tagLabels(combobox)).toHaveLength(3)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('takes only the first preselected option in single-select mode', async () => {
|
|
108
|
+
const combobox = await createCombobox('id="t" name="t" label="L"', [
|
|
109
|
+
{ value: 'HODEPINE', selected: true },
|
|
110
|
+
{ value: 'KVALME' },
|
|
111
|
+
])
|
|
112
|
+
|
|
113
|
+
expect(combobox['_value']).toEqual(['HODEPINE'])
|
|
114
|
+
})
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
describe('Preselected via defaultValue', () => {
|
|
118
|
+
test('seeds the value from defaultValue', async () => {
|
|
119
|
+
const combobox = await createCombobox('id="t" name="t" label="L" multiple', bydeler)
|
|
120
|
+
combobox.defaultValue = ['bfr', 'bga']
|
|
121
|
+
await combobox.updateComplete
|
|
122
|
+
|
|
123
|
+
expect(combobox['_value']).toEqual(['bfr', 'bga'])
|
|
124
|
+
expect(selectedInList(combobox)).toEqual(['bfr', 'bga'])
|
|
125
|
+
expect(tagLabels(combobox)).toHaveLength(2)
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
test('lets value take precedence over defaultValue', async () => {
|
|
129
|
+
const combobox = await createCombobox('id="t" name="t" label="L" multiple value="bsa"', bydeler)
|
|
130
|
+
combobox.defaultValue = ['bfr', 'bga']
|
|
131
|
+
await combobox.updateComplete
|
|
132
|
+
|
|
133
|
+
expect(combobox['_value']).toEqual(['bsa'])
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
test('does not reapply defaultValue after the selection is cleared', async () => {
|
|
137
|
+
const combobox = await createCombobox('id="t" name="t" label="L" multiple', bydeler)
|
|
138
|
+
combobox.defaultValue = ['bfr']
|
|
139
|
+
await combobox.updateComplete
|
|
140
|
+
expect(combobox['_value']).toEqual(['bfr'])
|
|
141
|
+
|
|
142
|
+
combobox.toggleValue('bfr')
|
|
143
|
+
await combobox.updateComplete
|
|
144
|
+
|
|
145
|
+
expect(combobox['_value']).toEqual([])
|
|
146
|
+
expect(selectedInList(combobox)).toEqual([])
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test('restores defaultValue on form reset', async () => {
|
|
150
|
+
const container = document.createElement('div')
|
|
151
|
+
container.innerHTML = `<form><pkt-combobox id="t" name="t" label="L" multiple></pkt-combobox></form>`
|
|
152
|
+
document.body.appendChild(container)
|
|
153
|
+
await customElements.whenDefined('pkt-combobox')
|
|
154
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
155
|
+
combobox.options = bydeler.map((o) => ({ ...o }))
|
|
156
|
+
combobox.defaultValue = ['bfr']
|
|
157
|
+
await combobox.updateComplete
|
|
158
|
+
|
|
159
|
+
combobox.toggleValue('bga')
|
|
160
|
+
await combobox.updateComplete
|
|
161
|
+
expect(combobox['_value']).toEqual(['bfr', 'bga'])
|
|
162
|
+
;(container.querySelector('form') as HTMLFormElement).reset()
|
|
163
|
+
await combobox.updateComplete
|
|
164
|
+
|
|
165
|
+
expect(combobox['_value']).toEqual(['bfr'])
|
|
166
|
+
})
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
describe('Form reset', () => {
|
|
170
|
+
const mountInForm = async (attrs: string, options: IPktComboboxOption[]) => {
|
|
171
|
+
const container = document.createElement('div')
|
|
172
|
+
container.innerHTML = `<form><pkt-combobox ${attrs}></pkt-combobox></form>`
|
|
173
|
+
document.body.appendChild(container)
|
|
174
|
+
await customElements.whenDefined('pkt-combobox')
|
|
175
|
+
const combobox = container.querySelector('pkt-combobox') as PktCombobox
|
|
176
|
+
combobox.options = options.map((o) => ({ ...o }))
|
|
177
|
+
await combobox.updateComplete
|
|
178
|
+
const form = container.querySelector('form') as HTMLFormElement
|
|
179
|
+
return { combobox, form }
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
test('clears a selected option', async () => {
|
|
183
|
+
const { combobox, form } = await mountInForm('id="t" name="t" label="L" multiple', bydeler)
|
|
184
|
+
combobox.toggleValue('bfr')
|
|
185
|
+
await combobox.updateComplete
|
|
186
|
+
|
|
187
|
+
form.reset()
|
|
188
|
+
await combobox.updateComplete
|
|
189
|
+
|
|
190
|
+
expect(combobox['_value']).toEqual([])
|
|
191
|
+
expect(tagLabels(combobox)).toHaveLength(0)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
test('clears a user-added value', async () => {
|
|
195
|
+
const { combobox, form } = await mountInForm(
|
|
196
|
+
'id="t" name="t" label="L" multiple allow-user-input',
|
|
197
|
+
bydeler,
|
|
198
|
+
)
|
|
199
|
+
combobox.toggleValue('egendefinert')
|
|
200
|
+
await combobox.updateComplete
|
|
201
|
+
expect(combobox['_value']).toEqual(['egendefinert'])
|
|
202
|
+
|
|
203
|
+
form.reset()
|
|
204
|
+
await combobox.updateComplete
|
|
205
|
+
|
|
206
|
+
expect(combobox['_value']).toEqual([])
|
|
207
|
+
expect(tagLabels(combobox)).toHaveLength(0)
|
|
208
|
+
expect(combobox.options.some((option) => option.userAdded)).toBe(false)
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('clears a user-added value alongside a selected option', async () => {
|
|
212
|
+
const { combobox, form } = await mountInForm(
|
|
213
|
+
'id="t" name="t" label="L" multiple allow-user-input',
|
|
214
|
+
bydeler,
|
|
215
|
+
)
|
|
216
|
+
combobox.toggleValue('bfr')
|
|
217
|
+
await combobox.updateComplete
|
|
218
|
+
combobox.toggleValue('egendefinert')
|
|
219
|
+
await combobox.updateComplete
|
|
220
|
+
expect(combobox['_value']).toEqual(['bfr', 'egendefinert'])
|
|
221
|
+
|
|
222
|
+
form.reset()
|
|
223
|
+
await combobox.updateComplete
|
|
224
|
+
|
|
225
|
+
expect(combobox['_value']).toEqual([])
|
|
226
|
+
expect(selectedInList(combobox)).toEqual([])
|
|
227
|
+
})
|
|
228
|
+
})
|
|
229
|
+
|
|
230
|
+
describe('Selection state after picking an option', () => {
|
|
231
|
+
test('marks the option in the listbox on the first click', async () => {
|
|
232
|
+
const combobox = await createCombobox(
|
|
233
|
+
'id="t" name="t" label="L" multiple maxlength="3" include-search',
|
|
234
|
+
bydeler,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
combobox.toggleValue('bfr')
|
|
238
|
+
await combobox.updateComplete
|
|
239
|
+
|
|
240
|
+
expect(combobox['_value']).toEqual(['bfr'])
|
|
241
|
+
expect(selectedInList(combobox)).toEqual(['bfr'])
|
|
242
|
+
expect(tagLabels(combobox)).toHaveLength(1)
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
test('keeps value and listbox in sync across repeated toggles', async () => {
|
|
246
|
+
const combobox = await createCombobox('id="t" name="t" label="L" multiple', bydeler)
|
|
247
|
+
|
|
248
|
+
for (const value of ['bfr', 'bga', 'bfr', 'bsa']) {
|
|
249
|
+
combobox.toggleValue(value)
|
|
250
|
+
await combobox.updateComplete
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
expect(combobox['_value']).toEqual(['bga', 'bsa'])
|
|
254
|
+
expect(selectedInList(combobox)).toEqual(['bga', 'bsa'])
|
|
255
|
+
expect(tagLabels(combobox)).toHaveLength(2)
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
test('keeps selection when all options are preselected', async () => {
|
|
259
|
+
const combobox = await createCombobox(
|
|
260
|
+
'id="t" name="t" label="L" multiple value="bfr,bga,bsa,bgo"',
|
|
261
|
+
bydeler,
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
combobox.toggleValue('bfr')
|
|
265
|
+
await combobox.updateComplete
|
|
266
|
+
|
|
267
|
+
expect(combobox['_value']).toEqual(['bga', 'bsa', 'bgo'])
|
|
268
|
+
expect(selectedInList(combobox)).toEqual(['bga', 'bsa', 'bgo'])
|
|
269
|
+
})
|
|
270
|
+
})
|
|
271
|
+
})
|