@oslokommune/punkt-elements 13.4.2 → 13.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/dist/calendar-32W9p9uc.cjs +115 -0
  3. package/dist/{calendar-DevQhOup.js → calendar-CJSxvwAq.js} +353 -340
  4. package/dist/{card-Dtw26f7i.js → card-BDz4RWxK.js} +1 -1
  5. package/dist/{card-BUITGoqX.cjs → card-DBlFf1ry.cjs} +1 -1
  6. package/dist/{datepicker-CYOn3tRm.js → datepicker-BJKJBoy_.js} +102 -59
  7. package/dist/datepicker-CmTrG5GE.cjs +164 -0
  8. package/dist/{heading-D6jXE_Mz.js → heading-Bdh9absf.js} +22 -22
  9. package/dist/heading-CNycsyMj.cjs +1 -0
  10. package/dist/index.d.ts +6 -2
  11. package/dist/pkt-calendar.cjs +1 -1
  12. package/dist/pkt-calendar.js +1 -1
  13. package/dist/pkt-card.cjs +1 -1
  14. package/dist/pkt-card.js +1 -1
  15. package/dist/pkt-datepicker.cjs +1 -1
  16. package/dist/pkt-datepicker.js +1 -1
  17. package/dist/pkt-heading.cjs +1 -1
  18. package/dist/pkt-heading.js +1 -1
  19. package/dist/pkt-index.cjs +1 -1
  20. package/dist/pkt-index.js +5 -5
  21. package/package.json +3 -3
  22. package/src/components/calendar/calendar.accessibility.test.ts +111 -0
  23. package/src/components/calendar/calendar.constraints.test.ts +110 -0
  24. package/src/components/calendar/calendar.core.test.ts +367 -0
  25. package/src/components/calendar/calendar.interaction.test.ts +139 -0
  26. package/src/components/calendar/calendar.selection.test.ts +273 -0
  27. package/src/components/calendar/calendar.ts +74 -42
  28. package/src/components/card/card.test.ts +19 -5
  29. package/src/components/consent/consent.test.ts +436 -0
  30. package/src/components/datepicker/datepicker.accessibility.test.ts +193 -0
  31. package/src/components/datepicker/datepicker.core.test.ts +322 -0
  32. package/src/components/datepicker/datepicker.input.test.ts +268 -0
  33. package/src/components/datepicker/datepicker.selection.test.ts +286 -0
  34. package/src/components/datepicker/datepicker.ts +121 -19
  35. package/src/components/datepicker/datepicker.validation.test.ts +176 -0
  36. package/src/components/heading/heading.test.ts +458 -0
  37. package/src/components/heading/heading.ts +3 -0
  38. package/src/components/helptext/helptext.test.ts +474 -0
  39. package/dist/calendar-BZe2D4Sr.cjs +0 -108
  40. package/dist/datepicker-B9rhz_AF.cjs +0 -154
  41. package/dist/heading-BRE_iFtR.cjs +0 -1
@@ -0,0 +1,322 @@
1
+ import '@testing-library/jest-dom'
2
+ import { toHaveNoViolations } from 'jest-axe'
3
+ import { fireEvent } from '@testing-library/dom'
4
+ import { parseISODateString } from '@/utils/dateutils'
5
+
6
+ expect.extend(toHaveNoViolations)
7
+
8
+ import './datepicker'
9
+ import '../calendar/calendar'
10
+ import { PktDatepicker } from './datepicker'
11
+ import { PktCalendar } from '../calendar/calendar'
12
+
13
+ const waitForCustomElements = async () => {
14
+ await customElements.whenDefined('pkt-datepicker')
15
+ await customElements.whenDefined('pkt-calendar')
16
+ }
17
+
18
+ // Helper function to create datepicker markup
19
+ const createDatepicker = async (datepickerProps = '') => {
20
+ const container = document.createElement('div')
21
+ container.innerHTML = `
22
+ <pkt-datepicker ${datepickerProps}></pkt-datepicker>
23
+ `
24
+ document.body.appendChild(container)
25
+ await waitForCustomElements()
26
+ return container
27
+ }
28
+
29
+ // Cleanup after each test
30
+ afterEach(() => {
31
+ document.body.innerHTML = ''
32
+ })
33
+
34
+ describe('PktDatepicker', () => {
35
+ describe('Rendering and basic functionality', () => {
36
+ test('renders without errors', async () => {
37
+ const container = await createDatepicker()
38
+
39
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
40
+ expect(datepicker).toBeInTheDocument()
41
+
42
+ await datepicker.updateComplete
43
+ expect(datepicker).toBeTruthy()
44
+ })
45
+
46
+ test('renders with correct structure', async () => {
47
+ const container = await createDatepicker('label="Test Datepicker"')
48
+
49
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
50
+ await datepicker.updateComplete
51
+
52
+ const inputWrapper = datepicker.querySelector('pkt-input-wrapper')
53
+ const input = datepicker.querySelector('input')
54
+ const calendarButton = datepicker.querySelector('button[type="button"]')
55
+
56
+ expect(inputWrapper).toBeInTheDocument()
57
+ expect(input).toBeInTheDocument()
58
+ expect(calendarButton).toBeInTheDocument()
59
+ })
60
+
61
+ test('renders calendar when opened', async () => {
62
+ const container = await createDatepicker('label="Test Datepicker"')
63
+
64
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
65
+ await datepicker.updateComplete
66
+
67
+ const calendarButton = datepicker.querySelector('button[type="button"]')
68
+ fireEvent.click(calendarButton!)
69
+ await datepicker.updateComplete
70
+
71
+ const calendar = datepicker.querySelector('pkt-calendar')
72
+ expect(calendar).toBeInTheDocument()
73
+ })
74
+
75
+ test('closes calendar when clicking outside', async () => {
76
+ const container = await createDatepicker('label="Test Datepicker"')
77
+
78
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
79
+ await datepicker.updateComplete
80
+
81
+ // Open calendar
82
+ const calendarButton = datepicker.querySelector('button[type="button"]')
83
+ fireEvent.click(calendarButton!)
84
+ await datepicker.updateComplete
85
+
86
+ // Click outside
87
+ fireEvent.click(document.body)
88
+ await datepicker.updateComplete
89
+
90
+ expect(datepicker.calendarOpen).toBe(false)
91
+ })
92
+
93
+ test('closes calendar when clicking outside', async () => {
94
+ const container = await createDatepicker('label="Test Datepicker"')
95
+
96
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
97
+ await datepicker.updateComplete
98
+
99
+ // Open calendar
100
+ const calendarButton = datepicker.querySelector('button[type="button"]')
101
+ fireEvent.click(calendarButton!)
102
+ await datepicker.updateComplete
103
+
104
+ // Click outside
105
+ fireEvent.click(document.body)
106
+ await datepicker.updateComplete
107
+
108
+ expect(datepicker.calendarOpen).toBe(false)
109
+ })
110
+ })
111
+
112
+ describe('Properties and attributes', () => {
113
+ test('applies default properties correctly', async () => {
114
+ const container = await createDatepicker('name="test" id="test"')
115
+
116
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
117
+ await datepicker.updateComplete
118
+
119
+ expect(datepicker.value).toBe('')
120
+ expect(datepicker.label).toBe('Datovelger')
121
+ expect(datepicker.multiple).toBe(false)
122
+ expect(datepicker.range).toBe(false)
123
+ expect(datepicker.maxlength).toBe(null)
124
+ expect(datepicker.showRangeLabels).toBe(false)
125
+ })
126
+
127
+ test('handles value property correctly', async () => {
128
+ const testDate = '2024-06-15'
129
+ const container = await createDatepicker(`value="${testDate}"`)
130
+
131
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
132
+ await datepicker.updateComplete
133
+
134
+ expect(datepicker.value).toBe(testDate)
135
+
136
+ const input = datepicker.querySelector('input') as HTMLInputElement
137
+ expect(input.value).toBeTruthy()
138
+ })
139
+
140
+ test('handles multiple values correctly', async () => {
141
+ const testDates = '2024-06-15,2024-06-20,2024-06-25'
142
+ const container = await createDatepicker(`value="${testDates}" multiple`)
143
+
144
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
145
+ await datepicker.updateComplete
146
+
147
+ expect(datepicker.value).toBe(testDates)
148
+ expect(datepicker.multiple).toBe(true)
149
+
150
+ // Should show tags for multiple values
151
+ const tags = datepicker.querySelectorAll('pkt-tag')
152
+ expect(tags.length).toBe(3)
153
+ })
154
+
155
+ test('handles range values correctly', async () => {
156
+ const rangeValue = '2024-06-15,2024-06-20'
157
+ const container = await createDatepicker(`value="${rangeValue}" range`)
158
+
159
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
160
+ await datepicker.updateComplete
161
+
162
+ expect(datepicker.value).toBe(rangeValue)
163
+ expect(datepicker.range).toBe(true)
164
+ })
165
+
166
+ test('handles label property correctly', async () => {
167
+ const testLabel = 'Select your date'
168
+ const container = await createDatepicker(`label="${testLabel}"`)
169
+
170
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
171
+ await datepicker.updateComplete
172
+
173
+ expect(datepicker.label).toBe(testLabel)
174
+
175
+ const label = datepicker.querySelector('label')
176
+ expect(label?.textContent).toContain(testLabel)
177
+ })
178
+
179
+ test('handles dateformat property correctly', async () => {
180
+ const customFormat = 'MM/dd/yyyy'
181
+ const container = await createDatepicker(
182
+ `dateformat="${customFormat}" value="2024-06-15" multiple`,
183
+ )
184
+
185
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
186
+ await datepicker.updateComplete
187
+
188
+ expect(datepicker.dateformat).toBe(customFormat)
189
+
190
+ // Multiple mode input should be empty (for new input)
191
+ const input = datepicker.querySelector('input') as HTMLInputElement
192
+ expect(input.value).toBe('')
193
+
194
+ // Selected dates should show in tags with custom format
195
+ const tag = datepicker.querySelector('pkt-tag time')
196
+ expect(tag).toBeInTheDocument()
197
+ if (tag) {
198
+ expect(tag.textContent).toMatch(/\d{2}\/\d{2}\/\d{4}/)
199
+ }
200
+ })
201
+
202
+ test('handles maxlength property correctly', async () => {
203
+ const container = await createDatepicker('multiple maxlength="3"')
204
+
205
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
206
+ await datepicker.updateComplete
207
+
208
+ expect(datepicker.maxlength).toBe(3)
209
+ })
210
+
211
+ test('handles disabled property correctly', async () => {
212
+ const container = await createDatepicker('disabled')
213
+
214
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
215
+ await datepicker.updateComplete
216
+
217
+ expect(datepicker.disabled).toBe(true)
218
+
219
+ const input = datepicker.querySelector('input') as HTMLInputElement
220
+ const calendarButton = datepicker.querySelector('button[type="button"]') as HTMLButtonElement
221
+
222
+ expect(input.disabled).toBe(true)
223
+ expect(calendarButton.disabled).toBe(true)
224
+ })
225
+
226
+ test('handles currentmonth property correctly', async () => {
227
+ const testMonth = '2024-03-01'
228
+ const container = await createDatepicker(`currentmonth="${testMonth}"`)
229
+
230
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
231
+ await datepicker.updateComplete
232
+
233
+ // Open calendar to see the month
234
+ const calendarButton = datepicker.querySelector('button[type="button"]')
235
+ fireEvent.click(calendarButton!)
236
+ await datepicker.updateComplete
237
+
238
+ const calendar = datepicker.querySelector('pkt-calendar') as PktCalendar
239
+ expect(calendar.currentmonth).toEqual(parseISODateString(testMonth))
240
+ })
241
+ })
242
+
243
+ describe('Calendar integration', () => {
244
+ test('passes properties to calendar correctly', async () => {
245
+ const container = await createDatepicker(
246
+ 'min="2024-06-01" max="2024-06-30" excludeweekdays="0,6"',
247
+ )
248
+
249
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
250
+ await datepicker.updateComplete
251
+
252
+ // Open calendar
253
+ const calendarButton = datepicker.querySelector('button[type="button"]')
254
+ fireEvent.click(calendarButton!)
255
+ await datepicker.updateComplete
256
+
257
+ const calendar = datepicker.querySelector('pkt-calendar') as PktCalendar
258
+ expect(calendar.earliest).toBe('2024-06-01')
259
+ expect(calendar.latest).toBe('2024-06-30')
260
+ expect(calendar.excludeweekdays).toEqual(['0', '6'])
261
+ })
262
+
263
+ test('syncs selected dates between input and calendar', async () => {
264
+ const testDate = '2024-06-15'
265
+ const container = await createDatepicker(`value="${testDate}"`)
266
+
267
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
268
+ await datepicker.updateComplete
269
+
270
+ // Open calendar
271
+ const calendarButton = datepicker.querySelector('button[type="button"]')
272
+ fireEvent.click(calendarButton!)
273
+ await datepicker.updateComplete
274
+
275
+ const calendar = datepicker.querySelector('pkt-calendar') as PktCalendar
276
+ expect(calendar.selected).toContain(testDate)
277
+
278
+ const selectedDate = datepicker.querySelector('.pkt-calendar__date--selected')
279
+ expect(selectedDate).toBeInTheDocument()
280
+ })
281
+
282
+ test('updates input when date selected in calendar', async () => {
283
+ const container = await createDatepicker()
284
+
285
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
286
+ await datepicker.updateComplete
287
+
288
+ // Open calendar
289
+ const calendarButton = datepicker.querySelector('button[type="button"]')
290
+ fireEvent.click(calendarButton!)
291
+ await datepicker.updateComplete
292
+
293
+ // Select a date
294
+ const availableDate = datepicker.querySelector(
295
+ '.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
296
+ )
297
+ fireEvent.click(availableDate!)
298
+ await datepicker.updateComplete
299
+
300
+ // Input should be updated
301
+ const input = datepicker.querySelector('input') as HTMLInputElement
302
+ expect(input.value).toBeTruthy()
303
+ expect(datepicker.value).toBeTruthy()
304
+ })
305
+
306
+ test('shows current month correctly in calendar', async () => {
307
+ const currentMonth = '2024-06-01'
308
+ const container = await createDatepicker(`currentmonth="${currentMonth}"`)
309
+
310
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
311
+ await datepicker.updateComplete
312
+
313
+ // Open calendar
314
+ const calendarButton = datepicker.querySelector('button[type="button"]')
315
+ fireEvent.click(calendarButton!)
316
+ await datepicker.updateComplete
317
+
318
+ const monthTitle = datepicker.querySelector('.pkt-calendar__month-title')
319
+ expect(monthTitle?.textContent).toContain('2024')
320
+ })
321
+ })
322
+ })
@@ -0,0 +1,268 @@
1
+ import '@testing-library/jest-dom'
2
+ import { fireEvent } from '@testing-library/dom'
3
+
4
+ import './datepicker'
5
+ import '../calendar/calendar'
6
+ import { PktDatepicker } from './datepicker'
7
+
8
+ const waitForCustomElements = async () => {
9
+ await customElements.whenDefined('pkt-datepicker')
10
+ await customElements.whenDefined('pkt-calendar')
11
+ }
12
+
13
+ // Helper function to create datepicker markup
14
+ const createDatepicker = async (datepickerProps = '') => {
15
+ const container = document.createElement('div')
16
+ container.innerHTML = `
17
+ <pkt-datepicker ${datepickerProps}></pkt-datepicker>
18
+ `
19
+ document.body.appendChild(container)
20
+ await waitForCustomElements()
21
+ return container
22
+ }
23
+
24
+ // Cleanup after each test
25
+ afterEach(() => {
26
+ document.body.innerHTML = ''
27
+ })
28
+
29
+ describe('PktDatepicker', () => {
30
+ describe('Date input validation and formatting', () => {
31
+ test('validates date input format', async () => {
32
+ const container = await createDatepicker('label="Test"')
33
+
34
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
35
+ await datepicker.updateComplete
36
+
37
+ const input = datepicker.querySelector('input') as HTMLInputElement
38
+
39
+ // Test invalid date input - HTML5 date inputs will reject invalid formats
40
+ // For date inputs, we test boundary validation instead
41
+ fireEvent.change(input, { target: { value: '2024-02-30' } }) // Invalid date (Feb 30th)
42
+ fireEvent.blur(input)
43
+ await datepicker.updateComplete
44
+
45
+ // Should show validation error or handle gracefully
46
+ // For HTML5 date inputs, this might not trigger hasError, so we test that it doesn't crash
47
+ expect(datepicker).toBeInTheDocument()
48
+ })
49
+
50
+ test('formats dates according to dateformat property', async () => {
51
+ const container = await createDatepicker('dateformat="yyyy-MM-dd" value="2024-06-15"')
52
+
53
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
54
+ await datepicker.updateComplete
55
+
56
+ const input = datepicker.querySelector('input') as HTMLInputElement
57
+ expect(input.value).toBe('2024-06-15')
58
+ })
59
+
60
+ test('handles different date formats', async () => {
61
+ const testCases = [
62
+ { format: 'dd.MM.yyyy', expected: /\d{2}\.\d{2}\.\d{4}/ },
63
+ { format: 'MM/dd/yyyy', expected: /\d{2}\/\d{2}\/\d{4}/ },
64
+ { format: 'yyyy-MM-dd', expected: /\d{4}-\d{2}-\d{2}/ },
65
+ ]
66
+
67
+ for (const testCase of testCases) {
68
+ const container = await createDatepicker(
69
+ `dateformat="${testCase.format}" value="2024-06-15" multiple`,
70
+ )
71
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
72
+ await datepicker.updateComplete
73
+
74
+ // Multiple mode input should be empty (for new input)
75
+ const input = datepicker.querySelector('input') as HTMLInputElement
76
+ expect(input.value).toBe('')
77
+
78
+ // Selected dates should show in tags with custom format
79
+ const tag = datepicker.querySelector('pkt-tag time')
80
+ expect(tag).toBeInTheDocument()
81
+ if (tag) {
82
+ expect(tag.textContent).toMatch(testCase.expected)
83
+ }
84
+
85
+ // Cleanup
86
+ container.remove()
87
+ }
88
+ })
89
+
90
+ test('handles leap year dates correctly', async () => {
91
+ const leapYearDate = '2024-02-29'
92
+ const container = await createDatepicker(`value="${leapYearDate}"`)
93
+
94
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
95
+ await datepicker.updateComplete
96
+
97
+ expect(datepicker.value).toBe(leapYearDate)
98
+ expect(datepicker.hasError).toBe(false)
99
+ })
100
+
101
+ test('validates February 29 in non-leap years', async () => {
102
+ const invalidLeapDate = '2023-02-29'
103
+ const container = await createDatepicker(`value="${invalidLeapDate}"`)
104
+
105
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
106
+ await datepicker.updateComplete
107
+
108
+ // Should handle gracefully or show error
109
+ expect(datepicker).toBeInTheDocument()
110
+ })
111
+
112
+ test('handles edge case dates', async () => {
113
+ const edgeDates = ['1900-01-01', '2000-01-01', '2100-12-31', '1999-12-31']
114
+
115
+ for (const date of edgeDates) {
116
+ const container = await createDatepicker(`value="${date}"`)
117
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
118
+ await datepicker.updateComplete
119
+
120
+ expect(datepicker.value).toBe(date)
121
+ container.remove()
122
+ }
123
+ })
124
+ })
125
+
126
+ describe('Input field behavior', () => {
127
+ test('allows manual date entry', async () => {
128
+ const container = await createDatepicker('dateformat="yyyy-MM-dd"')
129
+
130
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
131
+ await datepicker.updateComplete
132
+
133
+ const input = datepicker.querySelector('input') as HTMLInputElement
134
+
135
+ fireEvent.change(input, { target: { value: '2024-06-15' } })
136
+ fireEvent.blur(input)
137
+ await datepicker.updateComplete
138
+
139
+ expect(datepicker.value).toBe('2024-06-15')
140
+ })
141
+
142
+ test('validates manual date entry', async () => {
143
+ const container = await createDatepicker()
144
+
145
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
146
+ await datepicker.updateComplete
147
+
148
+ const input = datepicker.querySelector('input') as HTMLInputElement
149
+
150
+ // HTML5 date inputs reject invalid formats, so we test that the component handles this gracefully
151
+ fireEvent.change(input, { target: { value: 'invalid-date' } })
152
+ fireEvent.blur(input)
153
+ await datepicker.updateComplete
154
+
155
+ // HTML5 date input will reject invalid dates, component should handle gracefully
156
+ expect(datepicker).toBeInTheDocument()
157
+ expect(input.value).toBe('') // Invalid dates become empty
158
+ })
159
+
160
+ test('shows placeholder text correctly', async () => {
161
+ const placeholderText = 'Select a date'
162
+ const container = await createDatepicker(`placeholder="${placeholderText}"`)
163
+
164
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
165
+ await datepicker.updateComplete
166
+
167
+ const input = datepicker.querySelector('input') as HTMLInputElement
168
+ expect(input.placeholder).toBe(placeholderText)
169
+ })
170
+
171
+ test('shows help text correctly', async () => {
172
+ const helpText = 'Choose your preferred date'
173
+ const container = await createDatepicker(`helptext="${helpText}"`)
174
+
175
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
176
+ await datepicker.updateComplete
177
+
178
+ const helpTextElement = datepicker.querySelector('pkt-helptext')
179
+ expect(helpTextElement?.textContent).toContain(helpText)
180
+ })
181
+
182
+ test('handles readonly state correctly', async () => {
183
+ const container = await createDatepicker('readonly')
184
+
185
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
186
+ await datepicker.updateComplete
187
+
188
+ const input = datepicker.querySelector('input') as HTMLInputElement
189
+ expect(input.readOnly).toBe(true)
190
+
191
+ // Calendar should still be accessible
192
+ const calendarButton = datepicker.querySelector('button[type="button"]') as HTMLButtonElement
193
+ expect(calendarButton.disabled).toBe(false)
194
+ })
195
+ })
196
+
197
+ describe('Keyboard navigation and interaction', () => {
198
+ test('opens calendar with Enter key on calendar button', async () => {
199
+ const container = await createDatepicker()
200
+
201
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
202
+ await datepicker.updateComplete
203
+
204
+ const calendarButton = datepicker.querySelector('button[type="button"]') as HTMLElement
205
+ if (calendarButton) {
206
+ calendarButton.focus()
207
+
208
+ fireEvent.keyDown(calendarButton, { key: 'Enter' })
209
+ await datepicker.updateComplete
210
+
211
+ expect(datepicker.calendarOpen).toBe(true)
212
+ }
213
+ })
214
+
215
+ test('opens calendar with Space key on calendar button', async () => {
216
+ const container = await createDatepicker()
217
+
218
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
219
+ await datepicker.updateComplete
220
+
221
+ const calendarButton = datepicker.querySelector('button[type="button"]') as HTMLElement
222
+ if (calendarButton) {
223
+ calendarButton.focus()
224
+
225
+ fireEvent.keyDown(calendarButton, { key: ' ' })
226
+ await datepicker.updateComplete
227
+
228
+ expect(datepicker.calendarOpen).toBe(true)
229
+ }
230
+ })
231
+
232
+ test('closes calendar with Escape key', async () => {
233
+ const container = await createDatepicker()
234
+
235
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
236
+ await datepicker.updateComplete
237
+
238
+ // Open calendar
239
+ const calendarButton = datepicker.querySelector('button[type="button"]')
240
+ fireEvent.click(calendarButton!)
241
+ await datepicker.updateComplete
242
+
243
+ // Press Escape
244
+ fireEvent.keyDown(datepicker, { key: 'Escape' })
245
+ await datepicker.updateComplete
246
+
247
+ expect(datepicker.calendarOpen).toBe(false)
248
+ })
249
+
250
+ test('navigates between tags with arrow keys in multiple mode', async () => {
251
+ const multipleDates = '2024-06-15,2024-06-20,2024-06-25'
252
+ const container = await createDatepicker(`value="${multipleDates}" multiple`)
253
+
254
+ const datepicker = container.querySelector('pkt-datepicker') as PktDatepicker
255
+ await datepicker.updateComplete
256
+
257
+ const tags = datepicker.querySelectorAll('pkt-tag')
258
+ const firstTag = tags[0] as HTMLElement
259
+
260
+ firstTag.focus()
261
+ fireEvent.keyDown(firstTag, { key: 'ArrowRight' })
262
+ await datepicker.updateComplete
263
+
264
+ // Focus should move to next tag
265
+ expect(document.activeElement).toBeTruthy()
266
+ })
267
+ })
268
+ })