@transferwise/components 46.156.0 → 46.157.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/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 +133 -43
- package/build/styles/Calendar/Calendar.css +112 -37
- package/build/styles/Calendar/TableLink/TableLink.css +115 -0
- package/build/styles/Typeahead/Typeahead.css +4 -0
- package/build/styles/Typeahead/TypeaheadOption/TypeaheadOption.css +5 -0
- package/build/styles/main.css +133 -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 +1 -1
- 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 +4 -0
- package/src/Typeahead/Typeahead.less +2 -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 +133 -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
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from '@storybook/react-webpack5';
|
|
2
|
+
import { expect, waitFor, within, userEvent } from 'storybook/test';
|
|
3
|
+
|
|
4
|
+
import Calendar from '../Calendar';
|
|
5
|
+
import type { DisabledRules } from '../Calendar.types';
|
|
6
|
+
|
|
7
|
+
export default {
|
|
8
|
+
component: Calendar,
|
|
9
|
+
title: 'Forms/Calendar/Tests',
|
|
10
|
+
tags: ['!autodocs', '!manifest'],
|
|
11
|
+
} satisfies Meta<typeof Calendar>;
|
|
12
|
+
|
|
13
|
+
type Story = StoryObj<typeof Calendar>;
|
|
14
|
+
|
|
15
|
+
// min: March 2024, max: October 2024
|
|
16
|
+
const min = new Date(2024, 2, 1);
|
|
17
|
+
const max = new Date(2024, 9, 31);
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Year view: years within min/max are enabled, years outside are disabled (aria-disabled),
|
|
21
|
+
* and both navigation arrows are disabled because min and max fall within the same 2020–2039 block.
|
|
22
|
+
*/
|
|
23
|
+
export const YearViewBoundaries: Story = {
|
|
24
|
+
render: () => <Calendar value={new Date(2024, 5, 15)} min={min} max={max} />,
|
|
25
|
+
play: async ({ canvasElement }) => {
|
|
26
|
+
const canvas = within(canvasElement);
|
|
27
|
+
|
|
28
|
+
await userEvent.click(canvas.getByRole('button', { name: /year view/iu }));
|
|
29
|
+
|
|
30
|
+
// Selected year (within range) is enabled
|
|
31
|
+
await expect(
|
|
32
|
+
canvas.getByRole('button', { name: /2024.*selected year/iu }),
|
|
33
|
+
).not.toHaveAttribute('aria-disabled');
|
|
34
|
+
|
|
35
|
+
// Adjacent years outside range are disabled
|
|
36
|
+
await expect(
|
|
37
|
+
canvas.getByRole('button', { name: /^2023.*not available/iu }),
|
|
38
|
+
).toHaveAttribute('aria-disabled', 'true');
|
|
39
|
+
await expect(
|
|
40
|
+
canvas.getByRole('button', { name: /^2025.*not available/iu }),
|
|
41
|
+
).toHaveAttribute('aria-disabled', 'true');
|
|
42
|
+
|
|
43
|
+
// Both nav arrows disabled — min and max are both in the 2020–2039 block
|
|
44
|
+
await expect(canvas.getByRole('button', { name: /previous 20 years/iu })).toBeDisabled();
|
|
45
|
+
await expect(canvas.getByRole('button', { name: /next 20 years/iu })).toBeDisabled();
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Month view: months within min/max are enabled, months outside are disabled (aria-disabled),
|
|
51
|
+
* and both navigation arrows are disabled because min and max are both in 2024.
|
|
52
|
+
*/
|
|
53
|
+
export const MonthViewBoundaries: Story = {
|
|
54
|
+
render: () => <Calendar value={new Date(2024, 5, 15)} min={min} max={max} />,
|
|
55
|
+
play: async ({ canvasElement }) => {
|
|
56
|
+
const canvas = within(canvasElement);
|
|
57
|
+
|
|
58
|
+
// Navigate: year view → click 2024 → month view
|
|
59
|
+
await userEvent.click(canvas.getByRole('button', { name: /year view/iu }));
|
|
60
|
+
await userEvent.click(canvas.getByRole('button', { name: /2024.*selected year/iu }));
|
|
61
|
+
|
|
62
|
+
// Selected month (within range) is enabled
|
|
63
|
+
await expect(
|
|
64
|
+
canvas.getByRole('button', { name: /^Jun.*selected month/iu }),
|
|
65
|
+
).not.toHaveAttribute('aria-disabled');
|
|
66
|
+
|
|
67
|
+
// Boundary months (min=Mar, max=Oct) are enabled
|
|
68
|
+
await expect(
|
|
69
|
+
canvas.getByRole('button', { name: /^Mar(?!.*not available)/iu }),
|
|
70
|
+
).not.toHaveAttribute('aria-disabled');
|
|
71
|
+
await expect(
|
|
72
|
+
canvas.getByRole('button', { name: /^Oct(?!.*not available)/iu }),
|
|
73
|
+
).not.toHaveAttribute('aria-disabled');
|
|
74
|
+
|
|
75
|
+
// Months outside range are disabled
|
|
76
|
+
await expect(
|
|
77
|
+
canvas.getByRole('button', { name: /^Feb.*not available/iu }),
|
|
78
|
+
).toHaveAttribute('aria-disabled', 'true');
|
|
79
|
+
await expect(
|
|
80
|
+
canvas.getByRole('button', { name: /^Nov.*not available/iu }),
|
|
81
|
+
).toHaveAttribute('aria-disabled', 'true');
|
|
82
|
+
|
|
83
|
+
// Both nav arrows disabled — min and max are both in 2024
|
|
84
|
+
await expect(canvas.getByRole('button', { name: /previous year/iu })).toBeDisabled();
|
|
85
|
+
await expect(canvas.getByRole('button', { name: /next year/iu })).toBeDisabled();
|
|
86
|
+
},
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const disabledRules: DisabledRules = {
|
|
90
|
+
weekdays: ['sat', 'sun'],
|
|
91
|
+
reason: 'We are closed on weekends.',
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* When a disabled cell receives direct DOM focus (e.g. screen reader virtual cursor),
|
|
96
|
+
* the inline prompt updates to negative sentiment showing the disabled reason.
|
|
97
|
+
*/
|
|
98
|
+
export const DisabledDateFocusUpdatesPrompt: Story = {
|
|
99
|
+
render: () => <Calendar value={new Date(2024, 6, 5)} disabledRules={disabledRules} />,
|
|
100
|
+
play: async ({ canvasElement }) => {
|
|
101
|
+
const canvas = within(canvasElement);
|
|
102
|
+
|
|
103
|
+
// Initially prompt is neutral (focused date is enabled)
|
|
104
|
+
const prompt = canvasElement.querySelector('.wds-inline-prompt')!;
|
|
105
|
+
await expect(prompt).toHaveClass('wds-prompt--neutral');
|
|
106
|
+
|
|
107
|
+
// Simulate screen reader focus on a disabled Saturday (matches both "6 July 2024" and "July 6, 2024")
|
|
108
|
+
const saturday = canvas.getByLabelText(/(?:6 July|July 6).*2024/u);
|
|
109
|
+
saturday.focus();
|
|
110
|
+
|
|
111
|
+
// Prompt should switch to negative after React re-render
|
|
112
|
+
await waitFor(() => expect(prompt).toHaveClass('wds-prompt--negative'));
|
|
113
|
+
},
|
|
114
|
+
};
|
package/src/Calendar/index.ts
CHANGED
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
import { mockMatchMedia, mockResizeObserver, render, screen, userEvent } from '../test-utils';
|
|
2
|
+
import DateLookup from './DateLookup';
|
|
3
|
+
import type { DisabledRules } from '../Calendar/Calendar.types';
|
|
4
|
+
|
|
5
|
+
import { act } from 'react';
|
|
6
|
+
|
|
7
|
+
const user = userEvent.setup({ advanceTimers: jest.advanceTimersByTimeAsync });
|
|
8
|
+
|
|
9
|
+
mockMatchMedia();
|
|
10
|
+
mockResizeObserver();
|
|
11
|
+
|
|
12
|
+
const initialValue = new Date(2024, 6, 1);
|
|
13
|
+
|
|
14
|
+
const weekendRules: DisabledRules = {
|
|
15
|
+
weekdays: ['sat', 'sun'],
|
|
16
|
+
reason: 'We are closed on weekends.',
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const bankHolidayRules: DisabledRules = {
|
|
20
|
+
dates: [new Date(2024, 6, 4)],
|
|
21
|
+
reason: 'Bank holiday.',
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
describe('DateLookup - disabled dates', () => {
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
jest.useFakeTimers();
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
afterEach(async () => {
|
|
30
|
+
await jest.runOnlyPendingTimersAsync();
|
|
31
|
+
jest.useRealTimers();
|
|
32
|
+
jest.clearAllMocks();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const openDateLookup = async () => {
|
|
36
|
+
await user.click(screen.getByRole('button'));
|
|
37
|
+
await act(async () => {
|
|
38
|
+
await jest.runOnlyPendingTimersAsync();
|
|
39
|
+
});
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
describe('panel behavior with rule-disabled dates', () => {
|
|
43
|
+
it('does NOT close when a rule-disabled date is clicked', async () => {
|
|
44
|
+
render(
|
|
45
|
+
<DateLookup value={initialValue} disabledRules={bankHolidayRules} onChange={jest.fn()} />,
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
await openDateLookup();
|
|
49
|
+
const holiday = screen.getByLabelText(/^4 July 2024/u);
|
|
50
|
+
await user.click(holiday);
|
|
51
|
+
|
|
52
|
+
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('closes when an enabled date is clicked', async () => {
|
|
56
|
+
const handleChange = jest.fn();
|
|
57
|
+
render(
|
|
58
|
+
<DateLookup
|
|
59
|
+
value={initialValue}
|
|
60
|
+
disabledRules={bankHolidayRules}
|
|
61
|
+
onChange={handleChange}
|
|
62
|
+
/>,
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
await openDateLookup();
|
|
66
|
+
const enabledDate = screen.getByLabelText(/^3 July 2024/u);
|
|
67
|
+
await user.click(enabledDate);
|
|
68
|
+
|
|
69
|
+
await act(async () => {
|
|
70
|
+
await jest.runOnlyPendingTimersAsync();
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
|
|
74
|
+
expect(handleChange).toHaveBeenCalledWith(new Date(2024, 6, 3));
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('panel behavior with out-of-range dates', () => {
|
|
79
|
+
const minDate = new Date(2024, 6, 3);
|
|
80
|
+
const maxDate = new Date(2024, 6, 25);
|
|
81
|
+
|
|
82
|
+
it('does NOT close when an out-of-range date is clicked', async () => {
|
|
83
|
+
render(
|
|
84
|
+
<DateLookup
|
|
85
|
+
value={new Date(2024, 6, 5)}
|
|
86
|
+
min={minDate}
|
|
87
|
+
max={maxDate}
|
|
88
|
+
onChange={jest.fn()}
|
|
89
|
+
/>,
|
|
90
|
+
);
|
|
91
|
+
|
|
92
|
+
await openDateLookup();
|
|
93
|
+
await user.click(screen.getByRole('button', { name: /^1 July 2024/u }));
|
|
94
|
+
|
|
95
|
+
expect(screen.getByRole('dialog')).toBeInTheDocument();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('does NOT call onChange when an out-of-range date is clicked', async () => {
|
|
99
|
+
const handleChange = jest.fn();
|
|
100
|
+
render(
|
|
101
|
+
<DateLookup
|
|
102
|
+
value={new Date(2024, 6, 5)}
|
|
103
|
+
min={minDate}
|
|
104
|
+
max={maxDate}
|
|
105
|
+
onChange={handleChange}
|
|
106
|
+
/>,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
await openDateLookup();
|
|
110
|
+
await user.click(screen.getByRole('button', { name: /^1 July 2024/u }));
|
|
111
|
+
|
|
112
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('prop threading', () => {
|
|
117
|
+
it('renders rule-disabled dates with aria-disabled in the calendar', async () => {
|
|
118
|
+
render(<DateLookup value={initialValue} disabledRules={weekendRules} onChange={jest.fn()} />);
|
|
119
|
+
|
|
120
|
+
await openDateLookup();
|
|
121
|
+
|
|
122
|
+
const saturday = screen.getByLabelText(/^6 July 2024/u);
|
|
123
|
+
expect(saturday).toHaveAttribute('aria-disabled', 'true');
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
it('renders out-of-range dates with aria-disabled in the calendar', async () => {
|
|
127
|
+
render(
|
|
128
|
+
<DateLookup
|
|
129
|
+
value={new Date(2024, 6, 5)}
|
|
130
|
+
min={new Date(2024, 6, 3)}
|
|
131
|
+
max={new Date(2024, 6, 25)}
|
|
132
|
+
onChange={jest.fn()}
|
|
133
|
+
/>,
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
await openDateLookup();
|
|
137
|
+
|
|
138
|
+
const outOfRangeDate = screen.getByRole('button', { name: /^1 July 2024/u });
|
|
139
|
+
expect(outOfRangeDate).toHaveAttribute('aria-disabled', 'true');
|
|
140
|
+
expect(outOfRangeDate).toBeEnabled();
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('renders InlinePrompt with disabledRules reason text', async () => {
|
|
144
|
+
render(<DateLookup value={initialValue} disabledRules={weekendRules} onChange={jest.fn()} />);
|
|
145
|
+
|
|
146
|
+
await openDateLookup();
|
|
147
|
+
|
|
148
|
+
expect(screen.getByText('We are closed on weekends.')).toBeInTheDocument();
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
it('renders previous month button as disabled when min is in the current month', async () => {
|
|
152
|
+
render(
|
|
153
|
+
<DateLookup value={new Date(2024, 6, 5)} min={new Date(2024, 6, 1)} onChange={jest.fn()} />,
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
await openDateLookup();
|
|
157
|
+
|
|
158
|
+
expect(screen.getByRole('button', { name: /previous month/iu })).toBeDisabled();
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('renders next month button as disabled when max is in the current month', async () => {
|
|
162
|
+
render(
|
|
163
|
+
<DateLookup
|
|
164
|
+
value={new Date(2024, 6, 5)}
|
|
165
|
+
max={new Date(2024, 6, 31)}
|
|
166
|
+
onChange={jest.fn()}
|
|
167
|
+
/>,
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
await openDateLookup();
|
|
171
|
+
|
|
172
|
+
expect(screen.getByRole('button', { name: /next month/iu })).toBeDisabled();
|
|
173
|
+
});
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
describe('InlinePrompt for out-of-range dates', () => {
|
|
177
|
+
const minDate = new Date(2024, 6, 3);
|
|
178
|
+
const maxDate = new Date(2024, 6, 25);
|
|
179
|
+
|
|
180
|
+
it('shows negative prompt with boundary dates when out-of-range date is clicked', async () => {
|
|
181
|
+
render(
|
|
182
|
+
<DateLookup
|
|
183
|
+
value={new Date(2024, 6, 5)}
|
|
184
|
+
min={minDate}
|
|
185
|
+
max={maxDate}
|
|
186
|
+
onChange={jest.fn()}
|
|
187
|
+
/>,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
await openDateLookup();
|
|
191
|
+
await user.click(screen.getByRole('button', { name: /^1 July 2024/u }));
|
|
192
|
+
|
|
193
|
+
// The prompt renders inside a portal so query from document
|
|
194
|
+
const prompt = document.querySelector('.wds-inline-prompt');
|
|
195
|
+
expect(prompt).toBeInTheDocument();
|
|
196
|
+
expect(prompt).toHaveClass('wds-prompt--negative');
|
|
197
|
+
expect(prompt).toHaveTextContent(
|
|
198
|
+
'Please select a date between 3 July 2024 and 25 July 2024.',
|
|
199
|
+
);
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
it('shows boundary message (not disabledRules reason) when out-of-range date is clicked', async () => {
|
|
203
|
+
render(
|
|
204
|
+
<DateLookup
|
|
205
|
+
value={new Date(2024, 6, 5)}
|
|
206
|
+
min={minDate}
|
|
207
|
+
max={maxDate}
|
|
208
|
+
disabledRules={weekendRules}
|
|
209
|
+
onChange={jest.fn()}
|
|
210
|
+
/>,
|
|
211
|
+
);
|
|
212
|
+
|
|
213
|
+
await openDateLookup();
|
|
214
|
+
// July 27 is beyond max
|
|
215
|
+
await user.click(screen.getByRole('button', { name: /^27 July 2024/u }));
|
|
216
|
+
|
|
217
|
+
const prompt = document.querySelector('.wds-inline-prompt');
|
|
218
|
+
expect(prompt).toHaveTextContent(
|
|
219
|
+
'Please select a date between 3 July 2024 and 25 July 2024.',
|
|
220
|
+
);
|
|
221
|
+
expect(prompt).not.toHaveTextContent('We are closed on weekends.');
|
|
222
|
+
});
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
describe('onDateSelect', () => {
|
|
226
|
+
it('fires with isEnabled: false when navigating to a rule-disabled date', async () => {
|
|
227
|
+
const handleDateSelect = jest.fn();
|
|
228
|
+
render(
|
|
229
|
+
<DateLookup
|
|
230
|
+
value={new Date(2024, 6, 5)}
|
|
231
|
+
disabledRules={weekendRules}
|
|
232
|
+
onChange={jest.fn()}
|
|
233
|
+
onDateSelect={handleDateSelect}
|
|
234
|
+
/>,
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
await openDateLookup();
|
|
238
|
+
await user.keyboard('{ArrowRight}');
|
|
239
|
+
|
|
240
|
+
expect(handleDateSelect).toHaveBeenCalledWith(
|
|
241
|
+
expect.objectContaining({
|
|
242
|
+
date: new Date(2024, 6, 6),
|
|
243
|
+
isEnabled: false,
|
|
244
|
+
}),
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
it('fires with isEnabled: false when clicking an out-of-range date', async () => {
|
|
249
|
+
const handleDateSelect = jest.fn();
|
|
250
|
+
render(
|
|
251
|
+
<DateLookup
|
|
252
|
+
value={new Date(2024, 6, 5)}
|
|
253
|
+
min={new Date(2024, 6, 3)}
|
|
254
|
+
max={new Date(2024, 6, 25)}
|
|
255
|
+
onChange={jest.fn()}
|
|
256
|
+
onDateSelect={handleDateSelect}
|
|
257
|
+
/>,
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
await openDateLookup();
|
|
261
|
+
await user.click(screen.getByRole('button', { name: /^1 July 2024/u }));
|
|
262
|
+
|
|
263
|
+
expect(handleDateSelect).toHaveBeenCalledWith(
|
|
264
|
+
expect.objectContaining({
|
|
265
|
+
date: new Date(2024, 6, 1),
|
|
266
|
+
isEnabled: false,
|
|
267
|
+
}),
|
|
268
|
+
);
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('fires with isEnabled: true when clicking an enabled date', async () => {
|
|
272
|
+
const handleDateSelect = jest.fn();
|
|
273
|
+
render(
|
|
274
|
+
<DateLookup
|
|
275
|
+
value={new Date(2024, 6, 5)}
|
|
276
|
+
disabledRules={weekendRules}
|
|
277
|
+
onChange={jest.fn()}
|
|
278
|
+
onDateSelect={handleDateSelect}
|
|
279
|
+
/>,
|
|
280
|
+
);
|
|
281
|
+
|
|
282
|
+
await openDateLookup();
|
|
283
|
+
await user.click(screen.getByLabelText(/^3 July 2024/u));
|
|
284
|
+
|
|
285
|
+
expect(handleDateSelect).toHaveBeenCalledWith(
|
|
286
|
+
expect.objectContaining({
|
|
287
|
+
date: new Date(2024, 6, 3),
|
|
288
|
+
isEnabled: true,
|
|
289
|
+
}),
|
|
290
|
+
);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('fires exactly once when an enabled date is clicked', async () => {
|
|
294
|
+
const handleDateSelect = jest.fn();
|
|
295
|
+
render(
|
|
296
|
+
<DateLookup
|
|
297
|
+
value={new Date(2024, 6, 5)}
|
|
298
|
+
disabledRules={weekendRules}
|
|
299
|
+
onChange={jest.fn()}
|
|
300
|
+
onDateSelect={handleDateSelect}
|
|
301
|
+
/>,
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
await openDateLookup();
|
|
305
|
+
handleDateSelect.mockClear();
|
|
306
|
+
|
|
307
|
+
await user.click(screen.getByLabelText(/^3 July 2024/u));
|
|
308
|
+
|
|
309
|
+
expect(handleDateSelect).toHaveBeenCalledTimes(1);
|
|
310
|
+
});
|
|
311
|
+
});
|
|
312
|
+
});
|
|
@@ -110,24 +110,13 @@ describe('DateLookup', () => {
|
|
|
110
110
|
['{ArrowRight}', +1],
|
|
111
111
|
['{ArrowUp}', -7],
|
|
112
112
|
['{ArrowDown}', +7],
|
|
113
|
-
])("handles '%s' to step %d day(s)", async (text: string
|
|
113
|
+
])("handles '%s' to step %d day(s) without changing value", async (text: string) => {
|
|
114
114
|
const handleChange = jest.fn();
|
|
115
115
|
await setupAndOpenWithMouse({ onChange: handleChange });
|
|
116
116
|
|
|
117
|
-
expect(handleChange).not.toHaveBeenCalled();
|
|
118
|
-
|
|
119
117
|
await user.keyboard(text);
|
|
120
118
|
|
|
121
|
-
|
|
122
|
-
value.setDate(initialValue.getDate() + step);
|
|
123
|
-
expect(handleChange).toHaveBeenCalledWith(value);
|
|
124
|
-
|
|
125
|
-
await user.keyboard('{Escape}');
|
|
126
|
-
await act(async () => {
|
|
127
|
-
await jest.runOnlyPendingTimersAsync();
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
expect(handleChange).toHaveBeenCalledWith(initialValue);
|
|
119
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
131
120
|
});
|
|
132
121
|
});
|
|
133
122
|
|
|
@@ -137,7 +126,7 @@ describe('DateLookup', () => {
|
|
|
137
126
|
['{ArrowRight}', +1],
|
|
138
127
|
['{ArrowUp}', -4],
|
|
139
128
|
['{ArrowDown}', +4],
|
|
140
|
-
])("handles '%s' to step %d year(s)", async (text: string
|
|
129
|
+
])("handles '%s' to step %d year(s) without changing value", async (text: string) => {
|
|
141
130
|
const handleChange = jest.fn();
|
|
142
131
|
await setupAndOpenWithMouse({ onChange: handleChange });
|
|
143
132
|
|
|
@@ -146,20 +135,9 @@ describe('DateLookup', () => {
|
|
|
146
135
|
await jest.runOnlyPendingTimersAsync();
|
|
147
136
|
});
|
|
148
137
|
|
|
149
|
-
expect(handleChange).not.toHaveBeenCalled();
|
|
150
|
-
|
|
151
138
|
await user.keyboard(text);
|
|
152
139
|
|
|
153
|
-
|
|
154
|
-
value.setFullYear(initialValue.getFullYear() + step);
|
|
155
|
-
expect(handleChange).toHaveBeenCalledWith(value);
|
|
156
|
-
|
|
157
|
-
await user.keyboard('{Escape}');
|
|
158
|
-
await act(async () => {
|
|
159
|
-
await jest.runOnlyPendingTimersAsync();
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
expect(handleChange).toHaveBeenCalledWith(initialValue);
|
|
140
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
163
141
|
});
|
|
164
142
|
});
|
|
165
143
|
|
|
@@ -169,7 +147,7 @@ describe('DateLookup', () => {
|
|
|
169
147
|
['{ArrowRight}', +1],
|
|
170
148
|
['{ArrowUp}', -4],
|
|
171
149
|
['{ArrowDown}', +4],
|
|
172
|
-
])("handles '%s' to step %d month(s)", async (text: string
|
|
150
|
+
])("handles '%s' to step %d month(s) without changing value", async (text: string) => {
|
|
173
151
|
const handleChange = jest.fn();
|
|
174
152
|
await setupAndOpenWithMouse({ onChange: handleChange });
|
|
175
153
|
|
|
@@ -182,20 +160,39 @@ describe('DateLookup', () => {
|
|
|
182
160
|
await jest.runOnlyPendingTimersAsync();
|
|
183
161
|
});
|
|
184
162
|
|
|
163
|
+
await user.keyboard(text);
|
|
164
|
+
|
|
185
165
|
expect(handleChange).not.toHaveBeenCalled();
|
|
166
|
+
});
|
|
167
|
+
});
|
|
186
168
|
|
|
187
|
-
|
|
169
|
+
describe('focus vs selection separation', () => {
|
|
170
|
+
it('does not call onChange during arrow key navigation', async () => {
|
|
171
|
+
const handleChange = jest.fn();
|
|
172
|
+
render(<DateLookup value={new Date(2020, 0, 15)} onChange={handleChange} />);
|
|
188
173
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
expect(handleChange).
|
|
174
|
+
// Open the panel
|
|
175
|
+
await user.click(screen.getByRole('button'));
|
|
176
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
192
177
|
|
|
193
|
-
await user.keyboard('{
|
|
194
|
-
await act(async () => {
|
|
195
|
-
await jest.runOnlyPendingTimersAsync();
|
|
196
|
-
});
|
|
178
|
+
await user.keyboard('{ArrowRight}');
|
|
197
179
|
|
|
198
|
-
expect(handleChange).
|
|
180
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('calls onChange when Enter is pressed on a focused date', async () => {
|
|
184
|
+
const handleChange = jest.fn();
|
|
185
|
+
render(<DateLookup value={new Date(2020, 0, 15)} onChange={handleChange} />);
|
|
186
|
+
|
|
187
|
+
// Open the panel
|
|
188
|
+
await user.click(screen.getByRole('button'));
|
|
189
|
+
expect(handleChange).not.toHaveBeenCalled();
|
|
190
|
+
|
|
191
|
+
// Arrow to next date then confirm with Enter
|
|
192
|
+
await user.keyboard('{ArrowRight}');
|
|
193
|
+
await user.keyboard('{Enter}');
|
|
194
|
+
|
|
195
|
+
expect(handleChange).toHaveBeenCalledWith(new Date(2020, 0, 16));
|
|
199
196
|
});
|
|
200
197
|
});
|
|
201
198
|
});
|
|
@@ -229,7 +226,7 @@ describe('DateLookup (events)', () => {
|
|
|
229
226
|
const today = new Date();
|
|
230
227
|
const todayDateLabel = today.toLocaleDateString('en-GB', {
|
|
231
228
|
day: 'numeric',
|
|
232
|
-
month: '
|
|
229
|
+
month: 'long',
|
|
233
230
|
year: 'numeric',
|
|
234
231
|
});
|
|
235
232
|
const expectedHeaderText = today.toLocaleDateString('en-GB', {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
import { createRef, PureComponent, KeyboardEvent } from 'react';
|
|
3
3
|
import Calendar from '../Calendar';
|
|
4
|
+
import type { DateChangeMeta, DisabledRules, OnDateSelectParams } from '../Calendar/Calendar.types';
|
|
4
5
|
import {
|
|
5
6
|
Size,
|
|
6
7
|
MonthFormat,
|
|
@@ -15,19 +16,37 @@ import { OverlayIdProvider } from '../Provider/overlay/OverlayIdProvider';
|
|
|
15
16
|
import DateTrigger from './DateTrigger';
|
|
16
17
|
|
|
17
18
|
export interface DateLookupProps {
|
|
18
|
-
|
|
19
|
+
/** The currently selected date, or `null` for no selection. */
|
|
19
20
|
value: Date | null;
|
|
21
|
+
/** Called when an enabled date is confirmed. Receives `null` when the value is cleared. */
|
|
22
|
+
onChange: (date: Date | null) => void;
|
|
23
|
+
/** HTML `id` applied to the trigger button, used for label association. */
|
|
24
|
+
id?: string;
|
|
25
|
+
/** Earliest selectable date (inclusive). Dates before this are interactive but not selectable, and show a boundary prompt. */
|
|
20
26
|
min?: Date | null;
|
|
27
|
+
/** Latest selectable date (inclusive). Dates after this are interactive but not selectable, and show a boundary prompt. */
|
|
21
28
|
max?: Date | null;
|
|
29
|
+
/** Size of the trigger input. Defaults to `'md'`. */
|
|
22
30
|
size?: SizeSmall | SizeMedium | SizeLarge;
|
|
23
|
-
|
|
31
|
+
/** Label text rendered above the trigger input. */
|
|
24
32
|
label?: string;
|
|
33
|
+
/** ID of an external element that labels the trigger, for use inside custom label contexts. */
|
|
25
34
|
'aria-labelledby'?: string;
|
|
35
|
+
/** Placeholder text shown in the trigger when no date is selected. */
|
|
36
|
+
placeholder?: string;
|
|
37
|
+
/** Month name format in the calendar header. Defaults to `'long'` (e.g. "January"). */
|
|
26
38
|
monthFormat?: `${MonthFormat}`;
|
|
39
|
+
/** When `true`, the trigger and calendar are non-interactive. */
|
|
27
40
|
disabled?: boolean;
|
|
41
|
+
/** When `true`, a clear button is shown in the trigger to reset the value to `null`. */
|
|
28
42
|
clearable?: boolean;
|
|
29
|
-
|
|
43
|
+
/** Rules for disabling specific weekdays, individual dates, or date ranges. The required `reason` is shown in an inline prompt and linked to disabled cells via `aria-describedby` for screen reader accessibility. */
|
|
44
|
+
disabledRules?: DisabledRules;
|
|
45
|
+
/** Called whenever any date is focused or clicked, including disabled and out-of-range dates. `isEnabled` is `false` when the date is not selectable. */
|
|
46
|
+
onDateSelect?: (params: OnDateSelectParams) => void;
|
|
47
|
+
/** Called when the calendar panel opens. */
|
|
30
48
|
onFocus?: () => void;
|
|
49
|
+
/** Called when the calendar panel closes. */
|
|
31
50
|
onBlur?: () => void;
|
|
32
51
|
}
|
|
33
52
|
|
|
@@ -128,7 +147,15 @@ class DateLookup extends PureComponent<DateLookupPropsWithInputAttributes, DateL
|
|
|
128
147
|
}
|
|
129
148
|
};
|
|
130
149
|
|
|
131
|
-
handleDateChange = (date: Date | null) => {
|
|
150
|
+
handleDateChange = (date: Date | null, meta: DateChangeMeta) => {
|
|
151
|
+
const { onDateSelect } = this.props;
|
|
152
|
+
if (date && onDateSelect) {
|
|
153
|
+
onDateSelect({ date, isEnabled: meta.isEnabled });
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
handleDateSelect = (date: Date | null) => {
|
|
158
|
+
if (!date) return;
|
|
132
159
|
this.setState(
|
|
133
160
|
(state) => {
|
|
134
161
|
const originalDate = state.originalDate ?? this.props.value;
|
|
@@ -136,18 +163,14 @@ class DateLookup extends PureComponent<DateLookupPropsWithInputAttributes, DateL
|
|
|
136
163
|
},
|
|
137
164
|
() => {
|
|
138
165
|
this.props.onChange(date);
|
|
166
|
+
this.close();
|
|
167
|
+
this.focusOn('.btn');
|
|
139
168
|
},
|
|
140
169
|
);
|
|
141
170
|
};
|
|
142
171
|
|
|
143
|
-
handleDateSelect = (date: Date) => {
|
|
144
|
-
this.props.onChange(date);
|
|
145
|
-
this.close();
|
|
146
|
-
this.focusOn('.btn');
|
|
147
|
-
};
|
|
148
|
-
|
|
149
172
|
getCalendar = () => {
|
|
150
|
-
const { value, min, max, monthFormat, placeholder } = this.props;
|
|
173
|
+
const { value, min, max, monthFormat, placeholder, disabledRules } = this.props;
|
|
151
174
|
return (
|
|
152
175
|
<div className="p-a-1">
|
|
153
176
|
<Calendar
|
|
@@ -156,8 +179,9 @@ class DateLookup extends PureComponent<DateLookupPropsWithInputAttributes, DateL
|
|
|
156
179
|
max={max}
|
|
157
180
|
monthFormat={monthFormat}
|
|
158
181
|
placeholder={placeholder}
|
|
159
|
-
|
|
160
|
-
|
|
182
|
+
disabledRules={disabledRules}
|
|
183
|
+
onDateFocus={this.handleDateChange}
|
|
184
|
+
onChange={this.handleDateSelect}
|
|
161
185
|
/>
|
|
162
186
|
</div>
|
|
163
187
|
);
|