@oslokommune/punkt-elements 15.4.5 → 16.0.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.
Files changed (73) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/dist/{card-CnPjrdre.js → card-CmfUyl_s.js} +1 -1
  3. package/dist/{card-5S2r9UD1.cjs → card-Db9QSEqh.cjs} +1 -1
  4. package/dist/{checkbox-D98_NjcU.cjs → checkbox-Cpyay9_l.cjs} +1 -1
  5. package/dist/{checkbox-BSz71IeT.js → checkbox-D6nltMuc.js} +1 -1
  6. package/dist/combobox-Bv37b6cI.cjs +135 -0
  7. package/dist/combobox-CoO8T-F-.js +818 -0
  8. package/dist/{datepicker-SEKblnRR.cjs → datepicker-CrvQ5Y5w.cjs} +1 -1
  9. package/dist/{datepicker-nnyTW0vf.js → datepicker-DbsIuC5Z.js} +2 -2
  10. package/dist/index.d.ts +157 -90
  11. package/dist/{input-element-Bkv6Yxld.js → input-element-BGNbdzy2.js} +1 -1
  12. package/dist/{input-element-DM0tY799.cjs → input-element-CSDVA3Y6.cjs} +1 -1
  13. package/dist/listbox-Dm2mKp6_.cjs +101 -0
  14. package/dist/listbox-OdkIn9_A.js +431 -0
  15. package/dist/pkt-card.cjs +1 -1
  16. package/dist/pkt-card.js +1 -1
  17. package/dist/pkt-checkbox.cjs +1 -1
  18. package/dist/pkt-checkbox.js +1 -1
  19. package/dist/pkt-combobox.cjs +1 -1
  20. package/dist/pkt-combobox.js +1 -1
  21. package/dist/pkt-datepicker.cjs +1 -1
  22. package/dist/pkt-datepicker.js +2 -2
  23. package/dist/pkt-header.cjs +1 -1
  24. package/dist/pkt-header.js +1 -1
  25. package/dist/pkt-index.cjs +1 -1
  26. package/dist/pkt-index.js +9 -9
  27. package/dist/pkt-listbox.cjs +1 -1
  28. package/dist/pkt-listbox.js +1 -1
  29. package/dist/pkt-options-controller-BogGk-6J.cjs +1 -0
  30. package/dist/{pkt-options-controller-BcGywCmf.js → pkt-options-controller-Z-bPox7n.js} +2 -2
  31. package/dist/pkt-radiobutton.cjs +1 -1
  32. package/dist/pkt-radiobutton.js +1 -1
  33. package/dist/pkt-select.cjs +1 -1
  34. package/dist/pkt-select.js +1 -1
  35. package/dist/pkt-tag.cjs +1 -1
  36. package/dist/pkt-tag.js +1 -1
  37. package/dist/pkt-textarea.cjs +1 -1
  38. package/dist/pkt-textarea.js +1 -1
  39. package/dist/pkt-textinput.cjs +1 -1
  40. package/dist/pkt-textinput.js +1 -1
  41. package/dist/{radiobutton-95wp024h.cjs → radiobutton-CNHCpKn0.cjs} +1 -1
  42. package/dist/{radiobutton-CTFAV5GU.js → radiobutton-DgC27mb0.js} +1 -1
  43. package/dist/{select-YLvYAQX6.js → select-7VuYtPZv.js} +2 -2
  44. package/dist/{select-CZ_Lx5W6.cjs → select-PWPy5gTB.cjs} +1 -1
  45. package/dist/{tag-68q0_Sn0.js → tag-DZPqFiem.js} +37 -33
  46. package/dist/tag-DmbgBCKu.cjs +27 -0
  47. package/dist/{textarea-CuTsE1WX.cjs → textarea-CO7Ikug5.cjs} +1 -1
  48. package/dist/{textarea-DhWH99qN.js → textarea-VpCEjVFx.js} +1 -1
  49. package/dist/{textinput-BCi9p0Du.js → textinput-C2AZ9ss2.js} +1 -1
  50. package/dist/{textinput-st4Vml5J.cjs → textinput-DRFZU3dA.cjs} +1 -1
  51. package/package.json +4 -4
  52. package/src/components/card/card.ts +1 -0
  53. package/src/components/combobox/combobox-base.ts +158 -0
  54. package/src/components/combobox/combobox-handlers.ts +419 -0
  55. package/src/components/combobox/combobox-types.ts +10 -0
  56. package/src/components/combobox/combobox-utils.ts +135 -0
  57. package/src/components/combobox/combobox-value.ts +248 -0
  58. package/src/components/combobox/combobox.accessibility.test.ts +243 -0
  59. package/src/components/combobox/{combobox.test.ts → combobox.core.test.ts} +104 -46
  60. package/src/components/combobox/combobox.interaction.test.ts +436 -0
  61. package/src/components/combobox/combobox.selection.test.ts +543 -0
  62. package/src/components/combobox/combobox.ts +260 -734
  63. package/src/components/listbox/index.ts +2 -0
  64. package/src/components/listbox/listbox.interaction.test.ts +580 -0
  65. package/src/components/listbox/listbox.test.ts +32 -6
  66. package/src/components/listbox/listbox.ts +109 -126
  67. package/src/components/tag/tag.ts +3 -0
  68. package/dist/combobox-C5YcNVSZ.cjs +0 -128
  69. package/dist/combobox-cer7PLSE.js +0 -533
  70. package/dist/listbox-C7NEa9SU.cjs +0 -96
  71. package/dist/listbox-Cykec1bj.js +0 -361
  72. package/dist/pkt-options-controller-BnTmkl3g.cjs +0 -1
  73. package/dist/tag-BnT5onW2.cjs +0 -26
@@ -67,31 +67,31 @@ describe('PktCombobox', () => {
67
67
  const wrapper = combobox.querySelector('pkt-input-wrapper')
68
68
  const comboboxDiv = wrapper?.querySelector('.pkt-combobox')
69
69
  const inputDiv = comboboxDiv?.querySelector('.pkt-combobox__input')
70
- const arrowButton = inputDiv?.querySelector('.pkt-combobox__arrow')
70
+ const arrowIcon = inputDiv?.querySelector('.pkt-combobox__arrow-icon')
71
71
  const listbox = comboboxDiv?.querySelector('pkt-listbox')
72
72
 
73
73
  expect(wrapper).toBeInTheDocument()
74
74
  expect(comboboxDiv).toBeInTheDocument()
75
75
  expect(inputDiv).toBeInTheDocument()
76
- expect(arrowButton).toBeInTheDocument()
76
+ expect(arrowIcon).toBeInTheDocument()
77
77
  expect(listbox).toBeInTheDocument()
78
78
  })
79
79
 
80
- test('renders arrow button with correct attributes', async () => {
80
+ test('renders select-only combobox with correct ARIA attributes', async () => {
81
81
  const container = await createCombobox('id="test-arrow" name="test" label="Test"')
82
82
 
83
83
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
84
84
  await combobox.updateComplete
85
85
 
86
- const arrowButton = combobox.querySelector('.pkt-combobox__arrow')
87
- const icon = arrowButton?.querySelector('pkt-icon')
86
+ const comboboxInput = combobox.querySelector('.pkt-combobox__input')
87
+ const icon = comboboxInput?.querySelector('pkt-icon')
88
88
 
89
- expect(arrowButton?.getAttribute('id')).toBe('test-arrow-arrow')
90
- expect(arrowButton?.getAttribute('aria-expanded')).toBe('false')
91
- expect(arrowButton?.getAttribute('aria-controls')).toBe('test-arrow-listbox')
92
- expect(arrowButton?.getAttribute('aria-haspopup')).toBe('listbox')
93
- expect(arrowButton?.getAttribute('aria-label')).toBe('Åpne liste')
94
- expect(arrowButton?.getAttribute('role')).toBe('button')
89
+ expect(comboboxInput?.getAttribute('id')).toBe('test-arrow-combobox')
90
+ expect(comboboxInput?.getAttribute('aria-expanded')).toBe('false')
91
+ expect(comboboxInput?.getAttribute('aria-controls')).toBe('test-arrow-listbox')
92
+ expect(comboboxInput?.getAttribute('aria-haspopup')).toBe('listbox')
93
+ expect(comboboxInput?.getAttribute('aria-labelledby')).toBe('test-arrow-combobox-label')
94
+ expect(comboboxInput?.getAttribute('role')).toBe('combobox')
95
95
  expect(icon?.getAttribute('name')).toBe('chevron-thin-down')
96
96
  })
97
97
  })
@@ -103,7 +103,7 @@ describe('PktCombobox', () => {
103
103
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
104
104
  await combobox.updateComplete
105
105
 
106
- expect(combobox.value).toBe(undefined)
106
+ expect(combobox.value).toBe('')
107
107
  expect(combobox.allowUserInput).toBe(false)
108
108
  expect(combobox.typeahead).toBe(false)
109
109
  expect(combobox.includeSearch).toBe(false)
@@ -124,11 +124,11 @@ describe('PktCombobox', () => {
124
124
  expect(combobox.multiple).toBe(true)
125
125
 
126
126
  const listbox = combobox.querySelector('pkt-listbox')
127
- expect(listbox?.hasAttribute('ismultiselect')).toBe(true)
127
+ expect(listbox?.hasAttribute('is-multi-select')).toBe(true)
128
128
  })
129
129
 
130
130
  test('handles allowUserInput property correctly', async () => {
131
- const container = await createCombobox('id="test" name="test" allowUserInput')
131
+ const container = await createCombobox('id="test" name="test" allow-user-input')
132
132
 
133
133
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
134
134
  await combobox.updateComplete
@@ -154,7 +154,7 @@ describe('PktCombobox', () => {
154
154
  })
155
155
 
156
156
  test('handles includeSearch property correctly', async () => {
157
- const container = await createCombobox('id="test" name="test" includeSearch')
157
+ const container = await createCombobox('id="test" name="test" include-search')
158
158
 
159
159
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
160
160
  await combobox.updateComplete
@@ -162,7 +162,7 @@ describe('PktCombobox', () => {
162
162
  expect(combobox.includeSearch).toBe(true)
163
163
 
164
164
  const listbox = combobox.querySelector('pkt-listbox')
165
- expect(listbox?.hasAttribute('includesearch')).toBe(true)
165
+ expect(listbox?.hasAttribute('include-search')).toBe(true)
166
166
  })
167
167
 
168
168
  test('handles disabled property correctly', async () => {
@@ -173,11 +173,9 @@ describe('PktCombobox', () => {
173
173
 
174
174
  expect(combobox.disabled).toBe(true)
175
175
 
176
- const inputDiv = combobox.querySelector('.pkt-combobox__input')
177
- const arrowButton = combobox.querySelector('.pkt-combobox__arrow')
178
- expect(inputDiv).toHaveClass('pkt-combobox__input--disabled')
179
- expect(arrowButton?.getAttribute('tabindex')).toBe('-1')
180
- expect(arrowButton?.hasAttribute('disabled')).toBe(true)
176
+ const comboboxInput = combobox.querySelector('.pkt-combobox__input')
177
+ expect(comboboxInput).toHaveClass('pkt-combobox__input--disabled')
178
+ expect(comboboxInput?.getAttribute('tabindex')).toBe('-1')
181
179
  })
182
180
 
183
181
  test('handles fullwidth property correctly', async () => {
@@ -256,7 +254,7 @@ describe('PktCombobox', () => {
256
254
  })
257
255
 
258
256
  test('preserves userAdded options when defaultOptions change', async () => {
259
- const container = await createCombobox('id="test" name="test" allowUserInput')
257
+ const container = await createCombobox('id="test" name="test" allow-user-input')
260
258
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
261
259
  await combobox.updateComplete
262
260
 
@@ -281,7 +279,7 @@ describe('PktCombobox', () => {
281
279
  })
282
280
 
283
281
  test('preserves userAdded option when parent replaces options by overwriting options property', async () => {
284
- const container = await createCombobox('id="test" name="test" allowUserInput')
282
+ const container = await createCombobox('id="test" name="test" allow-user-input')
285
283
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
286
284
  await combobox.updateComplete
287
285
  ;(combobox as any).addNewUserValue('userAdded')
@@ -322,7 +320,7 @@ describe('PktCombobox', () => {
322
320
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
323
321
  await combobox.updateComplete
324
322
 
325
- expect(combobox.value).toEqual(['value1', 'value2'])
323
+ expect(combobox.value).toBe('value1,value2')
326
324
  expect(combobox['_value']).toEqual(['value1', 'value2'])
327
325
  })
328
326
 
@@ -378,7 +376,7 @@ describe('PktCombobox', () => {
378
376
 
379
377
  test('shows placeholder in multiple mode with outside tag placement', async () => {
380
378
  const container = await createCombobox(
381
- 'id="test" name="test" placeholder="Select options" multiple tagPlacement="outside"',
379
+ 'id="test" name="test" placeholder="Select options" multiple tag-placement="outside"',
382
380
  )
383
381
 
384
382
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
@@ -407,7 +405,7 @@ describe('PktCombobox', () => {
407
405
  const testOptions: IPktComboboxOption[] = [{ value: 'option1', label: 'Option 1' }]
408
406
 
409
407
  const container = await createCombobox(
410
- 'id="test" name="test" multiple tagPlacement="outside" value="option1"',
408
+ 'id="test" name="test" multiple tag-placement="outside" value="option1"',
411
409
  )
412
410
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
413
411
  combobox.options = testOptions
@@ -435,7 +433,7 @@ describe('PktCombobox', () => {
435
433
  })
436
434
 
437
435
  test('renders text input when allowUserInput is true', async () => {
438
- const container = await createCombobox('id="test" name="test-name" allowUserInput')
436
+ const container = await createCombobox('id="test" name="test-name" allow-user-input')
439
437
 
440
438
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
441
439
  await combobox.updateComplete
@@ -463,7 +461,7 @@ describe('PktCombobox', () => {
463
461
  })
464
462
 
465
463
  test('sets correct aria-autocomplete for allowUserInput', async () => {
466
- const container = await createCombobox('id="test" name="test" allowUserInput')
464
+ const container = await createCombobox('id="test" name="test" allow-user-input')
467
465
 
468
466
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
469
467
  await combobox.updateComplete
@@ -480,7 +478,7 @@ describe('PktCombobox', () => {
480
478
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
481
479
  await combobox.updateComplete
482
480
 
483
- const arrowButton = combobox.querySelector('.pkt-combobox__arrow')
481
+ const arrowButton = combobox.querySelector('.pkt-combobox__input')
484
482
  const inputDiv = combobox.querySelector('.pkt-combobox__input')
485
483
 
486
484
  expect(combobox['_isOptionsOpen']).toBe(false)
@@ -502,7 +500,7 @@ describe('PktCombobox', () => {
502
500
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
503
501
  await combobox.updateComplete
504
502
 
505
- const arrowButton = combobox.querySelector('.pkt-combobox__arrow')
503
+ const arrowButton = combobox.querySelector('.pkt-combobox__input')
506
504
 
507
505
  // Click to open
508
506
  fireEvent.click(arrowButton!)
@@ -521,7 +519,7 @@ describe('PktCombobox', () => {
521
519
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
522
520
  await combobox.updateComplete
523
521
 
524
- const arrowButton = combobox.querySelector('.pkt-combobox__arrow')
522
+ const arrowButton = combobox.querySelector('.pkt-combobox__input')
525
523
 
526
524
  // Try to click when disabled
527
525
  fireEvent.click(arrowButton!)
@@ -533,18 +531,18 @@ describe('PktCombobox', () => {
533
531
 
534
532
  describe('Search functionality', () => {
535
533
  test('handles search input when includeSearch is true', async () => {
536
- const container = await createCombobox('id="test" name="test" includeSearch')
534
+ const container = await createCombobox('id="test" name="test" include-search')
537
535
 
538
536
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
539
537
  await combobox.updateComplete
540
538
 
541
539
  const listbox = combobox.querySelector('pkt-listbox')
542
- expect(listbox?.hasAttribute('includesearch')).toBe(true)
540
+ expect(listbox?.hasAttribute('include-search')).toBe(true)
543
541
  })
544
542
 
545
543
  test('sets search placeholder correctly', async () => {
546
544
  const container = await createCombobox(
547
- 'id="test" name="test" includeSearch searchPlaceholder="Search items..."',
545
+ 'id="test" name="test" include-search search-placeholder="Search items..."',
548
546
  )
549
547
 
550
548
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
@@ -564,7 +562,7 @@ describe('PktCombobox', () => {
564
562
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
565
563
  await combobox.updateComplete
566
564
 
567
- const arrowButton = combobox.querySelector('.pkt-combobox__arrow')
565
+ const arrowButton = combobox.querySelector('.pkt-combobox__input')
568
566
 
569
567
  // Test Enter key
570
568
  fireEvent.keyDown(arrowButton!, { key: 'Enter' })
@@ -580,7 +578,7 @@ describe('PktCombobox', () => {
580
578
  })
581
579
 
582
580
  test('handles input focus for text inputs', async () => {
583
- const container = await createCombobox('id="test" name="test" allowUserInput')
581
+ const container = await createCombobox('id="test" name="test" allow-user-input')
584
582
 
585
583
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
586
584
  await combobox.updateComplete
@@ -621,7 +619,7 @@ describe('PktCombobox', () => {
621
619
  ]
622
620
 
623
621
  const container = await createCombobox(
624
- 'id="test" name="test" multiple allowUserInput maxlength="2"',
622
+ 'id="test" name="test" multiple allow-user-input maxlength="2"',
625
623
  )
626
624
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
627
625
  combobox.options = testOptions
@@ -672,22 +670,23 @@ describe('PktCombobox', () => {
672
670
  expect(results).toHaveNoViolations()
673
671
  })
674
672
 
675
- test('sets correct ARIA attributes on arrow button', async () => {
673
+ test('sets correct ARIA attributes on select-only combobox', async () => {
676
674
  const container = await createCombobox('id="test-aria" name="test" label="Test"')
677
675
 
678
676
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
679
677
  await combobox.updateComplete
680
678
 
681
- const arrowButton = combobox.querySelector('.pkt-combobox__arrow')
682
- expect(arrowButton?.getAttribute('aria-controls')).toBe('test-aria-listbox')
683
- expect(arrowButton?.getAttribute('aria-haspopup')).toBe('listbox')
684
- expect(arrowButton?.getAttribute('aria-expanded')).toBe('false')
685
- expect(arrowButton?.getAttribute('aria-label')).toBe('Åpne liste')
679
+ const comboboxInput = combobox.querySelector('.pkt-combobox__input')
680
+ expect(comboboxInput?.getAttribute('role')).toBe('combobox')
681
+ expect(comboboxInput?.getAttribute('aria-controls')).toBe('test-aria-listbox')
682
+ expect(comboboxInput?.getAttribute('aria-haspopup')).toBe('listbox')
683
+ expect(comboboxInput?.getAttribute('aria-expanded')).toBe('false')
684
+ expect(comboboxInput?.getAttribute('aria-labelledby')).toBe('test-aria-combobox-label')
686
685
  })
687
686
 
688
687
  test('sets correct ARIA attributes on text input', async () => {
689
688
  const container = await createCombobox(
690
- 'id="test-input" name="test" label="Test Label" allowUserInput',
689
+ 'id="test-input" name="test" label="Test Label" allow-user-input',
691
690
  )
692
691
 
693
692
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
@@ -703,7 +702,9 @@ describe('PktCombobox', () => {
703
702
  test('sets correct aria-activedescendant when value is selected', async () => {
704
703
  const testOptions: IPktComboboxOption[] = [{ value: 'option1', label: 'Option 1' }]
705
704
 
706
- const container = await createCombobox('id="test" name="test" allowUserInput value="option1"')
705
+ const container = await createCombobox(
706
+ 'id="test" name="test" allow-user-input value="option1"',
707
+ )
707
708
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
708
709
  combobox.options = testOptions
709
710
  await combobox.updateComplete
@@ -715,7 +716,7 @@ describe('PktCombobox', () => {
715
716
 
716
717
  describe('Event handling', () => {
717
718
  test('dispatches search event when search value changes', async () => {
718
- const container = await createCombobox('id="test" name="test" includeSearch')
719
+ const container = await createCombobox('id="test" name="test" include-search')
719
720
  const combobox = container.querySelector('pkt-combobox') as PktCombobox
720
721
  await combobox.updateComplete
721
722
 
@@ -747,4 +748,61 @@ describe('PktCombobox', () => {
747
748
  expect(combobox['_isOptionsOpen']).toBe(false)
748
749
  })
749
750
  })
751
+
752
+ describe('Dynamic slot options', () => {
753
+ test('picks up dynamically added <option> children', async () => {
754
+ const container = await createCombobox(
755
+ 'id="dynamic" name="dynamic"',
756
+ createOptions([
757
+ { value: 'a', label: 'A' },
758
+ { value: 'b', label: 'B' },
759
+ ]),
760
+ )
761
+ const combobox = container.querySelector('pkt-combobox') as PktCombobox
762
+ await combobox.updateComplete
763
+
764
+ expect(combobox['_options']).toHaveLength(2)
765
+
766
+ // Add a new option element to the DOM
767
+ const newOption = document.createElement('option')
768
+ newOption.value = 'c'
769
+ newOption.textContent = 'C'
770
+ combobox.appendChild(newOption)
771
+
772
+ // Wait for MutationObserver + Lit update
773
+ await combobox.updateComplete
774
+ await new Promise((r) => setTimeout(r, 0))
775
+ await combobox.updateComplete
776
+
777
+ expect(combobox['_options']).toHaveLength(3)
778
+ expect(combobox['_options'].some((o: IPktComboboxOption) => o.value === 'c')).toBe(true)
779
+ })
780
+
781
+ test('picks up dynamically removed <option> children', async () => {
782
+ const container = await createCombobox(
783
+ 'id="dynamic" name="dynamic"',
784
+ createOptions([
785
+ { value: 'a', label: 'A' },
786
+ { value: 'b', label: 'B' },
787
+ { value: 'c', label: 'C' },
788
+ ]),
789
+ )
790
+ const combobox = container.querySelector('pkt-combobox') as PktCombobox
791
+ await combobox.updateComplete
792
+
793
+ expect(combobox['_options']).toHaveLength(3)
794
+
795
+ // Remove the last option element
796
+ const options = combobox.querySelectorAll('option')
797
+ combobox.removeChild(options[options.length - 1])
798
+
799
+ // Wait for MutationObserver + Lit update
800
+ await combobox.updateComplete
801
+ await new Promise((r) => setTimeout(r, 0))
802
+ await combobox.updateComplete
803
+
804
+ expect(combobox['_options']).toHaveLength(2)
805
+ expect(combobox['_options'].some((o: IPktComboboxOption) => o.value === 'c')).toBe(false)
806
+ })
807
+ })
750
808
  })