@transferwise/components 46.156.0 → 46.157.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/build/Calendar/Calendar.constants.js +10 -0
- package/build/Calendar/Calendar.constants.js.map +1 -0
- package/build/Calendar/Calendar.constants.mjs +8 -0
- package/build/Calendar/Calendar.constants.mjs.map +1 -0
- package/build/Calendar/Calendar.js +121 -17
- package/build/Calendar/Calendar.js.map +1 -1
- package/build/Calendar/Calendar.messages.js +12 -0
- package/build/Calendar/Calendar.messages.js.map +1 -1
- package/build/Calendar/Calendar.messages.mjs +12 -0
- package/build/Calendar/Calendar.messages.mjs.map +1 -1
- package/build/Calendar/Calendar.mjs +122 -18
- package/build/Calendar/Calendar.mjs.map +1 -1
- package/build/Calendar/DateHeader/DateHeader.js +5 -1
- package/build/Calendar/DateHeader/DateHeader.js.map +1 -1
- package/build/Calendar/DateHeader/DateHeader.mjs +5 -1
- package/build/Calendar/DateHeader/DateHeader.mjs.map +1 -1
- package/build/Calendar/DayCalendar/DayCalendar.js +20 -2
- package/build/Calendar/DayCalendar/DayCalendar.js.map +1 -1
- package/build/Calendar/DayCalendar/DayCalendar.mjs +20 -2
- package/build/Calendar/DayCalendar/DayCalendar.mjs.map +1 -1
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.js +60 -19
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.js.map +1 -1
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.mjs +60 -19
- package/build/Calendar/DayCalendar/Table/DayCalendarTable.mjs.map +1 -1
- package/build/Calendar/MonthCalendar/MonthCalendar.js +6 -0
- package/build/Calendar/MonthCalendar/MonthCalendar.js.map +1 -1
- package/build/Calendar/MonthCalendar/MonthCalendar.mjs +6 -0
- package/build/Calendar/MonthCalendar/MonthCalendar.mjs.map +1 -1
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.js +8 -3
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.js.map +1 -1
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.mjs +8 -3
- package/build/Calendar/MonthCalendar/Table/MonthCalendarTable.mjs.map +1 -1
- package/build/Calendar/TableLink/TableLink.js +95 -0
- package/build/Calendar/TableLink/TableLink.js.map +1 -0
- package/build/Calendar/TableLink/TableLink.mjs +91 -0
- package/build/Calendar/TableLink/TableLink.mjs.map +1 -0
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.js +8 -3
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.js.map +1 -1
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.mjs +8 -3
- package/build/Calendar/YearCalendar/Table/YearCalendarTable.mjs.map +1 -1
- package/build/Calendar/YearCalendar/YearCalendar.js +7 -0
- package/build/Calendar/YearCalendar/YearCalendar.js.map +1 -1
- package/build/Calendar/YearCalendar/YearCalendar.mjs +7 -0
- package/build/Calendar/YearCalendar/YearCalendar.mjs.map +1 -1
- package/build/Calendar/_helpers/clampToDayViewBoundary/clampToDayViewBoundary.js +24 -0
- package/build/Calendar/_helpers/clampToDayViewBoundary/clampToDayViewBoundary.js.map +1 -0
- package/build/Calendar/_helpers/clampToDayViewBoundary/clampToDayViewBoundary.mjs +22 -0
- package/build/Calendar/_helpers/clampToDayViewBoundary/clampToDayViewBoundary.mjs.map +1 -0
- package/build/Calendar/_helpers/hasDisabledDatesInMonth/hasDisabledDatesInMonth.js +33 -0
- package/build/Calendar/_helpers/hasDisabledDatesInMonth/hasDisabledDatesInMonth.js.map +1 -0
- package/build/Calendar/_helpers/hasDisabledDatesInMonth/hasDisabledDatesInMonth.mjs +31 -0
- package/build/Calendar/_helpers/hasDisabledDatesInMonth/hasDisabledDatesInMonth.mjs.map +1 -0
- package/build/Calendar/_helpers/isDateDisabled/isDateDisabled.js +40 -0
- package/build/Calendar/_helpers/isDateDisabled/isDateDisabled.js.map +1 -0
- package/build/Calendar/_helpers/isDateDisabled/isDateDisabled.mjs +38 -0
- package/build/Calendar/_helpers/isDateDisabled/isDateDisabled.mjs.map +1 -0
- package/build/DateLookup/DateLookup.js +20 -9
- package/build/DateLookup/DateLookup.js.map +1 -1
- package/build/DateLookup/DateLookup.mjs +20 -9
- package/build/DateLookup/DateLookup.mjs.map +1 -1
- package/build/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.js +3 -5
- package/build/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.js.map +1 -1
- package/build/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.mjs +3 -5
- package/build/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.mjs.map +1 -1
- package/build/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.js.map +1 -1
- package/build/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.mjs.map +1 -1
- package/build/Typeahead/Typeahead.js +53 -27
- package/build/Typeahead/Typeahead.js.map +1 -1
- package/build/Typeahead/Typeahead.messages.js +3 -0
- package/build/Typeahead/Typeahead.messages.js.map +1 -1
- package/build/Typeahead/Typeahead.messages.mjs +3 -0
- package/build/Typeahead/Typeahead.messages.mjs.map +1 -1
- package/build/Typeahead/Typeahead.mjs +53 -27
- package/build/Typeahead/Typeahead.mjs.map +1 -1
- package/build/Typeahead/TypeaheadInput/TypeaheadInput.js +2 -2
- package/build/Typeahead/TypeaheadInput/TypeaheadInput.js.map +1 -1
- package/build/Typeahead/TypeaheadInput/TypeaheadInput.mjs +2 -2
- package/build/Typeahead/TypeaheadInput/TypeaheadInput.mjs.map +1 -1
- package/build/Typeahead/TypeaheadOption/TypeaheadOption.js +3 -1
- package/build/Typeahead/TypeaheadOption/TypeaheadOption.js.map +1 -1
- package/build/Typeahead/TypeaheadOption/TypeaheadOption.mjs +3 -1
- package/build/Typeahead/TypeaheadOption/TypeaheadOption.mjs.map +1 -1
- package/build/i18n/en.json +5 -0
- package/build/i18n/en.json.js +5 -0
- package/build/i18n/en.json.js.map +1 -1
- package/build/i18n/en.json.mjs +5 -0
- package/build/i18n/en.json.mjs.map +1 -1
- package/build/index.js +2 -2
- package/build/index.mjs +1 -1
- package/build/main.css +147 -43
- package/build/styles/Calendar/Calendar.css +112 -37
- package/build/styles/Calendar/TableLink/TableLink.css +115 -0
- package/build/styles/Typeahead/Typeahead.css +14 -0
- package/build/styles/Typeahead/TypeaheadOption/TypeaheadOption.css +5 -0
- package/build/styles/main.css +147 -43
- package/build/types/Body/Body.d.ts +2 -2
- package/build/types/Calendar/Calendar.constants.d.ts +2 -0
- package/build/types/Calendar/Calendar.constants.d.ts.map +1 -0
- package/build/types/Calendar/Calendar.d.ts +16 -3
- package/build/types/Calendar/Calendar.d.ts.map +1 -1
- package/build/types/Calendar/Calendar.messages.d.ts +20 -0
- package/build/types/Calendar/Calendar.messages.d.ts.map +1 -1
- package/build/types/Calendar/Calendar.types.d.ts +32 -0
- package/build/types/Calendar/Calendar.types.d.ts.map +1 -0
- package/build/types/Calendar/DateHeader/DateHeader.d.ts +2 -0
- package/build/types/Calendar/DateHeader/DateHeader.d.ts.map +1 -1
- package/build/types/Calendar/DayCalendar/DayCalendar.d.ts +7 -1
- package/build/types/Calendar/DayCalendar/DayCalendar.d.ts.map +1 -1
- package/build/types/Calendar/DayCalendar/Table/DayCalendarTable.d.ts +6 -0
- package/build/types/Calendar/DayCalendar/Table/DayCalendarTable.d.ts.map +1 -1
- package/build/types/Calendar/MonthCalendar/MonthCalendar.d.ts +1 -0
- package/build/types/Calendar/MonthCalendar/MonthCalendar.d.ts.map +1 -1
- package/build/types/Calendar/MonthCalendar/Table/MonthCalendarTable.d.ts +2 -1
- package/build/types/Calendar/MonthCalendar/Table/MonthCalendarTable.d.ts.map +1 -1
- package/build/types/Calendar/{tableLink → TableLink}/TableLink.d.ts +5 -1
- package/build/types/Calendar/TableLink/TableLink.d.ts.map +1 -0
- package/build/types/Calendar/TableLink/index.d.ts.map +1 -0
- package/build/types/Calendar/YearCalendar/Table/YearCalendarTable.d.ts +2 -1
- package/build/types/Calendar/YearCalendar/Table/YearCalendarTable.d.ts.map +1 -1
- package/build/types/Calendar/YearCalendar/YearCalendar.d.ts +1 -0
- package/build/types/Calendar/YearCalendar/YearCalendar.d.ts.map +1 -1
- package/build/types/Calendar/_helpers/clampToDayViewBoundary/clampToDayViewBoundary.d.ts +8 -0
- package/build/types/Calendar/_helpers/clampToDayViewBoundary/clampToDayViewBoundary.d.ts.map +1 -0
- package/build/types/Calendar/_helpers/hasDisabledDatesInMonth/hasDisabledDatesInMonth.d.ts +3 -0
- package/build/types/Calendar/_helpers/hasDisabledDatesInMonth/hasDisabledDatesInMonth.d.ts.map +1 -0
- package/build/types/Calendar/_helpers/index.d.ts +4 -0
- package/build/types/Calendar/_helpers/index.d.ts.map +1 -0
- package/build/types/Calendar/_helpers/isDateDisabled/isDateDisabled.d.ts +3 -0
- package/build/types/Calendar/_helpers/isDateDisabled/isDateDisabled.d.ts.map +1 -0
- package/build/types/Calendar/index.d.ts +1 -0
- package/build/types/Calendar/index.d.ts.map +1 -1
- package/build/types/DateLookup/DateLookup.d.ts +24 -5
- package/build/types/DateLookup/DateLookup.d.ts.map +1 -1
- package/build/types/DateLookup/index.d.ts +1 -0
- package/build/types/DateLookup/index.d.ts.map +1 -1
- package/build/types/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.d.ts +6 -4
- package/build/types/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.d.ts.map +1 -1
- package/build/types/IconButton/IconButton.d.ts +1 -1
- package/build/types/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.d.ts +1 -1
- package/build/types/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.d.ts.map +1 -1
- package/build/types/MoneyInput/MoneyInput.d.ts +1 -1
- package/build/types/MoneyInput/MoneyInput.d.ts.map +1 -1
- package/build/types/Title/Title.d.ts +2 -2
- package/build/types/Typeahead/Typeahead.d.ts.map +1 -1
- package/build/types/Typeahead/Typeahead.messages.d.ts +5 -0
- package/build/types/Typeahead/Typeahead.messages.d.ts.map +1 -1
- package/build/types/Typeahead/TypeaheadInput/TypeaheadInput.d.ts.map +1 -1
- package/build/types/Typeahead/TypeaheadOption/TypeaheadOption.d.ts +1 -0
- package/build/types/Typeahead/TypeaheadOption/TypeaheadOption.d.ts.map +1 -1
- package/build/types/Upload/Steps/UploadImageStep/UploadImageStep.d.ts +1 -1
- package/build/types/UploadInput/UploadItem/UploadItemLink.d.ts +1 -1
- package/build/types/index.d.ts +3 -2
- package/build/types/index.d.ts.map +1 -1
- package/build/types/test-utils/index.d.ts +10 -0
- package/build/types/test-utils/index.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/Calendar/Calendar.constants.ts +5 -0
- package/src/Calendar/Calendar.css +112 -37
- package/src/Calendar/Calendar.disabledDates.test.tsx +610 -0
- package/src/Calendar/Calendar.less +5 -35
- package/src/Calendar/Calendar.messages.ts +21 -0
- package/src/Calendar/Calendar.test.tsx +342 -32
- package/src/Calendar/Calendar.tsx +169 -19
- package/src/Calendar/Calendar.types.ts +35 -0
- package/src/Calendar/DateHeader/DateHeader.tsx +6 -0
- package/src/Calendar/DayCalendar/DayCalendar.tsx +39 -1
- package/src/Calendar/DayCalendar/Table/DayCalendarTable.tsx +67 -27
- package/src/Calendar/MonthCalendar/MonthCalendar.tsx +8 -0
- package/src/Calendar/MonthCalendar/Table/MonthCalendarTable.tsx +15 -3
- package/src/Calendar/TableLink/TableLink.css +115 -0
- package/src/Calendar/TableLink/TableLink.less +68 -0
- package/src/Calendar/TableLink/TableLink.tsx +117 -0
- package/src/Calendar/YearCalendar/Table/YearCalendarTable.tsx +15 -3
- package/src/Calendar/YearCalendar/YearCalendar.tsx +9 -0
- package/src/Calendar/_helpers/clampToDayViewBoundary/clampToDayViewBoundary.test.ts +90 -0
- package/src/Calendar/_helpers/clampToDayViewBoundary/clampToDayViewBoundary.ts +25 -0
- package/src/Calendar/_helpers/hasDisabledDatesInMonth/hasDisabledDatesInMonth.test.ts +98 -0
- package/src/Calendar/_helpers/hasDisabledDatesInMonth/hasDisabledDatesInMonth.ts +45 -0
- package/src/Calendar/_helpers/index.ts +3 -0
- package/src/Calendar/_helpers/isDateDisabled/isDateDisabled.test.ts +169 -0
- package/src/Calendar/_helpers/isDateDisabled/isDateDisabled.ts +59 -0
- package/src/Calendar/_stories/Calendar.accessibility.docs.mdx +118 -0
- package/src/Calendar/_stories/Calendar.story.tsx +144 -4
- package/src/Calendar/_stories/Calendar.test.story.tsx +114 -0
- package/src/Calendar/index.ts +7 -0
- package/src/DateLookup/DateLookup.disabledDates.test.tsx +312 -0
- package/src/DateLookup/DateLookup.test.tsx +34 -37
- package/src/DateLookup/DateLookup.tsx +37 -13
- package/src/DateLookup/_stories/DateLookup.accessibility.docs.mdx +108 -0
- package/src/DateLookup/_stories/DateLookup.story.tsx +126 -2
- package/src/DateLookup/index.ts +6 -0
- package/src/ExpressiveMoneyInput/CurrencySelector/CurrencySelector.tsx +12 -13
- package/src/Inputs/SelectInput/SelectInput.test.tsx +37 -0
- package/src/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.tsx +1 -1
- package/src/Typeahead/Typeahead.css +14 -0
- package/src/Typeahead/Typeahead.less +8 -0
- package/src/Typeahead/Typeahead.messages.ts +5 -0
- package/src/Typeahead/Typeahead.test.tsx +80 -3
- package/src/Typeahead/Typeahead.tsx +50 -27
- package/src/Typeahead/TypeaheadInput/TypeaheadInput.tsx +2 -6
- package/src/Typeahead/TypeaheadOption/TypeaheadOption.css +5 -0
- package/src/Typeahead/TypeaheadOption/TypeaheadOption.less +9 -0
- package/src/Typeahead/TypeaheadOption/TypeaheadOption.tsx +4 -2
- package/src/i18n/en.json +4 -0
- package/src/index.ts +3 -2
- package/src/main.css +147 -43
- package/build/Calendar/tableLink/TableLink.js +0 -61
- package/build/Calendar/tableLink/TableLink.js.map +0 -1
- package/build/Calendar/tableLink/TableLink.mjs +0 -57
- package/build/Calendar/tableLink/TableLink.mjs.map +0 -1
- package/build/types/Calendar/tableLink/TableLink.d.ts.map +0 -1
- package/build/types/Calendar/tableLink/index.d.ts.map +0 -1
- package/src/Calendar/tableLink/TableLink.tsx +0 -77
- /package/build/types/Calendar/{tableLink → TableLink}/index.d.ts +0 -0
- /package/src/Calendar/{tableLink → TableLink}/index.ts +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { mockMatchMedia, render, screen, userEvent } from '../test-utils';
|
|
1
|
+
import { fireEvent, mockMatchMedia, render, screen, userEvent } from '../test-utils';
|
|
2
2
|
import Calendar from './Calendar';
|
|
3
|
+
import type { DisabledRules } from './Calendar.types';
|
|
3
4
|
|
|
4
5
|
mockMatchMedia();
|
|
5
6
|
|
|
@@ -15,35 +16,180 @@ describe('Calendar', () => {
|
|
|
15
16
|
['{ArrowUp}', -7],
|
|
16
17
|
['{ArrowDown}', +7],
|
|
17
18
|
])("handles '%s' to step %d day(s)", async (text: string, step: number) => {
|
|
18
|
-
const
|
|
19
|
-
render(<Calendar value={initialValue}
|
|
19
|
+
const handleDateFocus = jest.fn();
|
|
20
|
+
render(<Calendar value={initialValue} onDateFocus={handleDateFocus} />);
|
|
20
21
|
|
|
21
22
|
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
22
23
|
await user.keyboard(text);
|
|
23
24
|
|
|
24
25
|
const expected = new Date(initialValue);
|
|
25
26
|
expected.setDate(initialValue.getDate() + step);
|
|
26
|
-
expect(
|
|
27
|
+
expect(handleDateFocus).toHaveBeenCalledWith(expected, { isEnabled: true });
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
it('selects a day on click', async () => {
|
|
31
|
+
const handleDateFocus = jest.fn();
|
|
32
|
+
render(<Calendar value={initialValue} onDateFocus={handleDateFocus} />);
|
|
33
|
+
|
|
34
|
+
await user.click(screen.getByText('15'));
|
|
35
|
+
|
|
36
|
+
expect(handleDateFocus).toHaveBeenCalledWith(new Date(2000, 0, 15), { isEnabled: true });
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('fires both onDateFocus and onChange when clicking a day', async () => {
|
|
40
|
+
const handleDateFocus = jest.fn();
|
|
30
41
|
const handleChange = jest.fn();
|
|
31
|
-
render(
|
|
42
|
+
render(
|
|
43
|
+
<Calendar value={initialValue} onDateFocus={handleDateFocus} onChange={handleChange} />,
|
|
44
|
+
);
|
|
32
45
|
|
|
33
46
|
await user.click(screen.getByText('15'));
|
|
34
47
|
|
|
48
|
+
expect(handleDateFocus).toHaveBeenCalledWith(new Date(2000, 0, 15), { isEnabled: true });
|
|
35
49
|
expect(handleChange).toHaveBeenCalledWith(new Date(2000, 0, 15));
|
|
36
50
|
});
|
|
37
51
|
|
|
38
|
-
it('
|
|
52
|
+
it('does not fire onChange during arrow key navigation', async () => {
|
|
53
|
+
const handleChange = jest.fn();
|
|
54
|
+
render(<Calendar value={initialValue} onChange={handleChange} />);
|
|
55
|
+
|
|
56
|
+
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
57
|
+
expect(handleChange).toHaveBeenCalledTimes(1);
|
|
58
|
+
|
|
59
|
+
await user.keyboard('{ArrowRight}');
|
|
60
|
+
|
|
61
|
+
expect(handleChange).toHaveBeenCalledTimes(1);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('fires onChange when Enter is pressed on focused date', async () => {
|
|
39
65
|
const handleChange = jest.fn();
|
|
40
|
-
|
|
41
|
-
|
|
66
|
+
render(<Calendar value={initialValue} onChange={handleChange} />);
|
|
67
|
+
|
|
68
|
+
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
69
|
+
await user.keyboard('{ArrowRight}');
|
|
70
|
+
await user.keyboard('{Enter}');
|
|
71
|
+
|
|
72
|
+
const expected = new Date(initialValue);
|
|
73
|
+
expected.setDate(initialValue.getDate() + 1);
|
|
74
|
+
expect(handleChange).toHaveBeenCalledWith(expected);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('does not fire onChange when Enter is pressed on a disabled focused date', async () => {
|
|
78
|
+
const handleChange = jest.fn();
|
|
79
|
+
const disabledRules: DisabledRules = {
|
|
80
|
+
weekdays: ['sat', 'sun'],
|
|
81
|
+
reason: 'Closed on weekends.',
|
|
82
|
+
};
|
|
83
|
+
render(
|
|
84
|
+
<Calendar
|
|
85
|
+
value={new Date(2000, 0, 7)}
|
|
86
|
+
disabledRules={disabledRules}
|
|
87
|
+
onChange={handleChange}
|
|
88
|
+
/>,
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
92
|
+
// Click on selected day (Friday) fires onChange once
|
|
93
|
+
expect(handleChange).toHaveBeenCalledTimes(1);
|
|
94
|
+
|
|
95
|
+
// ArrowRight from Jan 7 (Friday) → Jan 8 (Saturday = disabled)
|
|
96
|
+
await user.keyboard('{ArrowRight}');
|
|
97
|
+
await user.keyboard('{Enter}');
|
|
98
|
+
|
|
99
|
+
expect(handleChange).toHaveBeenCalledTimes(1);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('fires onDateFocus with isEnabled: false when Enter is pressed on a disabled focused date', async () => {
|
|
103
|
+
const handleDateFocus = jest.fn();
|
|
104
|
+
const disabledRules: DisabledRules = {
|
|
105
|
+
weekdays: ['sat', 'sun'],
|
|
106
|
+
reason: 'Closed on weekends.',
|
|
107
|
+
};
|
|
108
|
+
render(
|
|
109
|
+
<Calendar
|
|
110
|
+
value={new Date(2000, 0, 7)}
|
|
111
|
+
disabledRules={disabledRules}
|
|
112
|
+
onDateFocus={handleDateFocus}
|
|
113
|
+
/>,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
117
|
+
handleDateFocus.mockClear();
|
|
118
|
+
|
|
119
|
+
// ArrowRight from Jan 7 (Friday) → Jan 8 (Saturday = disabled)
|
|
120
|
+
await user.keyboard('{ArrowRight}');
|
|
121
|
+
handleDateFocus.mockClear();
|
|
122
|
+
|
|
123
|
+
await user.keyboard('{Enter}');
|
|
124
|
+
|
|
125
|
+
expect(handleDateFocus).toHaveBeenCalledWith(new Date(2000, 0, 8), { isEnabled: false });
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('fires onChange when Space is pressed on a focused enabled date', async () => {
|
|
129
|
+
const handleChange = jest.fn();
|
|
130
|
+
render(<Calendar value={initialValue} onChange={handleChange} />);
|
|
131
|
+
|
|
132
|
+
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
133
|
+
await user.keyboard('{ArrowRight}');
|
|
134
|
+
await user.keyboard(' ');
|
|
135
|
+
|
|
136
|
+
const expected = new Date(initialValue);
|
|
137
|
+
expected.setDate(initialValue.getDate() + 1);
|
|
138
|
+
expect(handleChange).toHaveBeenCalledWith(expected);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('does not fire onChange when Space is pressed on a disabled focused date', async () => {
|
|
142
|
+
const handleChange = jest.fn();
|
|
143
|
+
const disabledRules: DisabledRules = {
|
|
144
|
+
weekdays: ['sat', 'sun'],
|
|
145
|
+
reason: 'Closed on weekends.',
|
|
146
|
+
};
|
|
147
|
+
render(
|
|
148
|
+
<Calendar
|
|
149
|
+
value={new Date(2000, 0, 7)}
|
|
150
|
+
disabledRules={disabledRules}
|
|
151
|
+
onChange={handleChange}
|
|
152
|
+
/>,
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
156
|
+
expect(handleChange).toHaveBeenCalledTimes(1);
|
|
157
|
+
|
|
158
|
+
// ArrowRight from Jan 7 (Friday) → Jan 8 (Saturday = disabled)
|
|
159
|
+
await user.keyboard('{ArrowRight}');
|
|
160
|
+
await user.keyboard(' ');
|
|
161
|
+
|
|
162
|
+
expect(handleChange).toHaveBeenCalledTimes(1);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('renders and remains interactive without onDateFocus', async () => {
|
|
166
|
+
const handleChange = jest.fn();
|
|
167
|
+
render(<Calendar value={initialValue} onChange={handleChange} />);
|
|
42
168
|
|
|
43
169
|
await user.click(screen.getByText('15'));
|
|
44
170
|
|
|
45
|
-
expect(
|
|
46
|
-
|
|
171
|
+
expect(handleChange).toHaveBeenCalledWith(new Date(2000, 0, 15));
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('fires onDateFocus when a cell receives DOM focus directly (screen reader navigation)', () => {
|
|
175
|
+
const handleDateFocus = jest.fn();
|
|
176
|
+
render(<Calendar value={initialValue} onDateFocus={handleDateFocus} />);
|
|
177
|
+
|
|
178
|
+
const cell = screen.getByLabelText(/^15 January 2000/u);
|
|
179
|
+
fireEvent.focus(cell);
|
|
180
|
+
|
|
181
|
+
expect(handleDateFocus).toHaveBeenCalledWith(new Date(2000, 0, 15), { isEnabled: true });
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it('does not fire onDateFocus when the already-focused cell receives focus', () => {
|
|
185
|
+
const handleDateFocus = jest.fn();
|
|
186
|
+
render(<Calendar value={initialValue} onDateFocus={handleDateFocus} />);
|
|
187
|
+
|
|
188
|
+
// Focus the selected date (which is already focusedDate in state)
|
|
189
|
+
const selected = screen.getByLabelText(/^1 January 2000.*selected/iu);
|
|
190
|
+
fireEvent.focus(selected);
|
|
191
|
+
|
|
192
|
+
expect(handleDateFocus).not.toHaveBeenCalled();
|
|
47
193
|
});
|
|
48
194
|
});
|
|
49
195
|
|
|
@@ -54,15 +200,15 @@ describe('Calendar', () => {
|
|
|
54
200
|
['{ArrowUp}', -4],
|
|
55
201
|
['{ArrowDown}', +4],
|
|
56
202
|
])("handles '%s' to step %d year(s)", async (text: string, step: number) => {
|
|
57
|
-
const
|
|
58
|
-
render(<Calendar value={initialValue}
|
|
203
|
+
const handleDateFocus = jest.fn();
|
|
204
|
+
render(<Calendar value={initialValue} onDateFocus={handleDateFocus} />);
|
|
59
205
|
|
|
60
206
|
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
61
207
|
await user.keyboard(text);
|
|
62
208
|
|
|
63
209
|
const expected = new Date(initialValue);
|
|
64
210
|
expected.setFullYear(initialValue.getFullYear() + step);
|
|
65
|
-
expect(
|
|
211
|
+
expect(handleDateFocus).toHaveBeenCalledWith(expected, { isEnabled: true });
|
|
66
212
|
});
|
|
67
213
|
});
|
|
68
214
|
|
|
@@ -73,8 +219,8 @@ describe('Calendar', () => {
|
|
|
73
219
|
['{ArrowUp}', -4],
|
|
74
220
|
['{ArrowDown}', +4],
|
|
75
221
|
])("handles '%s' to step %d month(s)", async (text: string, step: number) => {
|
|
76
|
-
const
|
|
77
|
-
render(<Calendar value={initialValue}
|
|
222
|
+
const handleDateFocus = jest.fn();
|
|
223
|
+
render(<Calendar value={initialValue} onDateFocus={handleDateFocus} />);
|
|
78
224
|
|
|
79
225
|
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
80
226
|
await user.keyboard(' ');
|
|
@@ -82,48 +228,194 @@ describe('Calendar', () => {
|
|
|
82
228
|
|
|
83
229
|
const expected = new Date(initialValue);
|
|
84
230
|
expected.setMonth(initialValue.getMonth() + step);
|
|
85
|
-
expect(
|
|
231
|
+
expect(handleDateFocus).toHaveBeenCalledWith(expected, { isEnabled: true });
|
|
86
232
|
});
|
|
87
233
|
});
|
|
88
234
|
|
|
89
235
|
describe('min/max clamping', () => {
|
|
90
|
-
it('
|
|
91
|
-
const min = new Date(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
render(<Calendar value={initialValue} min={min} onChange={handleChange} />);
|
|
236
|
+
it('allows keyboard navigation past min within the same month', async () => {
|
|
237
|
+
const min = new Date(2000, 0, 15);
|
|
238
|
+
const handleDateFocus = jest.fn();
|
|
239
|
+
render(<Calendar value={new Date(2000, 0, 16)} min={min} onDateFocus={handleDateFocus} />);
|
|
95
240
|
|
|
96
241
|
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
97
242
|
await user.keyboard('{ArrowLeft}{ArrowLeft}');
|
|
98
243
|
|
|
99
|
-
expect(
|
|
244
|
+
expect(handleDateFocus).toHaveBeenLastCalledWith(new Date(2000, 0, 14), {
|
|
245
|
+
isEnabled: false,
|
|
246
|
+
});
|
|
100
247
|
});
|
|
101
248
|
|
|
102
|
-
it('
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
249
|
+
it('clamps to first of month when navigating before min into a previous month', async () => {
|
|
250
|
+
const min = new Date(2000, 0, 2);
|
|
251
|
+
const handleDateFocus = jest.fn();
|
|
252
|
+
render(<Calendar value={new Date(2000, 0, 2)} min={min} onDateFocus={handleDateFocus} />);
|
|
253
|
+
|
|
254
|
+
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
255
|
+
await user.keyboard('{ArrowLeft}{ArrowLeft}');
|
|
256
|
+
|
|
257
|
+
expect(handleDateFocus).toHaveBeenLastCalledWith(new Date(2000, 0, 1), {
|
|
258
|
+
isEnabled: false,
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
it('allows keyboard navigation past max within the same month', async () => {
|
|
263
|
+
const max = new Date(2000, 0, 15);
|
|
264
|
+
const handleDateFocus = jest.fn();
|
|
265
|
+
render(<Calendar value={new Date(2000, 0, 14)} max={max} onDateFocus={handleDateFocus} />);
|
|
266
|
+
|
|
267
|
+
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
268
|
+
await user.keyboard('{ArrowRight}{ArrowRight}');
|
|
269
|
+
|
|
270
|
+
expect(handleDateFocus).toHaveBeenLastCalledWith(new Date(2000, 0, 16), {
|
|
271
|
+
isEnabled: false,
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
it('clamps to last of month when navigating past max into a next month', async () => {
|
|
276
|
+
const max = new Date(2000, 0, 30);
|
|
277
|
+
const handleDateFocus = jest.fn();
|
|
278
|
+
render(<Calendar value={new Date(2000, 0, 30)} max={max} onDateFocus={handleDateFocus} />);
|
|
107
279
|
|
|
108
280
|
await user.click(screen.getByRole('button', { name: /selected day/iu }));
|
|
109
281
|
await user.keyboard('{ArrowRight}{ArrowRight}');
|
|
110
282
|
|
|
111
|
-
expect(
|
|
283
|
+
expect(handleDateFocus).toHaveBeenLastCalledWith(new Date(2000, 0, 31), {
|
|
284
|
+
isEnabled: false,
|
|
285
|
+
});
|
|
112
286
|
});
|
|
113
287
|
|
|
114
288
|
it('clamps value to range when value is outside min/max', () => {
|
|
115
289
|
const min = new Date(2000, 0, 5);
|
|
116
290
|
const max = new Date(2000, 0, 20);
|
|
117
|
-
const
|
|
118
|
-
render(<Calendar value={initialValue} min={min} max={max}
|
|
291
|
+
const handleDateFocus = jest.fn();
|
|
292
|
+
render(<Calendar value={initialValue} min={min} max={max} onDateFocus={handleDateFocus} />);
|
|
119
293
|
|
|
120
|
-
expect(
|
|
294
|
+
expect(handleDateFocus).toHaveBeenCalledWith(min, { isEnabled: true });
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
describe('navigation button boundary disabling', () => {
|
|
299
|
+
describe('day view — previous/next month', () => {
|
|
300
|
+
it('disables previous month button when min is in the current view month', () => {
|
|
301
|
+
render(<Calendar value={new Date(2000, 3, 15)} min={new Date(2000, 3, 1)} />);
|
|
302
|
+
expect(screen.getByRole('button', { name: /previous month/iu })).toBeDisabled();
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
it('disables next month button when max is in the current view month', () => {
|
|
306
|
+
render(<Calendar value={new Date(2000, 3, 15)} max={new Date(2000, 3, 30)} />);
|
|
307
|
+
expect(screen.getByRole('button', { name: /next month/iu })).toBeDisabled();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it('does not disable month buttons when min/max are in adjacent months', () => {
|
|
311
|
+
render(
|
|
312
|
+
<Calendar
|
|
313
|
+
value={new Date(2000, 3, 15)}
|
|
314
|
+
min={new Date(2000, 2, 1)}
|
|
315
|
+
max={new Date(2000, 4, 30)}
|
|
316
|
+
/>,
|
|
317
|
+
);
|
|
318
|
+
expect(screen.getByRole('button', { name: /previous month/iu })).toBeEnabled();
|
|
319
|
+
expect(screen.getByRole('button', { name: /next month/iu })).toBeEnabled();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('does not disable month buttons when no min/max is set', () => {
|
|
323
|
+
render(<Calendar value={new Date(2000, 3, 15)} />);
|
|
324
|
+
expect(screen.getByRole('button', { name: /previous month/iu })).toBeEnabled();
|
|
325
|
+
expect(screen.getByRole('button', { name: /next month/iu })).toBeEnabled();
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('disables previous month button when min is in January and view is January', () => {
|
|
329
|
+
// Edge case: month wraps to previous year
|
|
330
|
+
render(<Calendar value={new Date(2000, 0, 15)} min={new Date(2000, 0, 1)} />);
|
|
331
|
+
expect(screen.getByRole('button', { name: /previous month/iu })).toBeDisabled();
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('disables next month button when max is in December and view is December', () => {
|
|
335
|
+
// Edge case: month wraps to next year
|
|
336
|
+
render(<Calendar value={new Date(2000, 11, 15)} max={new Date(2000, 11, 31)} />);
|
|
337
|
+
expect(screen.getByRole('button', { name: /next month/iu })).toBeDisabled();
|
|
338
|
+
});
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
describe('month view — previous/next year', () => {
|
|
342
|
+
it('disables previous year button when min year equals view year', async () => {
|
|
343
|
+
render(<Calendar value={new Date(2000, 3, 15)} min={new Date(2000, 0, 1)} />);
|
|
344
|
+
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
345
|
+
await user.click(screen.getByRole('button', { name: /2000, selected year/iu }));
|
|
346
|
+
expect(screen.getByRole('button', { name: /previous year/iu })).toBeDisabled();
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('disables next year button when max year equals view year', async () => {
|
|
350
|
+
render(<Calendar value={new Date(2000, 3, 15)} max={new Date(2000, 11, 31)} />);
|
|
351
|
+
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
352
|
+
await user.click(screen.getByRole('button', { name: /2000, selected year/iu }));
|
|
353
|
+
expect(screen.getByRole('button', { name: /next year/iu })).toBeDisabled();
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
it('does not disable year buttons when min/max are in adjacent years', async () => {
|
|
357
|
+
render(
|
|
358
|
+
<Calendar
|
|
359
|
+
value={new Date(2000, 3, 15)}
|
|
360
|
+
min={new Date(1999, 0, 1)}
|
|
361
|
+
max={new Date(2001, 11, 31)}
|
|
362
|
+
/>,
|
|
363
|
+
);
|
|
364
|
+
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
365
|
+
await user.click(screen.getByRole('button', { name: /2000, selected year/iu }));
|
|
366
|
+
expect(screen.getByRole('button', { name: /previous year/iu })).toBeEnabled();
|
|
367
|
+
expect(screen.getByRole('button', { name: /next year/iu })).toBeEnabled();
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it('does not disable year buttons when no min/max is set', async () => {
|
|
371
|
+
render(<Calendar value={new Date(2000, 3, 15)} />);
|
|
372
|
+
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
373
|
+
await user.click(screen.getByRole('button', { name: /2000, selected year/iu }));
|
|
374
|
+
expect(screen.getByRole('button', { name: /previous year/iu })).toBeEnabled();
|
|
375
|
+
expect(screen.getByRole('button', { name: /next year/iu })).toBeEnabled();
|
|
376
|
+
});
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
describe('year view — previous/next 20 years', () => {
|
|
380
|
+
it('disables previous 20-year button when min falls in the current block', async () => {
|
|
381
|
+
// value is 2000, block is 2000–2019, min is 2005 → previous block (1980–1999) is entirely before min
|
|
382
|
+
render(<Calendar value={new Date(2005, 0, 1)} min={new Date(2005, 0, 1)} />);
|
|
383
|
+
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
384
|
+
expect(screen.getByRole('button', { name: /previous 20 years/iu })).toBeDisabled();
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
it('disables next 20-year button when max falls in the current block', async () => {
|
|
388
|
+
// value is 2000, block is 2000–2019, max is 2015 → next block (2020–2039) is entirely after max
|
|
389
|
+
render(<Calendar value={new Date(2000, 0, 1)} max={new Date(2015, 11, 31)} />);
|
|
390
|
+
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
391
|
+
expect(screen.getByRole('button', { name: /next 20 years/iu })).toBeDisabled();
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
it('does not disable 20-year buttons when min/max span multiple blocks', async () => {
|
|
395
|
+
render(
|
|
396
|
+
<Calendar
|
|
397
|
+
value={new Date(2000, 0, 1)}
|
|
398
|
+
min={new Date(1985, 0, 1)}
|
|
399
|
+
max={new Date(2025, 11, 31)}
|
|
400
|
+
/>,
|
|
401
|
+
);
|
|
402
|
+
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
403
|
+
expect(screen.getByRole('button', { name: /previous 20 years/iu })).toBeEnabled();
|
|
404
|
+
expect(screen.getByRole('button', { name: /next 20 years/iu })).toBeEnabled();
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
it('does not disable 20-year buttons when no min/max is set', async () => {
|
|
408
|
+
render(<Calendar value={new Date(2000, 0, 1)} />);
|
|
409
|
+
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
410
|
+
expect(screen.getByRole('button', { name: /previous 20 years/iu })).toBeEnabled();
|
|
411
|
+
expect(screen.getByRole('button', { name: /next 20 years/iu })).toBeEnabled();
|
|
412
|
+
});
|
|
121
413
|
});
|
|
122
414
|
});
|
|
123
415
|
|
|
124
416
|
describe('view switching', () => {
|
|
125
417
|
it('switches to year view and back to day view', async () => {
|
|
126
|
-
render(<Calendar value={initialValue}
|
|
418
|
+
render(<Calendar value={initialValue} />);
|
|
127
419
|
|
|
128
420
|
await user.click(screen.getByRole('button', { name: /year view/iu }));
|
|
129
421
|
expect(screen.getByRole('button', { name: /selected year/iu })).toBeInTheDocument();
|
|
@@ -134,5 +426,23 @@ describe('Calendar', () => {
|
|
|
134
426
|
await user.click(screen.getByRole('button', { name: /selected month/iu }));
|
|
135
427
|
expect(screen.getByRole('button', { name: /next month/iu })).toBeInTheDocument();
|
|
136
428
|
});
|
|
429
|
+
|
|
430
|
+
it('switches to year view when header button is activated with keyboard', async () => {
|
|
431
|
+
const handleChange = jest.fn();
|
|
432
|
+
render(<Calendar value={initialValue} onChange={handleChange} />);
|
|
433
|
+
|
|
434
|
+
// Tab through to the year view header button
|
|
435
|
+
const yearViewButton = screen.getByRole('button', { name: /year view/iu });
|
|
436
|
+
await user.tab();
|
|
437
|
+
while (document.activeElement !== yearViewButton) {
|
|
438
|
+
await user.tab();
|
|
439
|
+
}
|
|
440
|
+
expect(yearViewButton).toHaveFocus();
|
|
441
|
+
|
|
442
|
+
await user.keyboard('{Enter}');
|
|
443
|
+
|
|
444
|
+
expect(screen.getByRole('button', { name: /selected year/iu })).toBeInTheDocument();
|
|
445
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
446
|
+
});
|
|
137
447
|
});
|
|
138
448
|
});
|