@oslokommune/punkt-react 13.6.15 → 13.6.16

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 CHANGED
@@ -5,6 +5,24 @@ og skriver commits ca etter [Conventional Commits](https://conventionalcommits.o
5
5
 
6
6
  ---
7
7
 
8
+ ## [13.6.16](https://github.com/oslokommune/punkt/compare/13.6.15...13.6.16) (2025-10-14)
9
+
10
+ ### ⚠ BREAKING CHANGES
11
+ Ingen
12
+
13
+ ### Features
14
+ Ingen
15
+
16
+ ### Bug Fixes
17
+ * Legg til enkel testdekning av datepicker i React (#3076). I påvente av omskriving av datepicker til ren React-komponent er det greit å ha grunnleggende testdekning. Mer testing bør komme etter omskriving.
18
+
19
+
20
+ ### Chores
21
+ Ingen
22
+
23
+ ---
24
+
25
+
8
26
  ## [13.6.15](https://github.com/oslokommune/punkt/compare/13.6.14...13.6.15) (2025-10-09)
9
27
 
10
28
  ### ⚠ BREAKING CHANGES
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oslokommune/punkt-react",
3
- "version": "13.6.15",
3
+ "version": "13.6.16",
4
4
  "description": "React komponentbibliotek til Punkt, et designsystem laget av Oslo Origo",
5
5
  "homepage": "https://punkt.oslo.kommune.no",
6
6
  "author": "Team Designsystem, Oslo Origo",
@@ -110,5 +110,5 @@
110
110
  "url": "https://github.com/oslokommune/punkt/issues"
111
111
  },
112
112
  "license": "MIT",
113
- "gitHead": "4798e1ff1529f3e32cd4c4173fab3ec3586319da"
113
+ "gitHead": "82d543a41c3ad5f580586874c4e0547c7fa8f10d"
114
114
  }
@@ -0,0 +1,393 @@
1
+ import '@testing-library/jest-dom'
2
+ import { fireEvent, render, waitFor } from '@testing-library/react'
3
+ import { axe, toHaveNoViolations } from 'jest-axe'
4
+ import { PktDatepicker, IPktDatepicker } from './Datepicker'
5
+
6
+ const datePickerId = 'datepickerId'
7
+ const label = 'Date Picker Label'
8
+
9
+ const waitForDatepicker = async () => {
10
+ await window.customElements.whenDefined('pkt-datepicker')
11
+ }
12
+
13
+ const waitForCustomElementUpdate = async () => {
14
+ return new Promise((resolve) => {
15
+ requestAnimationFrame(resolve)
16
+ })
17
+ }
18
+
19
+ const createDatepickerTest = (props: Partial<IPktDatepicker> = {}) => {
20
+ const defaultProps: IPktDatepicker = {
21
+ label,
22
+ id: datePickerId,
23
+ ...props,
24
+ }
25
+
26
+ return render(<PktDatepicker {...defaultProps} />)
27
+ }
28
+
29
+ describe('PktDatepicker - React Form Element', () => {
30
+ beforeEach(async () => {
31
+ await waitForDatepicker()
32
+ })
33
+
34
+ expect.extend(toHaveNoViolations)
35
+
36
+ describe('Single date selection', () => {
37
+ test('sets and confirms a single date value', async () => {
38
+ const handleChange = jest.fn()
39
+ const handleValueChange = jest.fn()
40
+ const { container } = createDatepickerTest({
41
+ onChange: handleChange,
42
+ onValueChange: handleValueChange,
43
+ })
44
+
45
+ await waitFor(() => {
46
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
47
+ expect(input).toBeInTheDocument()
48
+
49
+ fireEvent.change(input, { target: { value: '2024-06-15' } })
50
+ fireEvent.blur(input)
51
+ })
52
+
53
+ await waitFor(() => {
54
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
55
+ expect(input.value).toBe('2024-06-15')
56
+
57
+ expect(handleChange).toHaveBeenCalledWith(
58
+ expect.objectContaining({
59
+ target: expect.objectContaining({
60
+ value: '2024-06-15',
61
+ }),
62
+ }),
63
+ )
64
+
65
+ expect(handleValueChange).toHaveBeenCalledWith(
66
+ expect.objectContaining({
67
+ detail: '2024-06-15',
68
+ }),
69
+ )
70
+ })
71
+ })
72
+
73
+ test('sets date via calendar interaction', async () => {
74
+ const handleValueChange = jest.fn()
75
+ const { container } = createDatepickerTest({
76
+ onValueChange: handleValueChange,
77
+ })
78
+
79
+ await waitFor(() => {
80
+ const calendarButton = container.querySelector('button[type="button"]')
81
+ expect(calendarButton).toBeInTheDocument()
82
+ fireEvent.click(calendarButton!)
83
+ })
84
+
85
+ await waitFor(() => {
86
+ const calendar = container.querySelector('pkt-calendar')
87
+ expect(calendar).toBeInTheDocument()
88
+ })
89
+
90
+ await waitFor(() => {
91
+ const dateButtons = container.querySelectorAll('pkt-calendar button[data-date]')
92
+ const firstDateButton = Array.from(dateButtons).find(
93
+ (btn) => btn.textContent && btn.textContent.trim() && !btn.hasAttribute('disabled'),
94
+ ) as HTMLButtonElement
95
+
96
+ if (firstDateButton) {
97
+ fireEvent.click(firstDateButton)
98
+ }
99
+ })
100
+
101
+ await waitForCustomElementUpdate()
102
+
103
+ await waitFor(() => {
104
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
105
+ expect(input.value).not.toBe('')
106
+ expect(handleValueChange).toHaveBeenCalled()
107
+ })
108
+ })
109
+
110
+ test('sets date via keyboard input', async () => {
111
+ const handleValueChange = jest.fn()
112
+ const { container } = createDatepickerTest({
113
+ onValueChange: handleValueChange,
114
+ })
115
+
116
+ await waitFor(() => {
117
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
118
+ expect(input).toBeInTheDocument()
119
+
120
+ fireEvent.change(input, { target: { value: '2025-10-15' } })
121
+ })
122
+
123
+ await waitForCustomElementUpdate()
124
+
125
+ await waitFor(() => {
126
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
127
+ expect(input.value).toBe('2025-10-15')
128
+ })
129
+ })
130
+
131
+ test('has no accessibility violations with single date set', async () => {
132
+ const { container } = createDatepickerTest({
133
+ value: '2024-06-15',
134
+ label: 'Select Date',
135
+ helptext: 'Choose a date from the calendar',
136
+ })
137
+
138
+ await waitForCustomElementUpdate()
139
+
140
+ await waitFor(() => {
141
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
142
+ expect(input.value).toBe('2024-06-15')
143
+ })
144
+
145
+ const results = await axe(container)
146
+ expect(results).toHaveNoViolations()
147
+ })
148
+ })
149
+
150
+ describe('Multiple date selection', () => {
151
+ test('sets and confirms multiple date values', async () => {
152
+ const handleValueChange = jest.fn()
153
+ const { container } = createDatepickerTest({
154
+ multiple: true,
155
+ value: ['2024-06-15', '2024-06-20'],
156
+ onValueChange: handleValueChange,
157
+ })
158
+
159
+ await waitFor(() => {
160
+ const datepicker = container.querySelector('pkt-datepicker')
161
+ expect(datepicker).toHaveAttribute('multiple')
162
+ })
163
+
164
+ await waitForCustomElementUpdate()
165
+
166
+ await waitFor(() => {
167
+ const tags = container.querySelectorAll('pkt-tag')
168
+ expect(tags.length).toBe(2)
169
+ })
170
+ })
171
+
172
+ test('dispatches onChange and onValueChange for multiple dates', async () => {
173
+ const handleChange = jest.fn()
174
+ const handleValueChange = jest.fn()
175
+ const dates = ['2024-06-15', '2024-06-20']
176
+
177
+ const { container } = createDatepickerTest({
178
+ multiple: true,
179
+ value: dates,
180
+ onChange: handleChange,
181
+ onValueChange: handleValueChange,
182
+ })
183
+
184
+ await waitForCustomElementUpdate()
185
+
186
+ await waitFor(() => {
187
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
188
+ fireEvent.change(input, { target: { value: '2024-06-25' } })
189
+ })
190
+
191
+ await waitForCustomElementUpdate()
192
+
193
+ await waitFor(() => {
194
+ expect(handleChange).toHaveBeenCalledWith(
195
+ expect.objectContaining({
196
+ target: expect.objectContaining({
197
+ tagName: 'PKT-DATEPICKER',
198
+ value: '2024-06-15,2024-06-20',
199
+ }),
200
+ }),
201
+ )
202
+
203
+ expect(handleValueChange).toHaveBeenCalledWith(
204
+ expect.objectContaining({
205
+ detail: ['2024-06-15', '2024-06-20'],
206
+ }),
207
+ )
208
+ })
209
+ })
210
+
211
+ test('has no accessibility violations with multiple dates set', async () => {
212
+ const { container } = createDatepickerTest({
213
+ multiple: true,
214
+ value: ['2024-06-15', '2024-06-20', '2024-06-25'],
215
+ label: 'Select Multiple Dates',
216
+ helptext: 'Choose multiple dates from the calendar',
217
+ })
218
+
219
+ await waitForCustomElementUpdate()
220
+
221
+ await waitFor(() => {
222
+ const tags = container.querySelectorAll('pkt-tag')
223
+ expect(tags.length).toBe(3)
224
+ })
225
+
226
+ const results = await axe(container)
227
+ expect(results).toHaveNoViolations()
228
+ })
229
+ })
230
+
231
+ describe('Range date selection', () => {
232
+ test('sets and confirms range date values', async () => {
233
+ const handleValueChange = jest.fn()
234
+ const { container } = createDatepickerTest({
235
+ range: true,
236
+ value: ['2024-06-15', '2024-06-25'],
237
+ onValueChange: handleValueChange,
238
+ })
239
+
240
+ await waitFor(() => {
241
+ const datepicker = container.querySelector('pkt-datepicker')
242
+ expect(datepicker).toHaveAttribute('range')
243
+ const rangeInputs = container.querySelectorAll('input[type="date"]')
244
+ expect(rangeInputs.length).toBe(2)
245
+ })
246
+ })
247
+
248
+ test('has no accessibility violations with range dates set', async () => {
249
+ const { container } = createDatepickerTest({
250
+ range: true,
251
+ value: ['2024-06-15', '2024-06-25'],
252
+ label: 'Select Date Range',
253
+ helptext: 'Choose start and end dates for your range',
254
+ })
255
+
256
+ await waitForCustomElementUpdate()
257
+
258
+ await waitFor(() => {
259
+ const datepicker = container.querySelector('pkt-datepicker')
260
+ expect(datepicker).toHaveAttribute('range')
261
+ expect(datepicker).toHaveAttribute('value', '2024-06-15,2024-06-25')
262
+ })
263
+
264
+ const results = await axe(container)
265
+ expect(results).toHaveNoViolations()
266
+ })
267
+ })
268
+
269
+ describe('Form integration', () => {
270
+ test('works as a form element with name attribute', async () => {
271
+ const { container } = createDatepickerTest({
272
+ name: 'dateField',
273
+ value: '2024-06-15',
274
+ })
275
+
276
+ await waitFor(() => {
277
+ const datepicker = container.querySelector('pkt-datepicker')
278
+ expect(datepicker).toHaveAttribute('name', 'dateField')
279
+ expect(datepicker).toHaveAttribute('value', '2024-06-15')
280
+ })
281
+ })
282
+
283
+ test('handles required validation', async () => {
284
+ const { container } = createDatepickerTest({
285
+ required: true,
286
+ name: 'requiredDate',
287
+ })
288
+
289
+ await waitFor(() => {
290
+ const datepicker = container.querySelector('pkt-datepicker')
291
+ expect(datepicker).toHaveAttribute('required')
292
+ expect(datepicker).toHaveAttribute('name', 'requiredDate')
293
+ })
294
+ })
295
+
296
+ test('handles disabled state', async () => {
297
+ const { container } = createDatepickerTest({
298
+ disabled: true,
299
+ })
300
+
301
+ await waitFor(() => {
302
+ const datepicker = container.querySelector('pkt-datepicker')
303
+ expect(datepicker).toHaveAttribute('disabled')
304
+ })
305
+ })
306
+
307
+ test('sets min and max date boundaries', async () => {
308
+ const { container } = createDatepickerTest({
309
+ min: '2024-01-01',
310
+ max: '2024-12-31',
311
+ value: '2024-06-15',
312
+ })
313
+
314
+ await waitFor(() => {
315
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
316
+ expect(input).toBeInTheDocument()
317
+ expect(input).toHaveAttribute('min', '2024-01-01')
318
+ expect(input).toHaveAttribute('max', '2024-12-31')
319
+ })
320
+ })
321
+ })
322
+
323
+ describe('Event handling', () => {
324
+ test('handles input value changes for form behavior', async () => {
325
+ const { container } = createDatepickerTest({})
326
+
327
+ await waitFor(() => {
328
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
329
+
330
+ fireEvent.change(input, { target: { value: '2024-07-20' } })
331
+ })
332
+
333
+ await waitForCustomElementUpdate()
334
+
335
+ await waitFor(() => {
336
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
337
+ expect(input.value).toBe('2024-07-20')
338
+
339
+ const datepicker = container.querySelector('pkt-datepicker')
340
+ expect(datepicker).toBeInTheDocument()
341
+ })
342
+ })
343
+
344
+ test('triggers focus and blur events', async () => {
345
+ const handleFocus = jest.fn()
346
+ const handleBlur = jest.fn()
347
+ const { container } = createDatepickerTest({
348
+ onFocus: handleFocus,
349
+ onBlur: handleBlur,
350
+ })
351
+
352
+ await waitFor(() => {
353
+ const input = container.querySelector('input[type="date"]') as HTMLInputElement
354
+
355
+ fireEvent.focus(input)
356
+ fireEvent.blur(input)
357
+ })
358
+
359
+ await waitFor(() => {
360
+ expect(handleFocus).toHaveBeenCalled()
361
+ expect(handleBlur).toHaveBeenCalled()
362
+ })
363
+ })
364
+ })
365
+
366
+ describe('Accessibility and form behavior', () => {
367
+ test('has proper label association for screen readers', async () => {
368
+ const { container } = createDatepickerTest({
369
+ label: 'Select your birthday',
370
+ })
371
+
372
+ await waitFor(() => {
373
+ const label = container.querySelector('label')
374
+ const input = container.querySelector('input[type="date"]')
375
+
376
+ expect(label).toHaveTextContent('Select your birthday')
377
+ expect(label).toHaveAttribute('for', expect.stringContaining('input'))
378
+ expect(input).toHaveAttribute('id', expect.stringContaining('input'))
379
+ })
380
+ })
381
+
382
+ test('supports help text for user guidance', async () => {
383
+ const { container } = createDatepickerTest({
384
+ helptext: 'Please select a date in the format DD.MM.YYYY',
385
+ })
386
+
387
+ await waitFor(() => {
388
+ const helpText = container.querySelector('[id*="helptext"]')
389
+ expect(helpText).toBeInTheDocument()
390
+ })
391
+ })
392
+ })
393
+ })