@oslokommune/punkt-react 14.5.3 → 15.0.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/CHANGELOG.md +33 -0
- package/dist/index.d.ts +64 -21
- package/dist/punkt-react.es.js +7198 -4674
- package/dist/punkt-react.umd.js +573 -569
- package/package.json +5 -5
- package/src/components/calendar/Calendar.accessibility.test.tsx +75 -0
- package/src/components/calendar/Calendar.constraints.test.tsx +84 -0
- package/src/components/calendar/Calendar.core.test.tsx +272 -0
- package/src/components/calendar/Calendar.interaction.test.tsx +96 -0
- package/src/components/calendar/Calendar.selection.test.tsx +227 -0
- package/src/components/calendar/Calendar.tsx +54 -0
- package/src/components/calendar/CalendarGrid.tsx +192 -0
- package/src/components/calendar/CalendarNav.tsx +111 -0
- package/src/components/calendar/calendar-utils.ts +90 -0
- package/src/components/calendar/types.ts +160 -0
- package/src/components/calendar/useCalendarState.ts +426 -0
- package/src/components/datepicker/DateTags.tsx +43 -0
- package/src/components/datepicker/Datepicker.accessibility.test.tsx +404 -0
- package/src/components/datepicker/Datepicker.core.test.tsx +270 -0
- package/src/components/datepicker/Datepicker.input.test.tsx +218 -0
- package/src/components/datepicker/Datepicker.selection.test.tsx +302 -0
- package/src/components/datepicker/Datepicker.tsx +61 -79
- package/src/components/datepicker/Datepicker.validation.test.tsx +317 -0
- package/src/components/datepicker/DatepickerInputs.tsx +184 -0
- package/src/components/datepicker/DatepickerPopup.tsx +90 -0
- package/src/components/datepicker/types.ts +139 -0
- package/src/components/datepicker/useDatepickerState.ts +502 -0
- package/src/components/index.ts +1 -0
- package/src/components/datepicker/Datepicker.test.tsx +0 -395
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
|
|
3
|
+
import { fireEvent, render, screen } from '@testing-library/react'
|
|
4
|
+
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
5
|
+
|
|
6
|
+
import { IPktDatepicker, PktDatepicker } from './Datepicker'
|
|
7
|
+
|
|
8
|
+
expect.extend(toHaveNoViolations)
|
|
9
|
+
|
|
10
|
+
const datePickerId = 'datepickerId'
|
|
11
|
+
const label = 'Date Picker Label'
|
|
12
|
+
|
|
13
|
+
const createDatepickerTest = (props: Partial<IPktDatepicker> = {}) => {
|
|
14
|
+
const defaultProps: IPktDatepicker = {
|
|
15
|
+
label,
|
|
16
|
+
id: datePickerId,
|
|
17
|
+
...props,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return render(<PktDatepicker {...defaultProps} />)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
describe('PktDatepicker', () => {
|
|
24
|
+
describe('Event handling', () => {
|
|
25
|
+
test('dispatches onChange event when value changes', () => {
|
|
26
|
+
const handleChange = jest.fn()
|
|
27
|
+
const { container } = createDatepickerTest({
|
|
28
|
+
onChange: handleChange,
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
32
|
+
fireEvent.change(input, { target: { value: '2024-06-15' } })
|
|
33
|
+
|
|
34
|
+
expect(handleChange).toHaveBeenCalled()
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
test('dispatches onValueChange event when value changes', () => {
|
|
38
|
+
const handleValueChange = jest.fn()
|
|
39
|
+
const { container } = createDatepickerTest({
|
|
40
|
+
onValueChange: handleValueChange,
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
44
|
+
fireEvent.change(input, { target: { value: '2024-06-15' } })
|
|
45
|
+
|
|
46
|
+
expect(handleValueChange).toHaveBeenCalledWith(['2024-06-15'])
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('input can receive focus', () => {
|
|
50
|
+
const { container } = createDatepickerTest()
|
|
51
|
+
|
|
52
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
53
|
+
input.focus()
|
|
54
|
+
|
|
55
|
+
expect(document.activeElement).toBe(input)
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('input can be blurred', () => {
|
|
59
|
+
const { container } = createDatepickerTest()
|
|
60
|
+
|
|
61
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
62
|
+
input.focus()
|
|
63
|
+
expect(document.activeElement).toBe(input)
|
|
64
|
+
|
|
65
|
+
input.blur()
|
|
66
|
+
expect(document.activeElement).not.toBe(input)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('dispatches both onChange and onValueChange', () => {
|
|
70
|
+
const handleChange = jest.fn()
|
|
71
|
+
const handleValueChange = jest.fn()
|
|
72
|
+
const { container } = createDatepickerTest({
|
|
73
|
+
onChange: handleChange,
|
|
74
|
+
onValueChange: handleValueChange,
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
78
|
+
fireEvent.change(input, { target: { value: '2024-06-15' } })
|
|
79
|
+
|
|
80
|
+
expect(handleChange).toHaveBeenCalled()
|
|
81
|
+
expect(handleValueChange).toHaveBeenCalledWith(['2024-06-15'])
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
describe('Accessibility', () => {
|
|
86
|
+
test('has no accessibility violations with single date set', async () => {
|
|
87
|
+
const { container } = createDatepickerTest({
|
|
88
|
+
value: '2024-06-15',
|
|
89
|
+
helptext: 'Choose a date from the calendar',
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
const results = await axe(container)
|
|
93
|
+
expect(results).toHaveNoViolations()
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
test('has no accessibility violations with range dates set', async () => {
|
|
97
|
+
const { container } = createDatepickerTest({
|
|
98
|
+
range: true,
|
|
99
|
+
value: ['2024-06-15', '2024-06-25'],
|
|
100
|
+
helptext: 'Choose start and end dates',
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const results = await axe(container)
|
|
104
|
+
expect(results).toHaveNoViolations()
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('has no accessibility violations with multiple dates', async () => {
|
|
108
|
+
const { container } = createDatepickerTest({
|
|
109
|
+
multiple: true,
|
|
110
|
+
value: ['2024-06-15', '2024-06-20', '2024-06-25'],
|
|
111
|
+
helptext: 'Choose multiple dates',
|
|
112
|
+
})
|
|
113
|
+
|
|
114
|
+
const results = await axe(container)
|
|
115
|
+
expect(results).toHaveNoViolations()
|
|
116
|
+
})
|
|
117
|
+
|
|
118
|
+
test('has no accessibility violations when disabled', async () => {
|
|
119
|
+
const { container } = createDatepickerTest({
|
|
120
|
+
disabled: true,
|
|
121
|
+
helptext: 'This field is disabled',
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
const results = await axe(container)
|
|
125
|
+
expect(results).toHaveNoViolations()
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
test('has no accessibility violations with error state', async () => {
|
|
129
|
+
const { container } = createDatepickerTest({
|
|
130
|
+
hasError: true,
|
|
131
|
+
errorMessage: 'Please enter a valid date',
|
|
132
|
+
helptext: 'Required field',
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
const results = await axe(container)
|
|
136
|
+
expect(results).toHaveNoViolations()
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('associates label with input correctly', () => {
|
|
140
|
+
const { container } = createDatepickerTest()
|
|
141
|
+
|
|
142
|
+
const labelEl = container.querySelector('label')
|
|
143
|
+
expect(labelEl).toBeInTheDocument()
|
|
144
|
+
expect(labelEl).toHaveAttribute('for', `${datePickerId}-input`)
|
|
145
|
+
|
|
146
|
+
const input = container.querySelector(`#${datePickerId}-input`)
|
|
147
|
+
expect(input).toBeInTheDocument()
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('has proper ARIA attributes for calendar button', () => {
|
|
151
|
+
const { container } = createDatepickerTest()
|
|
152
|
+
|
|
153
|
+
const button = container.querySelector('button[type="button"]')
|
|
154
|
+
expect(button).toBeInTheDocument()
|
|
155
|
+
expect(button).toHaveAttribute('aria-label', 'Åpne kalender')
|
|
156
|
+
})
|
|
157
|
+
|
|
158
|
+
test('input has aria-describedby when helptext is present', () => {
|
|
159
|
+
const { container } = createDatepickerTest({
|
|
160
|
+
helptext: 'Some help text',
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
164
|
+
expect(input).toHaveAttribute('aria-describedby', `${datePickerId}-helptext`)
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('input does not have aria-describedby when no helptext', () => {
|
|
168
|
+
const { container } = createDatepickerTest()
|
|
169
|
+
|
|
170
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
171
|
+
expect(input).not.toHaveAttribute('aria-describedby')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('input has aria-invalid when hasError is true', () => {
|
|
175
|
+
const { container } = createDatepickerTest({
|
|
176
|
+
hasError: true,
|
|
177
|
+
errorMessage: 'Invalid date',
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
181
|
+
expect(input).toHaveAttribute('aria-invalid', 'true')
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
test('input has aria-errormessage when hasError is true', () => {
|
|
185
|
+
const { container } = createDatepickerTest({
|
|
186
|
+
hasError: true,
|
|
187
|
+
errorMessage: 'Invalid date',
|
|
188
|
+
})
|
|
189
|
+
|
|
190
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
191
|
+
expect(input).toHaveAttribute('aria-errormessage', `${datePickerId}-error`)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
test('input does not have aria-invalid when no error', () => {
|
|
195
|
+
const { container } = createDatepickerTest()
|
|
196
|
+
|
|
197
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
198
|
+
expect(input).not.toHaveAttribute('aria-invalid', 'true')
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
test('calendar popup has aria-hidden when closed', () => {
|
|
202
|
+
const { container } = createDatepickerTest()
|
|
203
|
+
|
|
204
|
+
const popup = container.querySelector('.pkt-calendar-popup')
|
|
205
|
+
expect(popup).toHaveAttribute('aria-hidden', 'true')
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
test('calendar popup has aria-hidden false when open', () => {
|
|
209
|
+
const { container } = createDatepickerTest()
|
|
210
|
+
|
|
211
|
+
const button = container.querySelector('button[type="button"]') as HTMLButtonElement
|
|
212
|
+
fireEvent.click(button)
|
|
213
|
+
|
|
214
|
+
const popup = container.querySelector('.pkt-calendar-popup')
|
|
215
|
+
expect(popup).toHaveAttribute('aria-hidden', 'false')
|
|
216
|
+
})
|
|
217
|
+
|
|
218
|
+
test('date tags container has aria-live polite', () => {
|
|
219
|
+
const { container } = createDatepickerTest({
|
|
220
|
+
multiple: true,
|
|
221
|
+
value: ['2024-06-15'],
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
const tagsContainer = container.querySelector('.pkt-date-tags')
|
|
225
|
+
expect(tagsContainer).toHaveAttribute('aria-live', 'polite')
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test('handles focus management - button can be focused', () => {
|
|
229
|
+
const { container } = createDatepickerTest()
|
|
230
|
+
|
|
231
|
+
const button = container.querySelector('button[type="button"]') as HTMLButtonElement
|
|
232
|
+
button.focus()
|
|
233
|
+
|
|
234
|
+
expect(document.activeElement).toBe(button)
|
|
235
|
+
})
|
|
236
|
+
|
|
237
|
+
test('supports keyboard-only interaction to open and close calendar', () => {
|
|
238
|
+
const { container } = createDatepickerTest()
|
|
239
|
+
|
|
240
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
241
|
+
|
|
242
|
+
// Open with Space
|
|
243
|
+
fireEvent.keyDown(input, { key: ' ' })
|
|
244
|
+
let popup = container.querySelector('.pkt-calendar-popup')
|
|
245
|
+
expect(popup).not.toHaveAttribute('hidden')
|
|
246
|
+
|
|
247
|
+
// Close with Escape
|
|
248
|
+
fireEvent.keyDown(document, { key: 'Escape' })
|
|
249
|
+
popup = container.querySelector('.pkt-calendar-popup')
|
|
250
|
+
expect(popup).toHaveAttribute('hidden')
|
|
251
|
+
})
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
describe('Localization', () => {
|
|
255
|
+
test('uses custom strings for calendar button', () => {
|
|
256
|
+
const { container } = createDatepickerTest({
|
|
257
|
+
strings: {
|
|
258
|
+
calendar: { buttonAltText: 'Open calendar' },
|
|
259
|
+
},
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
const button = container.querySelector('button[type="button"]')
|
|
263
|
+
expect(button).toHaveAttribute('aria-label', 'Open calendar')
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
test('uses custom strings for range labels', () => {
|
|
267
|
+
const { container } = createDatepickerTest({
|
|
268
|
+
range: true,
|
|
269
|
+
showRangeLabels: true,
|
|
270
|
+
strings: {
|
|
271
|
+
generic: { from: 'From', to: 'To' },
|
|
272
|
+
calendar: { buttonAltText: 'Open calendar' },
|
|
273
|
+
},
|
|
274
|
+
})
|
|
275
|
+
|
|
276
|
+
const prefix = container.querySelector('.pkt-input-prefix')
|
|
277
|
+
expect(prefix).toHaveTextContent('From')
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
test('uses default Norwegian strings', () => {
|
|
281
|
+
const { container } = createDatepickerTest({
|
|
282
|
+
range: true,
|
|
283
|
+
showRangeLabels: true,
|
|
284
|
+
})
|
|
285
|
+
|
|
286
|
+
const button = container.querySelector('button[type="button"]')
|
|
287
|
+
expect(button).toHaveAttribute('aria-label', 'Åpne kalender')
|
|
288
|
+
|
|
289
|
+
const prefix = container.querySelector('.pkt-input-prefix')
|
|
290
|
+
expect(prefix).toHaveTextContent('Fra')
|
|
291
|
+
})
|
|
292
|
+
})
|
|
293
|
+
|
|
294
|
+
describe('InputWrapper integration', () => {
|
|
295
|
+
test('renders helptext', () => {
|
|
296
|
+
createDatepickerTest({
|
|
297
|
+
helptext: 'Please select a date',
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
expect(screen.getByText('Please select a date')).toBeInTheDocument()
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
test('renders error message when hasError is true', () => {
|
|
304
|
+
createDatepickerTest({
|
|
305
|
+
hasError: true,
|
|
306
|
+
errorMessage: 'Date is required',
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
expect(screen.getByText('Date is required')).toBeInTheDocument()
|
|
310
|
+
})
|
|
311
|
+
|
|
312
|
+
test('renders optional tag', () => {
|
|
313
|
+
const { container } = createDatepickerTest({
|
|
314
|
+
optionalTag: true,
|
|
315
|
+
optionalText: 'Valgfritt',
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
expect(container.textContent).toContain('Valgfritt')
|
|
319
|
+
})
|
|
320
|
+
|
|
321
|
+
test('renders required tag', () => {
|
|
322
|
+
const { container } = createDatepickerTest({
|
|
323
|
+
requiredTag: true,
|
|
324
|
+
requiredText: 'Må fylles ut',
|
|
325
|
+
})
|
|
326
|
+
|
|
327
|
+
expect(container.textContent).toContain('Må fylles ut')
|
|
328
|
+
})
|
|
329
|
+
|
|
330
|
+
test('applies fullwidth class to input', () => {
|
|
331
|
+
const { container } = createDatepickerTest({
|
|
332
|
+
fullwidth: true,
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
const input = container.querySelector('.pkt-datepicker__input') as HTMLInputElement
|
|
336
|
+
expect(input).toHaveClass('pkt-input--fullwidth')
|
|
337
|
+
})
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
describe('CSS classes', () => {
|
|
341
|
+
test('applies pkt-datepicker class to outer wrapper', () => {
|
|
342
|
+
const { container } = createDatepickerTest()
|
|
343
|
+
|
|
344
|
+
const wrapper = container.querySelector('.pkt-datepicker')
|
|
345
|
+
expect(wrapper).toBeInTheDocument()
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
test('applies pkt-datepicker__inputs class', () => {
|
|
349
|
+
const { container } = createDatepickerTest()
|
|
350
|
+
|
|
351
|
+
const inputs = container.querySelector('.pkt-datepicker__inputs')
|
|
352
|
+
expect(inputs).toBeInTheDocument()
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
test('applies datepicker input classes', () => {
|
|
356
|
+
const { container } = createDatepickerTest()
|
|
357
|
+
|
|
358
|
+
const input = container.querySelector('.pkt-datepicker__input')
|
|
359
|
+
expect(input).toBeInTheDocument()
|
|
360
|
+
expect(input).toHaveClass('pkt-input')
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
test('applies range class for range mode', () => {
|
|
364
|
+
const { container } = createDatepickerTest({
|
|
365
|
+
range: true,
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
const inputs = container.querySelectorAll('.pkt-datepicker--range')
|
|
369
|
+
expect(inputs.length).toBe(2)
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
test('applies multiple class for multiple mode', () => {
|
|
373
|
+
const { container } = createDatepickerTest({
|
|
374
|
+
multiple: true,
|
|
375
|
+
})
|
|
376
|
+
|
|
377
|
+
const input = container.querySelector('.pkt-datepicker--multiple')
|
|
378
|
+
expect(input).toBeInTheDocument()
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
test('applies calendar button classes', () => {
|
|
382
|
+
const { container } = createDatepickerTest()
|
|
383
|
+
|
|
384
|
+
const button = container.querySelector('button[type="button"]')
|
|
385
|
+
expect(button).toHaveClass('pkt-input-icon')
|
|
386
|
+
expect(button).toHaveClass('pkt-btn')
|
|
387
|
+
expect(button).toHaveClass('pkt-btn--icon-only')
|
|
388
|
+
expect(button).toHaveClass('pkt-btn--tertiary')
|
|
389
|
+
expect(button).toHaveClass('pkt-datepicker__calendar-button')
|
|
390
|
+
})
|
|
391
|
+
|
|
392
|
+
test('popup has show/hide classes based on open state', () => {
|
|
393
|
+
const { container } = createDatepickerTest()
|
|
394
|
+
|
|
395
|
+
const popup = container.querySelector('.pkt-calendar-popup')
|
|
396
|
+
expect(popup).toHaveClass('hide')
|
|
397
|
+
|
|
398
|
+
const button = container.querySelector('button[type="button"]') as HTMLButtonElement
|
|
399
|
+
fireEvent.click(button)
|
|
400
|
+
|
|
401
|
+
expect(popup).toHaveClass('show')
|
|
402
|
+
})
|
|
403
|
+
})
|
|
404
|
+
})
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
|
|
3
|
+
import { render, screen } from '@testing-library/react'
|
|
4
|
+
|
|
5
|
+
import { IPktDatepicker, PktDatepicker } from './Datepicker'
|
|
6
|
+
|
|
7
|
+
const datePickerId = 'datepickerId'
|
|
8
|
+
const label = 'Date Picker Label'
|
|
9
|
+
|
|
10
|
+
const createDatepickerTest = (props: Partial<IPktDatepicker> = {}) => {
|
|
11
|
+
const defaultProps: IPktDatepicker = {
|
|
12
|
+
label,
|
|
13
|
+
id: datePickerId,
|
|
14
|
+
...props,
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return render(<PktDatepicker {...defaultProps} />)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('PktDatepicker', () => {
|
|
21
|
+
describe('Rendering and basic functionality', () => {
|
|
22
|
+
test('renders without errors', () => {
|
|
23
|
+
const { container } = createDatepickerTest()
|
|
24
|
+
|
|
25
|
+
expect(container.querySelector('.pkt-datepicker')).toBeInTheDocument()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('renders with correct structure', () => {
|
|
29
|
+
const { container } = createDatepickerTest()
|
|
30
|
+
|
|
31
|
+
const inputWrapper = container.querySelector('.pkt-inputwrapper')
|
|
32
|
+
expect(inputWrapper).toBeInTheDocument()
|
|
33
|
+
|
|
34
|
+
const input = container.querySelector('input[type="date"]')
|
|
35
|
+
expect(input).toBeInTheDocument()
|
|
36
|
+
|
|
37
|
+
const button = container.querySelector('button[type="button"]')
|
|
38
|
+
expect(button).toBeInTheDocument()
|
|
39
|
+
|
|
40
|
+
const popup = container.querySelector('.pkt-calendar-popup')
|
|
41
|
+
expect(popup).toBeInTheDocument()
|
|
42
|
+
expect(popup).toHaveAttribute('hidden')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('renders pkt-datepicker class on outer wrapper', () => {
|
|
46
|
+
const { container } = createDatepickerTest()
|
|
47
|
+
|
|
48
|
+
const outer = container.querySelector('.pkt-datepicker')
|
|
49
|
+
expect(outer).toBeInTheDocument()
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
test('renders datepicker inputs container', () => {
|
|
53
|
+
const { container } = createDatepickerTest()
|
|
54
|
+
|
|
55
|
+
const inputs = container.querySelector('.pkt-datepicker__inputs')
|
|
56
|
+
expect(inputs).toBeInTheDocument()
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('renders calendar button with icon', () => {
|
|
60
|
+
const { container } = createDatepickerTest()
|
|
61
|
+
|
|
62
|
+
const button = container.querySelector('button[type="button"]')
|
|
63
|
+
expect(button).toBeInTheDocument()
|
|
64
|
+
expect(button).toHaveAttribute('aria-label', 'Åpne kalender')
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
describe('Properties and attributes', () => {
|
|
69
|
+
test('applies default properties correctly', () => {
|
|
70
|
+
const { container } = createDatepickerTest()
|
|
71
|
+
|
|
72
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
73
|
+
expect(input).toBeInTheDocument()
|
|
74
|
+
expect(input.value).toBe('')
|
|
75
|
+
expect(input).not.toBeDisabled()
|
|
76
|
+
expect(input).not.toBeRequired()
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('handles value property correctly', () => {
|
|
80
|
+
const { container } = createDatepickerTest({
|
|
81
|
+
value: '2024-06-15',
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
85
|
+
expect(input.value).toBe('2024-06-15')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('handles multiple values correctly', () => {
|
|
89
|
+
const { container } = createDatepickerTest({
|
|
90
|
+
multiple: true,
|
|
91
|
+
value: ['2024-06-15', '2024-06-20', '2024-06-25'],
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
const tags = container.querySelectorAll('.pkt-date-tags .pkt-tag')
|
|
95
|
+
expect(tags.length).toBe(3)
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('handles range values correctly', () => {
|
|
99
|
+
const { container } = createDatepickerTest({
|
|
100
|
+
range: true,
|
|
101
|
+
value: ['2024-06-15', '2024-06-25'],
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
const inputs = container.querySelectorAll('input[type="date"]') as NodeListOf<HTMLInputElement>
|
|
105
|
+
expect(inputs.length).toBe(2)
|
|
106
|
+
expect(inputs[0].value).toBe('2024-06-15')
|
|
107
|
+
expect(inputs[1].value).toBe('2024-06-25')
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
test('handles label property correctly', () => {
|
|
111
|
+
createDatepickerTest({ label: 'Custom Label' })
|
|
112
|
+
|
|
113
|
+
expect(screen.getByText('Custom Label')).toBeInTheDocument()
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
test('handles maxlength property correctly', () => {
|
|
117
|
+
const { container } = createDatepickerTest({
|
|
118
|
+
multiple: true,
|
|
119
|
+
maxlength: 5,
|
|
120
|
+
value: ['2024-06-15', '2024-06-20'],
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
const counter = container.querySelector('.pkt-input__counter')
|
|
124
|
+
expect(counter).toBeInTheDocument()
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
test('handles disabled property correctly', () => {
|
|
128
|
+
const { container } = createDatepickerTest({
|
|
129
|
+
disabled: true,
|
|
130
|
+
})
|
|
131
|
+
|
|
132
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
133
|
+
expect(input).toBeDisabled()
|
|
134
|
+
|
|
135
|
+
const button = container.querySelector('button[type="button"]')
|
|
136
|
+
expect(button).toBeDisabled()
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('handles required attribute', () => {
|
|
140
|
+
const { container } = createDatepickerTest({
|
|
141
|
+
required: true,
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
145
|
+
expect(input).toBeRequired()
|
|
146
|
+
})
|
|
147
|
+
|
|
148
|
+
test('sets min and max date boundaries on input', () => {
|
|
149
|
+
const { container } = createDatepickerTest({
|
|
150
|
+
min: '2024-01-01',
|
|
151
|
+
max: '2024-12-31',
|
|
152
|
+
value: '2024-06-15',
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
156
|
+
expect(input).toHaveAttribute('min', '2024-01-01')
|
|
157
|
+
expect(input).toHaveAttribute('max', '2024-12-31')
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
test('sets min and max on both range inputs', () => {
|
|
161
|
+
const { container } = createDatepickerTest({
|
|
162
|
+
range: true,
|
|
163
|
+
min: '2024-01-01',
|
|
164
|
+
max: '2024-12-31',
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
const inputs = container.querySelectorAll('input[type="date"]') as NodeListOf<HTMLInputElement>
|
|
168
|
+
expect(inputs[0]).toHaveAttribute('min', '2024-01-01')
|
|
169
|
+
expect(inputs[0]).toHaveAttribute('max', '2024-12-31')
|
|
170
|
+
expect(inputs[1]).toHaveAttribute('min', '2024-01-01')
|
|
171
|
+
expect(inputs[1]).toHaveAttribute('max', '2024-12-31')
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
test('renders placeholder text correctly', () => {
|
|
175
|
+
const { container } = createDatepickerTest({
|
|
176
|
+
placeholder: 'dd.mm.yyyy',
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
180
|
+
expect(input).toHaveAttribute('placeholder', 'dd.mm.yyyy')
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test('renders with custom className', () => {
|
|
184
|
+
const { container } = createDatepickerTest({
|
|
185
|
+
className: 'my-custom-class',
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
const wrapper = container.querySelector('.pkt-datepicker')
|
|
189
|
+
expect(wrapper).toHaveClass('my-custom-class')
|
|
190
|
+
})
|
|
191
|
+
|
|
192
|
+
test('uses id for input name when no name specified', () => {
|
|
193
|
+
const { container } = createDatepickerTest()
|
|
194
|
+
|
|
195
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
196
|
+
expect(input).toHaveAttribute('name', datePickerId)
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
test('uses custom name when specified', () => {
|
|
200
|
+
const { container } = createDatepickerTest({
|
|
201
|
+
name: 'dateField',
|
|
202
|
+
value: '2024-06-15',
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
const hiddenInput = container.querySelector('input[type="hidden"]') as HTMLInputElement
|
|
206
|
+
expect(hiddenInput).toHaveAttribute('name', 'dateField')
|
|
207
|
+
expect(hiddenInput.value).toBe('2024-06-15')
|
|
208
|
+
})
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
describe('Calendar integration', () => {
|
|
212
|
+
test('renders calendar popup hidden by default', () => {
|
|
213
|
+
const { container } = createDatepickerTest()
|
|
214
|
+
|
|
215
|
+
const popup = container.querySelector('.pkt-calendar-popup')
|
|
216
|
+
expect(popup).toHaveAttribute('hidden')
|
|
217
|
+
expect(popup).toHaveClass('hide')
|
|
218
|
+
})
|
|
219
|
+
|
|
220
|
+
test('renders calendar inside popup', () => {
|
|
221
|
+
const { container } = createDatepickerTest({ calendarOpen: true })
|
|
222
|
+
|
|
223
|
+
const calendar = container.querySelector('.pkt-calendar')
|
|
224
|
+
expect(calendar).toBeInTheDocument()
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
test('passes min/max to calendar component', () => {
|
|
228
|
+
const { container } = createDatepickerTest({
|
|
229
|
+
calendarOpen: true,
|
|
230
|
+
min: '2024-01-01',
|
|
231
|
+
max: '2024-12-31',
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
const calendar = container.querySelector('.pkt-calendar') as HTMLElement
|
|
235
|
+
expect(calendar).toBeInTheDocument()
|
|
236
|
+
|
|
237
|
+
const input = container.querySelector('input[type="date"]') as HTMLInputElement
|
|
238
|
+
expect(input).toHaveAttribute('min', '2024-01-01')
|
|
239
|
+
expect(input).toHaveAttribute('max', '2024-12-31')
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
test('passes weeknumbers to calendar', () => {
|
|
243
|
+
const { container } = createDatepickerTest({
|
|
244
|
+
calendarOpen: true,
|
|
245
|
+
weeknumbers: true,
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
const calendar = container.querySelector('.pkt-calendar') as HTMLElement
|
|
249
|
+
expect(calendar).toBeInTheDocument()
|
|
250
|
+
})
|
|
251
|
+
|
|
252
|
+
test('passes multiple and range to calendar', () => {
|
|
253
|
+
const { container: multipleContainer } = createDatepickerTest({
|
|
254
|
+
calendarOpen: true,
|
|
255
|
+
multiple: true,
|
|
256
|
+
})
|
|
257
|
+
|
|
258
|
+
const multiCal = multipleContainer.querySelector('.pkt-calendar') as HTMLElement
|
|
259
|
+
expect(multiCal).toBeInTheDocument()
|
|
260
|
+
|
|
261
|
+
const { container: rangeContainer } = createDatepickerTest({
|
|
262
|
+
calendarOpen: true,
|
|
263
|
+
range: true,
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
const rangeCal = rangeContainer.querySelector('.pkt-calendar') as HTMLElement
|
|
267
|
+
expect(rangeCal).toBeInTheDocument()
|
|
268
|
+
})
|
|
269
|
+
})
|
|
270
|
+
})
|