@oslokommune/punkt-react 14.5.4 → 15.0.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.
- package/CHANGELOG.md +51 -0
- package/dist/index.d.ts +64 -21
- package/dist/punkt-react.es.js +7202 -4676
- 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 +69 -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 +142 -0
- package/src/components/datepicker/useDatepickerState.ts +514 -0
- package/src/components/index.ts +1 -0
- package/src/components/tag/Tag.tsx +1 -1
- package/src/components/datepicker/Datepicker.test.tsx +0 -395
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oslokommune/punkt-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "15.0.1",
|
|
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",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@lit-labs/ssr-dom-shim": "^1.2.1",
|
|
41
41
|
"@lit/react": "^1.0.7",
|
|
42
|
-
"@oslokommune/punkt-elements": "^
|
|
42
|
+
"@oslokommune/punkt-elements": "^15.0.1",
|
|
43
43
|
"classnames": "^2.5.1",
|
|
44
44
|
"prettier": "^3.3.3",
|
|
45
45
|
"react-hook-form": "^7.53.0"
|
|
@@ -49,8 +49,8 @@
|
|
|
49
49
|
"@eslint/compat": "^2.0.2",
|
|
50
50
|
"@eslint/eslintrc": "^3.3.3",
|
|
51
51
|
"@eslint/js": "^9.37.0",
|
|
52
|
-
"@oslokommune/punkt-assets": "^
|
|
53
|
-
"@oslokommune/punkt-css": "^
|
|
52
|
+
"@oslokommune/punkt-assets": "^15.0.0",
|
|
53
|
+
"@oslokommune/punkt-css": "^15.0.1",
|
|
54
54
|
"@testing-library/jest-dom": "^6.5.0",
|
|
55
55
|
"@testing-library/react": "^16.0.1",
|
|
56
56
|
"@testing-library/user-event": "^14.5.2",
|
|
@@ -109,5 +109,5 @@
|
|
|
109
109
|
"url": "https://github.com/oslokommune/punkt/issues"
|
|
110
110
|
},
|
|
111
111
|
"license": "MIT",
|
|
112
|
-
"gitHead": "
|
|
112
|
+
"gitHead": "e4a75d4efe0a17688bdba0071e12cb789ae430c2"
|
|
113
113
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { render, fireEvent } from '@testing-library/react'
|
|
3
|
+
import { axe, toHaveNoViolations } from 'jest-axe'
|
|
4
|
+
|
|
5
|
+
import { PktCalendar, IPktCalendar } from './Calendar'
|
|
6
|
+
|
|
7
|
+
expect.extend(toHaveNoViolations)
|
|
8
|
+
|
|
9
|
+
const createCalendar = (props: Partial<IPktCalendar> = {}) => {
|
|
10
|
+
return render(<PktCalendar {...props} />)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe('PktCalendar', () => {
|
|
14
|
+
describe('Accessibility', () => {
|
|
15
|
+
test('has no accessibility violations', async () => {
|
|
16
|
+
const { container } = createCalendar({ withcontrols: true, weeknumbers: true })
|
|
17
|
+
|
|
18
|
+
const results = await axe(container)
|
|
19
|
+
expect(results).toHaveNoViolations()
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('has proper ARIA attributes', () => {
|
|
23
|
+
const { container } = createCalendar({ withcontrols: true })
|
|
24
|
+
|
|
25
|
+
const calendarTable = container.querySelector('.pkt-calendar__body')
|
|
26
|
+
expect(calendarTable).toHaveAttribute('role', 'grid')
|
|
27
|
+
|
|
28
|
+
const dateButtons = container.querySelectorAll('.pkt-calendar__date')
|
|
29
|
+
dateButtons.forEach((button) => {
|
|
30
|
+
expect(button).toHaveAttribute('aria-pressed')
|
|
31
|
+
expect(button).toHaveAttribute('tabindex')
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('navigation buttons have proper labels', () => {
|
|
36
|
+
const { container } = createCalendar({ withcontrols: true })
|
|
37
|
+
|
|
38
|
+
const prevButton = container.querySelector('.pkt-calendar__prev-month')
|
|
39
|
+
const nextButton = container.querySelector('.pkt-calendar__next-month')
|
|
40
|
+
|
|
41
|
+
expect(prevButton).toHaveAttribute('aria-label')
|
|
42
|
+
expect(nextButton).toHaveAttribute('aria-label')
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
test('date buttons have proper accessible names', () => {
|
|
46
|
+
const { container } = createCalendar()
|
|
47
|
+
|
|
48
|
+
const dateButtons = container.querySelectorAll('.pkt-calendar__date')
|
|
49
|
+
dateButtons.forEach((button) => {
|
|
50
|
+
expect(button).toHaveAttribute('aria-label')
|
|
51
|
+
expect(button.getAttribute('aria-label')).toBeTruthy()
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('selected dates have proper ARIA state', () => {
|
|
56
|
+
const { container } = createCalendar()
|
|
57
|
+
|
|
58
|
+
const firstDate = container.querySelector(
|
|
59
|
+
'.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
|
|
60
|
+
)
|
|
61
|
+
fireEvent.click(firstDate!)
|
|
62
|
+
|
|
63
|
+
expect(firstDate).toHaveAttribute('aria-pressed', 'true')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
test('disabled dates have proper ARIA state', () => {
|
|
67
|
+
const { container } = createCalendar({ excludeweekdays: ['0', '6'] })
|
|
68
|
+
|
|
69
|
+
const disabledDates = container.querySelectorAll('.pkt-calendar__date--disabled')
|
|
70
|
+
disabledDates.forEach((date) => {
|
|
71
|
+
expect(date).toHaveAttribute('disabled')
|
|
72
|
+
})
|
|
73
|
+
})
|
|
74
|
+
})
|
|
75
|
+
})
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { render } from '@testing-library/react'
|
|
3
|
+
|
|
4
|
+
import { PktCalendar, IPktCalendar } from './Calendar'
|
|
5
|
+
|
|
6
|
+
const createCalendar = (props: Partial<IPktCalendar> = {}) => {
|
|
7
|
+
return render(<PktCalendar {...props} />)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('PktCalendar', () => {
|
|
11
|
+
describe('Date range functionality', () => {
|
|
12
|
+
test('handles earliest date property correctly', () => {
|
|
13
|
+
const { container } = createCalendar({
|
|
14
|
+
earliest: '2024-01-15',
|
|
15
|
+
currentmonth: '2024-01-01',
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
// Check that dates before earliest are disabled (dates 1-14 should be disabled)
|
|
19
|
+
const disabledDates = container.querySelectorAll('.pkt-calendar__date--disabled')
|
|
20
|
+
expect(disabledDates.length).toBeGreaterThan(0)
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
test('handles latest date property correctly', () => {
|
|
24
|
+
const { container } = createCalendar({ latest: '2024-12-15' })
|
|
25
|
+
|
|
26
|
+
// Should render without errors
|
|
27
|
+
expect(container.querySelector('.pkt-calendar')).toBeInTheDocument()
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('restricts selectable dates between earliest and latest', () => {
|
|
31
|
+
const { container } = createCalendar({
|
|
32
|
+
earliest: '2024-06-10',
|
|
33
|
+
latest: '2024-06-20',
|
|
34
|
+
currentmonth: '2024-06-01',
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
const enabledDates = container.querySelectorAll(
|
|
38
|
+
'.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
|
|
39
|
+
)
|
|
40
|
+
const disabledDates = container.querySelectorAll('.pkt-calendar__date--disabled')
|
|
41
|
+
|
|
42
|
+
expect(enabledDates.length).toBeGreaterThan(0)
|
|
43
|
+
expect(disabledDates.length).toBeGreaterThan(0)
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
describe('Date exclusion functionality', () => {
|
|
48
|
+
test('excludes specific dates correctly', () => {
|
|
49
|
+
const { container } = createCalendar({
|
|
50
|
+
excludedates: ['2024-06-15', '2024-06-20'],
|
|
51
|
+
currentmonth: '2024-06-01',
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
// The excluded dates should be disabled
|
|
55
|
+
const date15 = container.querySelector('button[data-date="2024-06-15"]')
|
|
56
|
+
const date20 = container.querySelector('button[data-date="2024-06-20"]')
|
|
57
|
+
expect(date15).toHaveClass('pkt-calendar__date--disabled')
|
|
58
|
+
expect(date20).toHaveClass('pkt-calendar__date--disabled')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('excludes weekdays correctly', () => {
|
|
62
|
+
const { container } = createCalendar({ excludeweekdays: ['0', '6'] })
|
|
63
|
+
|
|
64
|
+
// Check that weekend dates are disabled
|
|
65
|
+
const disabledDates = container.querySelectorAll('.pkt-calendar__date--disabled')
|
|
66
|
+
expect(disabledDates.length).toBeGreaterThan(0)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('excludes both specific dates and weekdays', () => {
|
|
70
|
+
const { container } = createCalendar({
|
|
71
|
+
excludedates: ['2024-06-15'],
|
|
72
|
+
excludeweekdays: ['0', '6'],
|
|
73
|
+
currentmonth: '2024-06-01',
|
|
74
|
+
})
|
|
75
|
+
|
|
76
|
+
// Both specific dates and weekends should be disabled
|
|
77
|
+
const disabledDates = container.querySelectorAll('.pkt-calendar__date--disabled')
|
|
78
|
+
expect(disabledDates.length).toBeGreaterThan(0)
|
|
79
|
+
|
|
80
|
+
const date15 = container.querySelector('button[data-date="2024-06-15"]')
|
|
81
|
+
expect(date15).toHaveClass('pkt-calendar__date--disabled')
|
|
82
|
+
})
|
|
83
|
+
})
|
|
84
|
+
})
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { render, fireEvent } from '@testing-library/react'
|
|
3
|
+
import { parseISODateString } from 'shared-utils/date-utils'
|
|
4
|
+
|
|
5
|
+
import { PktCalendar, IPktCalendar } from './Calendar'
|
|
6
|
+
|
|
7
|
+
const createCalendar = (props: Partial<IPktCalendar> = {}) => {
|
|
8
|
+
return render(<PktCalendar {...props} />)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
describe('PktCalendar', () => {
|
|
12
|
+
describe('Rendering and basic functionality', () => {
|
|
13
|
+
test('renders without errors', () => {
|
|
14
|
+
const { container } = createCalendar()
|
|
15
|
+
|
|
16
|
+
const calendarElement = container.querySelector('.pkt-calendar')
|
|
17
|
+
expect(calendarElement).toBeInTheDocument()
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
test('renders with correct structure', () => {
|
|
21
|
+
const { container } = createCalendar()
|
|
22
|
+
|
|
23
|
+
const calendarElement = container.querySelector('.pkt-calendar')
|
|
24
|
+
const calendarTable = container.querySelector('.pkt-cal-days')
|
|
25
|
+
const monthNav = container.querySelector('.pkt-cal-month-nav')
|
|
26
|
+
|
|
27
|
+
expect(calendarElement).toBeInTheDocument()
|
|
28
|
+
expect(calendarTable).toBeInTheDocument()
|
|
29
|
+
expect(monthNav).toBeInTheDocument()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('renders month picker when withcontrols is true', () => {
|
|
33
|
+
const { container } = createCalendar({ withcontrols: true })
|
|
34
|
+
|
|
35
|
+
// Navigation buttons should always be present
|
|
36
|
+
const prevButton = container.querySelector('.pkt-calendar__prev-month')
|
|
37
|
+
const nextButton = container.querySelector('.pkt-calendar__next-month')
|
|
38
|
+
expect(prevButton).toBeInTheDocument()
|
|
39
|
+
expect(nextButton).toBeInTheDocument()
|
|
40
|
+
|
|
41
|
+
// Should show month picker controls, not static title
|
|
42
|
+
const monthPicker = container.querySelector('.pkt-cal-month-picker')
|
|
43
|
+
const monthTitle = container.querySelector('.pkt-calendar__month-title')
|
|
44
|
+
|
|
45
|
+
expect(monthPicker).toBeInTheDocument()
|
|
46
|
+
expect(monthTitle).not.toBeInTheDocument()
|
|
47
|
+
|
|
48
|
+
// Should have month and year inputs
|
|
49
|
+
const monthSelect = monthPicker?.querySelector('select')
|
|
50
|
+
const yearInput = monthPicker?.querySelector('input[type="number"]')
|
|
51
|
+
expect(monthSelect).toBeInTheDocument()
|
|
52
|
+
expect(yearInput).toBeInTheDocument()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('renders static month title when withcontrols is false', () => {
|
|
56
|
+
const { container } = createCalendar()
|
|
57
|
+
|
|
58
|
+
// Navigation buttons should always be present
|
|
59
|
+
const prevButton = container.querySelector('.pkt-calendar__prev-month')
|
|
60
|
+
const nextButton = container.querySelector('.pkt-calendar__next-month')
|
|
61
|
+
expect(prevButton).toBeInTheDocument()
|
|
62
|
+
expect(nextButton).toBeInTheDocument()
|
|
63
|
+
|
|
64
|
+
// Should show static month title, not controls
|
|
65
|
+
const monthTitle = container.querySelector('.pkt-calendar__month-title')
|
|
66
|
+
const monthPicker = container.querySelector('.pkt-cal-month-picker')
|
|
67
|
+
|
|
68
|
+
expect(monthTitle).toBeInTheDocument()
|
|
69
|
+
expect(monthPicker).not.toBeInTheDocument()
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
describe('Properties and attributes', () => {
|
|
74
|
+
test('applies default properties correctly', () => {
|
|
75
|
+
const { container } = createCalendar()
|
|
76
|
+
|
|
77
|
+
// Verify defaults through DOM effects
|
|
78
|
+
const grid = container.querySelector('[role="grid"]')
|
|
79
|
+
expect(grid).not.toHaveAttribute('aria-multiselectable')
|
|
80
|
+
|
|
81
|
+
// No week numbers by default
|
|
82
|
+
const weekNumbers = container.querySelectorAll('.pkt-calendar__week-number')
|
|
83
|
+
expect(weekNumbers.length).toBe(0)
|
|
84
|
+
|
|
85
|
+
// No month picker by default
|
|
86
|
+
const monthPicker = container.querySelector('.pkt-cal-month-picker')
|
|
87
|
+
expect(monthPicker).not.toBeInTheDocument()
|
|
88
|
+
|
|
89
|
+
// No selected dates
|
|
90
|
+
const selectedDates = container.querySelectorAll('.pkt-calendar__date--selected')
|
|
91
|
+
expect(selectedDates.length).toBe(0)
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('handles multiple property correctly', () => {
|
|
95
|
+
const { container } = createCalendar({ multiple: true })
|
|
96
|
+
|
|
97
|
+
const grid = container.querySelector('[role="grid"]')
|
|
98
|
+
expect(grid).toHaveAttribute('aria-multiselectable', 'true')
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('handles range property correctly', () => {
|
|
102
|
+
const { container } = createCalendar({ range: true })
|
|
103
|
+
|
|
104
|
+
const grid = container.querySelector('[role="grid"]')
|
|
105
|
+
expect(grid).toHaveAttribute('aria-multiselectable', 'true')
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
test('handles weeknumbers property correctly', () => {
|
|
109
|
+
const { container } = createCalendar({ weeknumbers: true })
|
|
110
|
+
|
|
111
|
+
const weekNumbers = container.querySelectorAll('.pkt-calendar__week-number')
|
|
112
|
+
expect(weekNumbers.length).toBeGreaterThan(0)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('handles maxMultiple property correctly', () => {
|
|
116
|
+
const { container } = createCalendar({ multiple: true, maxMultiple: 3 })
|
|
117
|
+
|
|
118
|
+
const grid = container.querySelector('[role="grid"]')
|
|
119
|
+
expect(grid).toHaveAttribute('aria-multiselectable', 'true')
|
|
120
|
+
|
|
121
|
+
// Select 3 dates - all should work
|
|
122
|
+
const availableDates = container.querySelectorAll(
|
|
123
|
+
'.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
|
|
124
|
+
)
|
|
125
|
+
expect(availableDates.length).toBeGreaterThan(3)
|
|
126
|
+
|
|
127
|
+
fireEvent.click(availableDates[0])
|
|
128
|
+
fireEvent.click(availableDates[1])
|
|
129
|
+
fireEvent.click(availableDates[2])
|
|
130
|
+
|
|
131
|
+
const selectedDates = container.querySelectorAll('.pkt-calendar__date--selected')
|
|
132
|
+
expect(selectedDates.length).toBe(3)
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
test('handles currentmonth property correctly', () => {
|
|
136
|
+
const { container } = createCalendar({ currentmonth: '2024-03-15' })
|
|
137
|
+
|
|
138
|
+
const monthTitle = container.querySelector('.pkt-calendar__month-title')
|
|
139
|
+
expect(monthTitle?.textContent).toContain('Mars')
|
|
140
|
+
expect(monthTitle?.textContent).toContain('2024')
|
|
141
|
+
})
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
describe('Month navigation', () => {
|
|
145
|
+
it('should navigate to previous month when clicking previous button', () => {
|
|
146
|
+
const { container } = createCalendar({ currentmonth: '2024-06-15' })
|
|
147
|
+
|
|
148
|
+
const monthTitle = container.querySelector('.pkt-calendar__month-title')
|
|
149
|
+
expect(monthTitle?.textContent).toContain('Juni')
|
|
150
|
+
|
|
151
|
+
const prevButton = container.querySelector('.pkt-calendar__prev-month')
|
|
152
|
+
fireEvent.click(prevButton!)
|
|
153
|
+
|
|
154
|
+
expect(monthTitle?.textContent).toContain('Mai')
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
it('should navigate to next month when clicking next button', () => {
|
|
158
|
+
const { container } = createCalendar({ currentmonth: '2024-06-15' })
|
|
159
|
+
|
|
160
|
+
const monthTitle = container.querySelector('.pkt-calendar__month-title')
|
|
161
|
+
expect(monthTitle?.textContent).toContain('Juni')
|
|
162
|
+
|
|
163
|
+
const nextButton = container.querySelector('.pkt-calendar__next-month')
|
|
164
|
+
fireEvent.click(nextButton!)
|
|
165
|
+
|
|
166
|
+
expect(monthTitle?.textContent).toContain('Juli')
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
test('navigates to next month with controls', () => {
|
|
170
|
+
const { container } = createCalendar({ withcontrols: true, currentmonth: '2024-06-15' })
|
|
171
|
+
|
|
172
|
+
const nextButton = container.querySelector('.pkt-calendar__next-month')
|
|
173
|
+
expect(nextButton).toBeInTheDocument()
|
|
174
|
+
|
|
175
|
+
const monthSelect = container.querySelector(
|
|
176
|
+
'.pkt-cal-month-picker select',
|
|
177
|
+
) as HTMLSelectElement
|
|
178
|
+
const initialMonth = monthSelect?.value
|
|
179
|
+
|
|
180
|
+
fireEvent.click(nextButton!)
|
|
181
|
+
|
|
182
|
+
const newMonth = monthSelect?.value
|
|
183
|
+
expect(newMonth).not.toBe(initialMonth)
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
test('month navigation updates visible dates', () => {
|
|
187
|
+
const { container } = createCalendar({ withcontrols: true, currentmonth: '2024-06-15' })
|
|
188
|
+
|
|
189
|
+
const initialDates = Array.from(container.querySelectorAll('.pkt-calendar__date')).map(
|
|
190
|
+
(el) => el.textContent,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
const nextButton = container.querySelector('.pkt-calendar__next-month')
|
|
194
|
+
fireEvent.click(nextButton!)
|
|
195
|
+
|
|
196
|
+
const newDates = Array.from(container.querySelectorAll('.pkt-calendar__date')).map(
|
|
197
|
+
(el) => el.textContent,
|
|
198
|
+
)
|
|
199
|
+
expect(newDates).not.toEqual(initialDates)
|
|
200
|
+
})
|
|
201
|
+
})
|
|
202
|
+
|
|
203
|
+
describe('Date formatting and localization', () => {
|
|
204
|
+
test('displays day names correctly', () => {
|
|
205
|
+
const { container } = createCalendar()
|
|
206
|
+
|
|
207
|
+
const dayHeaders = container.querySelectorAll('.pkt-calendar__day-name')
|
|
208
|
+
expect(dayHeaders.length).toBe(7)
|
|
209
|
+
|
|
210
|
+
dayHeaders.forEach((header) => {
|
|
211
|
+
expect(header.textContent).toBeTruthy()
|
|
212
|
+
expect(header.textContent!.length).toBeGreaterThan(0)
|
|
213
|
+
})
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test('displays month names correctly', () => {
|
|
217
|
+
const { container } = createCalendar({ currentmonth: '2024-06-15' })
|
|
218
|
+
|
|
219
|
+
const monthTitle = container.querySelector('.pkt-calendar__month-title')
|
|
220
|
+
expect(monthTitle).toBeInTheDocument()
|
|
221
|
+
expect(monthTitle?.textContent).toBeTruthy()
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
test('handles custom day strings', () => {
|
|
225
|
+
const { container } = createCalendar({
|
|
226
|
+
strings: { daysShort: ['S', 'M', 'T', 'W', 'T', 'F', 'S'] },
|
|
227
|
+
})
|
|
228
|
+
|
|
229
|
+
const dayHeaders = container.querySelectorAll('.pkt-calendar__day-name')
|
|
230
|
+
expect(dayHeaders[0].textContent?.trim()).toBe('S')
|
|
231
|
+
expect(dayHeaders[1].textContent?.trim()).toBe('M')
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
test('handles custom month strings', () => {
|
|
235
|
+
const { container } = createCalendar({
|
|
236
|
+
withcontrols: true,
|
|
237
|
+
strings: {
|
|
238
|
+
months: [
|
|
239
|
+
'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
|
240
|
+
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec',
|
|
241
|
+
],
|
|
242
|
+
},
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
const monthSelect = container.querySelector('.pkt-cal-month-picker select')
|
|
246
|
+
expect(monthSelect).toBeInTheDocument()
|
|
247
|
+
|
|
248
|
+
const options = monthSelect?.querySelectorAll('option')
|
|
249
|
+
expect(options?.[0]?.textContent).toContain('Jan')
|
|
250
|
+
})
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
describe('Today highlighting', () => {
|
|
254
|
+
test('highlights today date', () => {
|
|
255
|
+
const { container } = createCalendar()
|
|
256
|
+
|
|
257
|
+
const todayDate = container.querySelector('.pkt-calendar__date--today')
|
|
258
|
+
expect(todayDate).toBeInTheDocument()
|
|
259
|
+
})
|
|
260
|
+
|
|
261
|
+
test('today date is selectable unless excluded', () => {
|
|
262
|
+
const { container } = createCalendar()
|
|
263
|
+
|
|
264
|
+
const todayDate = container.querySelector('.pkt-calendar__date--today')
|
|
265
|
+
|
|
266
|
+
if (todayDate && !todayDate.classList.contains('pkt-calendar__date--disabled')) {
|
|
267
|
+
fireEvent.click(todayDate)
|
|
268
|
+
expect(todayDate).toHaveClass('pkt-calendar__date--selected')
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
})
|
|
272
|
+
})
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { render, fireEvent, act } from '@testing-library/react'
|
|
3
|
+
|
|
4
|
+
import { PktCalendar, IPktCalendar } from './Calendar'
|
|
5
|
+
|
|
6
|
+
const createCalendar = (props: Partial<IPktCalendar> = {}) => {
|
|
7
|
+
return render(<PktCalendar {...props} />)
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
describe('PktCalendar', () => {
|
|
11
|
+
describe('Keyboard navigation', () => {
|
|
12
|
+
test('handles arrow key navigation', () => {
|
|
13
|
+
const { container } = createCalendar()
|
|
14
|
+
|
|
15
|
+
const firstDate = container.querySelector(
|
|
16
|
+
'.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
|
|
17
|
+
) as HTMLElement
|
|
18
|
+
|
|
19
|
+
act(() => {
|
|
20
|
+
firstDate?.focus()
|
|
21
|
+
fireEvent.keyDown(firstDate!, { key: 'ArrowRight' })
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
// Check that focus moved (implementation dependent)
|
|
25
|
+
expect(document.activeElement).toBeTruthy()
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
test('handles enter key for date selection', () => {
|
|
29
|
+
const { container } = createCalendar()
|
|
30
|
+
|
|
31
|
+
const firstDate = container.querySelector(
|
|
32
|
+
'.pkt-calendar__date:not(.pkt-calendar__date--disabled)',
|
|
33
|
+
) as HTMLElement
|
|
34
|
+
|
|
35
|
+
act(() => {
|
|
36
|
+
firstDate?.focus()
|
|
37
|
+
fireEvent.keyDown(firstDate!, { key: 'Enter' })
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
expect(firstDate).toHaveClass('pkt-calendar__date--selected')
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('handles escape key', () => {
|
|
44
|
+
const onClose = jest.fn()
|
|
45
|
+
const { container } = createCalendar({ onClose })
|
|
46
|
+
|
|
47
|
+
const calendarEl = container.querySelector('.pkt-calendar') as HTMLElement
|
|
48
|
+
fireEvent.keyDown(calendarEl, { key: 'Escape' })
|
|
49
|
+
|
|
50
|
+
// Should trigger close callback
|
|
51
|
+
expect(onClose).toHaveBeenCalled()
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
describe('Edge cases and error handling', () => {
|
|
56
|
+
test('handles invalid date formats gracefully', () => {
|
|
57
|
+
const { container } = createCalendar({ earliest: 'invalid-date', latest: 'also-invalid' })
|
|
58
|
+
|
|
59
|
+
// Should not crash and render normally
|
|
60
|
+
expect(container.querySelector('.pkt-calendar')).toBeInTheDocument()
|
|
61
|
+
const calendarBody = container.querySelector('.pkt-calendar__body')
|
|
62
|
+
expect(calendarBody).toBeInTheDocument()
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('handles empty selected dates array', () => {
|
|
66
|
+
const { container } = createCalendar({ selected: [] })
|
|
67
|
+
|
|
68
|
+
const selectedDates = container.querySelectorAll('.pkt-calendar__date--selected')
|
|
69
|
+
expect(selectedDates.length).toBe(0)
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
test('handles conflicting properties gracefully', () => {
|
|
73
|
+
// Both multiple and range shouldn't typically be used together
|
|
74
|
+
const { container } = createCalendar({ multiple: true, range: true })
|
|
75
|
+
|
|
76
|
+
// Should still render without errors
|
|
77
|
+
expect(container.querySelector('.pkt-calendar')).toBeInTheDocument()
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('handles very early dates', () => {
|
|
81
|
+
const { container } = createCalendar({ currentmonth: '1900-01-01' })
|
|
82
|
+
|
|
83
|
+
expect(container.querySelector('.pkt-calendar')).toBeInTheDocument()
|
|
84
|
+
const calendarBody = container.querySelector('.pkt-calendar__body')
|
|
85
|
+
expect(calendarBody).toBeInTheDocument()
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('handles far future dates', () => {
|
|
89
|
+
const { container } = createCalendar({ currentmonth: '2100-12-31' })
|
|
90
|
+
|
|
91
|
+
expect(container.querySelector('.pkt-calendar')).toBeInTheDocument()
|
|
92
|
+
const calendarBody = container.querySelector('.pkt-calendar__body')
|
|
93
|
+
expect(calendarBody).toBeInTheDocument()
|
|
94
|
+
})
|
|
95
|
+
})
|
|
96
|
+
})
|