@transferwise/components 46.155.1 → 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/Markup/Markup.js +138 -0
- package/build/Markup/Markup.js.map +1 -0
- package/build/Markup/Markup.mjs +133 -0
- package/build/Markup/Markup.mjs.map +1 -0
- package/build/Markup/constants.js +21 -0
- package/build/Markup/constants.js.map +1 -0
- package/build/Markup/constants.mjs +15 -0
- package/build/Markup/constants.mjs.map +1 -0
- package/build/Markup/utils/parseMarkup.js +263 -0
- package/build/Markup/utils/parseMarkup.js.map +1 -0
- package/build/Markup/utils/parseMarkup.mjs +258 -0
- package/build/Markup/utils/parseMarkup.mjs.map +1 -0
- package/build/Markup/utils/sanitise.js +111 -0
- package/build/Markup/utils/sanitise.js.map +1 -0
- package/build/Markup/utils/sanitise.mjs +108 -0
- package/build/Markup/utils/sanitise.mjs.map +1 -0
- 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/i18n/hu.json +1 -1
- package/build/i18n/hu.json.js +1 -1
- package/build/i18n/hu.json.mjs +1 -1
- package/build/index.js +4 -2
- package/build/index.js.map +1 -1
- package/build/index.mjs +2 -1
- package/build/index.mjs.map +1 -1
- package/build/main.css +168 -43
- package/build/styles/Calendar/Calendar.css +112 -37
- package/build/styles/Calendar/TableLink/TableLink.css +115 -0
- package/build/styles/Markup/Markup.css +28 -0
- package/build/styles/Markup/_stories/Vulnerability/Vulnerability.css +69 -0
- package/build/styles/Typeahead/Typeahead.css +4 -0
- package/build/styles/Typeahead/TypeaheadOption/TypeaheadOption.css +5 -0
- package/build/styles/main.css +168 -43
- 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/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.d.ts +1 -1
- package/build/types/Inputs/SelectInput/TriggerButton/SelectInputTriggerButton.d.ts.map +1 -1
- package/build/types/Markup/Markup.d.ts +18 -0
- package/build/types/Markup/Markup.d.ts.map +1 -0
- package/build/types/Markup/Markup.types.d.ts +77 -0
- package/build/types/Markup/Markup.types.d.ts.map +1 -0
- package/build/types/Markup/_stories/Vulnerability/Vulnerability.d.ts +12 -0
- package/build/types/Markup/_stories/Vulnerability/Vulnerability.d.ts.map +1 -0
- package/build/types/Markup/constants.d.ts +6 -0
- package/build/types/Markup/constants.d.ts.map +1 -0
- package/build/types/Markup/index.d.ts +3 -0
- package/build/types/Markup/index.d.ts.map +1 -0
- package/build/types/Markup/utils/parseMarkup.d.ts +8 -0
- package/build/types/Markup/utils/parseMarkup.d.ts.map +1 -0
- package/build/types/Markup/utils/sanitise.d.ts +4 -0
- package/build/types/Markup/utils/sanitise.d.ts.map +1 -0
- 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/index.d.ts +5 -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/Markup/Markup.css +28 -0
- package/src/Markup/Markup.injection.test.tsx +163 -0
- package/src/Markup/Markup.less +29 -0
- package/src/Markup/Markup.test.tsx +502 -0
- package/src/Markup/Markup.tsx +175 -0
- package/src/Markup/Markup.types.ts +62 -0
- package/src/Markup/_stories/Markup.docs.mdx +75 -0
- package/src/Markup/_stories/Markup.security.docs.mdx +343 -0
- package/src/Markup/_stories/Markup.story.tsx +266 -0
- package/src/Markup/_stories/Vulnerability/Vulnerability.css +69 -0
- package/src/Markup/_stories/Vulnerability/Vulnerability.tsx +30 -0
- package/src/Markup/constants.ts +23 -0
- package/src/Markup/index.ts +2 -0
- package/src/Markup/utils/parseMarkup.test.ts +706 -0
- package/src/Markup/utils/parseMarkup.ts +265 -0
- package/src/Markup/utils/sanitise.test.ts +499 -0
- package/src/Markup/utils/sanitise.ts +129 -0
- 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/i18n/hu.json +1 -1
- package/src/index.ts +5 -2
- package/src/main.css +168 -43
- package/src/main.less +1 -0
- 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
|
@@ -2,10 +2,11 @@ import { formatDate } from '@transferwise/formatting';
|
|
|
2
2
|
import { useIntl } from 'react-intl';
|
|
3
3
|
|
|
4
4
|
import { getFocusableTime } from '../../GetFocusableTime/GetFocusableTime';
|
|
5
|
-
import TableLink from '../../
|
|
5
|
+
import TableLink from '../../TableLink';
|
|
6
6
|
|
|
7
7
|
interface MonthCalendarTableProps {
|
|
8
8
|
selectedDate: Date | null;
|
|
9
|
+
focusedDate?: Date | null;
|
|
9
10
|
min: Date | null;
|
|
10
11
|
max: Date | null;
|
|
11
12
|
viewYear: number;
|
|
@@ -19,6 +20,7 @@ const MONTH_ONLY_FORMAT: Intl.DateTimeFormatOptions = { month: 'short' };
|
|
|
19
20
|
|
|
20
21
|
const MonthCalendarTable = ({
|
|
21
22
|
selectedDate,
|
|
23
|
+
focusedDate,
|
|
22
24
|
min,
|
|
23
25
|
max,
|
|
24
26
|
viewYear,
|
|
@@ -34,7 +36,8 @@ const MonthCalendarTable = ({
|
|
|
34
36
|
type="month"
|
|
35
37
|
title={formatDate(new Date(viewYear, month), locale, MONTH_ONLY_FORMAT)}
|
|
36
38
|
active={isActive(month)}
|
|
37
|
-
|
|
39
|
+
outOfRange={isDisabled(month)}
|
|
40
|
+
disabled={false}
|
|
38
41
|
today={viewYear === new Date().getFullYear() && month === new Date().getMonth()}
|
|
39
42
|
autofocus={autofocusMonth === month}
|
|
40
43
|
onClick={onSelect}
|
|
@@ -46,6 +49,10 @@ const MonthCalendarTable = ({
|
|
|
46
49
|
return !!(month === selectedDate?.getMonth() && viewYear === selectedDate?.getFullYear());
|
|
47
50
|
};
|
|
48
51
|
|
|
52
|
+
const isFocused = (month: number) => {
|
|
53
|
+
return !!(month === focusedDate?.getMonth() && viewYear === focusedDate?.getFullYear());
|
|
54
|
+
};
|
|
55
|
+
|
|
49
56
|
const isThisMonth = (month: number) => {
|
|
50
57
|
return viewYear === new Date().getFullYear() && month === new Date().getMonth();
|
|
51
58
|
};
|
|
@@ -60,7 +67,12 @@ const MonthCalendarTable = ({
|
|
|
60
67
|
|
|
61
68
|
const autofocusMonth = (() => {
|
|
62
69
|
const months = Array.from({ length: ROWS * COLS }, (_, index) => index);
|
|
63
|
-
return getFocusableTime({
|
|
70
|
+
return getFocusableTime({
|
|
71
|
+
isActive: isFocused,
|
|
72
|
+
isNow: isThisMonth,
|
|
73
|
+
isDisabled,
|
|
74
|
+
timeSpan: months,
|
|
75
|
+
});
|
|
64
76
|
})();
|
|
65
77
|
|
|
66
78
|
return (
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
.wds-Calendar__option--day,
|
|
2
|
+
.wds-Calendar__option--month,
|
|
3
|
+
.wds-Calendar__option--year {
|
|
4
|
+
width: 100%;
|
|
5
|
+
padding: 4px 0;
|
|
6
|
+
padding: var(--size-4) 0;
|
|
7
|
+
border: transparent;
|
|
8
|
+
border-radius: 10px;
|
|
9
|
+
border-radius: var(--radius-small);
|
|
10
|
+
background-color: transparent;
|
|
11
|
+
font-weight: 600;
|
|
12
|
+
font-weight: var(--font-weight-semi-bold);
|
|
13
|
+
color: #37517e;
|
|
14
|
+
color: var(--color-content-primary);
|
|
15
|
+
}
|
|
16
|
+
@media (max-width: 320px) {
|
|
17
|
+
.wds-Calendar__option--day,
|
|
18
|
+
.wds-Calendar__option--month,
|
|
19
|
+
.wds-Calendar__option--year {
|
|
20
|
+
padding: 0;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
.wds-Calendar__option--day:focus,
|
|
24
|
+
.wds-Calendar__option--month:focus,
|
|
25
|
+
.wds-Calendar__option--year:focus {
|
|
26
|
+
background-color: var(--color-background-screen-hover);
|
|
27
|
+
}
|
|
28
|
+
.wds-Calendar__option--day.disabled,
|
|
29
|
+
.wds-Calendar__option--month.disabled,
|
|
30
|
+
.wds-Calendar__option--year.disabled,
|
|
31
|
+
.wds-Calendar__option--day[aria-disabled="true"],
|
|
32
|
+
.wds-Calendar__option--month[aria-disabled="true"],
|
|
33
|
+
.wds-Calendar__option--year[aria-disabled="true"] {
|
|
34
|
+
--ring-outline-color: var(--color-sentiment-content-primary);
|
|
35
|
+
color: #768e9c;
|
|
36
|
+
color: var(--color-content-tertiary);
|
|
37
|
+
cursor: not-allowed;
|
|
38
|
+
}
|
|
39
|
+
.wds-Calendar__option--day.disabled:hover,
|
|
40
|
+
.wds-Calendar__option--month.disabled:hover,
|
|
41
|
+
.wds-Calendar__option--year.disabled:hover,
|
|
42
|
+
.wds-Calendar__option--day[aria-disabled="true"]:hover,
|
|
43
|
+
.wds-Calendar__option--month[aria-disabled="true"]:hover,
|
|
44
|
+
.wds-Calendar__option--year[aria-disabled="true"]:hover,
|
|
45
|
+
.wds-Calendar__option--day.disabled:focus,
|
|
46
|
+
.wds-Calendar__option--month.disabled:focus,
|
|
47
|
+
.wds-Calendar__option--year.disabled:focus,
|
|
48
|
+
.wds-Calendar__option--day[aria-disabled="true"]:focus,
|
|
49
|
+
.wds-Calendar__option--month[aria-disabled="true"]:focus,
|
|
50
|
+
.wds-Calendar__option--year[aria-disabled="true"]:focus {
|
|
51
|
+
filter: initial;
|
|
52
|
+
opacity: 1;
|
|
53
|
+
opacity: initial;
|
|
54
|
+
background-color: var(--color-sentiment-background-surface);
|
|
55
|
+
color: var(--color-sentiment-content-primary);
|
|
56
|
+
}
|
|
57
|
+
.wds-Calendar__option--day.disabled.active,
|
|
58
|
+
.wds-Calendar__option--month.disabled.active,
|
|
59
|
+
.wds-Calendar__option--year.disabled.active,
|
|
60
|
+
.wds-Calendar__option--day[aria-disabled="true"].active,
|
|
61
|
+
.wds-Calendar__option--month[aria-disabled="true"].active,
|
|
62
|
+
.wds-Calendar__option--year[aria-disabled="true"].active,
|
|
63
|
+
.wds-Calendar__option--day.disabled.active:hover,
|
|
64
|
+
.wds-Calendar__option--month.disabled.active:hover,
|
|
65
|
+
.wds-Calendar__option--year.disabled.active:hover,
|
|
66
|
+
.wds-Calendar__option--day[aria-disabled="true"].active:hover,
|
|
67
|
+
.wds-Calendar__option--month[aria-disabled="true"].active:hover,
|
|
68
|
+
.wds-Calendar__option--year[aria-disabled="true"].active:hover,
|
|
69
|
+
.wds-Calendar__option--day.disabled.active:active,
|
|
70
|
+
.wds-Calendar__option--month.disabled.active:active,
|
|
71
|
+
.wds-Calendar__option--year.disabled.active:active,
|
|
72
|
+
.wds-Calendar__option--day[aria-disabled="true"].active:active,
|
|
73
|
+
.wds-Calendar__option--month[aria-disabled="true"].active:active,
|
|
74
|
+
.wds-Calendar__option--year[aria-disabled="true"].active:active,
|
|
75
|
+
.wds-Calendar__option--day.disabled.active:focus,
|
|
76
|
+
.wds-Calendar__option--month.disabled.active:focus,
|
|
77
|
+
.wds-Calendar__option--year.disabled.active:focus,
|
|
78
|
+
.wds-Calendar__option--day[aria-disabled="true"].active:focus,
|
|
79
|
+
.wds-Calendar__option--month[aria-disabled="true"].active:focus,
|
|
80
|
+
.wds-Calendar__option--year[aria-disabled="true"].active:focus {
|
|
81
|
+
filter: initial;
|
|
82
|
+
opacity: 1;
|
|
83
|
+
opacity: initial;
|
|
84
|
+
background-color: var(--color-sentiment-interactive-primary);
|
|
85
|
+
color: var(--color-sentiment-interactive-secondary);
|
|
86
|
+
}
|
|
87
|
+
.wds-Calendar__option--day.disabled.today,
|
|
88
|
+
.wds-Calendar__option--month.disabled.today,
|
|
89
|
+
.wds-Calendar__option--year.disabled.today,
|
|
90
|
+
.wds-Calendar__option--day[aria-disabled="true"].today,
|
|
91
|
+
.wds-Calendar__option--month[aria-disabled="true"].today,
|
|
92
|
+
.wds-Calendar__option--year[aria-disabled="true"].today {
|
|
93
|
+
text-decoration-color: #768e9c;
|
|
94
|
+
text-decoration-color: var(--color-content-tertiary);
|
|
95
|
+
}
|
|
96
|
+
.wds-Calendar__option--day {
|
|
97
|
+
height: 40px;
|
|
98
|
+
height: var(--size-40);
|
|
99
|
+
border-radius: 9999px;
|
|
100
|
+
border-radius: var(--radius-full);
|
|
101
|
+
line-height: 40px;
|
|
102
|
+
line-height: var(--size-40);
|
|
103
|
+
width: 40px;
|
|
104
|
+
width: var(--size-40);
|
|
105
|
+
padding: 0;
|
|
106
|
+
display: inline-flex;
|
|
107
|
+
align-items: center;
|
|
108
|
+
justify-content: center;
|
|
109
|
+
}
|
|
110
|
+
.wds-Calendar__option--day.today {
|
|
111
|
+
-webkit-text-decoration: underline;
|
|
112
|
+
text-decoration: underline;
|
|
113
|
+
text-decoration-thickness: 2px;
|
|
114
|
+
text-underline-offset: 4px;
|
|
115
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
@import (reference) "./../../styles/variables/neptune-tokens.less";
|
|
2
|
+
|
|
3
|
+
.wds-Calendar__option--day,
|
|
4
|
+
.wds-Calendar__option--month,
|
|
5
|
+
.wds-Calendar__option--year {
|
|
6
|
+
width: 100%;
|
|
7
|
+
padding: var(--size-4) 0;
|
|
8
|
+
@media (--screen-400-zoom) {
|
|
9
|
+
padding: 0;
|
|
10
|
+
}
|
|
11
|
+
border: transparent;
|
|
12
|
+
border-radius: var(--radius-small);
|
|
13
|
+
background-color: transparent;
|
|
14
|
+
font-weight: var(--font-weight-semi-bold);
|
|
15
|
+
color: var(--color-content-primary);
|
|
16
|
+
|
|
17
|
+
&:focus {
|
|
18
|
+
background-color: var(--color-background-screen-hover);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
&.disabled,
|
|
22
|
+
&[aria-disabled="true"] {
|
|
23
|
+
--ring-outline-color: var(--color-sentiment-content-primary);
|
|
24
|
+
color: var(--color-content-tertiary);
|
|
25
|
+
cursor: not-allowed;
|
|
26
|
+
|
|
27
|
+
&:hover,
|
|
28
|
+
&:focus {
|
|
29
|
+
filter: unset;
|
|
30
|
+
opacity: unset;
|
|
31
|
+
background-color: var(--color-sentiment-background-surface);
|
|
32
|
+
color: var(--color-sentiment-content-primary);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
&.active {
|
|
36
|
+
&,
|
|
37
|
+
&:hover,
|
|
38
|
+
&:active,
|
|
39
|
+
&:focus {
|
|
40
|
+
filter: unset;
|
|
41
|
+
opacity: unset;
|
|
42
|
+
background-color: var(--color-sentiment-interactive-primary);
|
|
43
|
+
color: var(--color-sentiment-interactive-secondary);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
&.today {
|
|
48
|
+
text-decoration-color: var(--color-content-tertiary);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.wds-Calendar__option--day {
|
|
54
|
+
height: var(--size-40);
|
|
55
|
+
border-radius: var(--radius-full);
|
|
56
|
+
line-height: var(--size-40);
|
|
57
|
+
width: var(--size-40);
|
|
58
|
+
padding: 0;
|
|
59
|
+
display: inline-flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
|
|
63
|
+
&.today {
|
|
64
|
+
text-decoration: underline;
|
|
65
|
+
text-decoration-thickness: 2px;
|
|
66
|
+
text-underline-offset: 4px;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import { useEffect, useRef } from 'react';
|
|
3
|
+
import { useIntl } from 'react-intl';
|
|
4
|
+
|
|
5
|
+
import messages from '../Calendar.messages';
|
|
6
|
+
import SentimentSurface from '../../SentimentSurface';
|
|
7
|
+
|
|
8
|
+
interface TableLinkProps {
|
|
9
|
+
item: number;
|
|
10
|
+
type: 'day' | 'month' | 'year';
|
|
11
|
+
title?: string;
|
|
12
|
+
longTitle?: string;
|
|
13
|
+
active: boolean;
|
|
14
|
+
outOfRange: boolean;
|
|
15
|
+
disabled: boolean;
|
|
16
|
+
today: boolean;
|
|
17
|
+
autofocus?: boolean;
|
|
18
|
+
disabledReasonId?: string;
|
|
19
|
+
announceReasonOnFocus?: boolean;
|
|
20
|
+
onClick: (item: number) => void;
|
|
21
|
+
onFocus?: (item: number) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const TableLink = ({
|
|
25
|
+
item,
|
|
26
|
+
type,
|
|
27
|
+
title,
|
|
28
|
+
longTitle,
|
|
29
|
+
active,
|
|
30
|
+
outOfRange,
|
|
31
|
+
disabled,
|
|
32
|
+
today,
|
|
33
|
+
autofocus,
|
|
34
|
+
disabledReasonId,
|
|
35
|
+
announceReasonOnFocus,
|
|
36
|
+
onClick,
|
|
37
|
+
onFocus,
|
|
38
|
+
}: TableLinkProps) => {
|
|
39
|
+
const buttonRef = useRef<HTMLButtonElement>(null);
|
|
40
|
+
// Distinguishes keyboard-driven .focus() (arrow keys) from screen-reader-initiated focus (VoiceOver cursor).
|
|
41
|
+
// Without this, the onFocus callback would re-trigger state updates that conflict with the already-committed focusedDate.
|
|
42
|
+
const isProgrammaticFocus = useRef(false);
|
|
43
|
+
const isMouseDown = useRef(false);
|
|
44
|
+
const intl = useIntl();
|
|
45
|
+
|
|
46
|
+
useEffect(() => {
|
|
47
|
+
if (autofocus) {
|
|
48
|
+
setTimeout(() => {
|
|
49
|
+
isProgrammaticFocus.current = true;
|
|
50
|
+
buttonRef.current?.focus();
|
|
51
|
+
isProgrammaticFocus.current = false;
|
|
52
|
+
}, 0);
|
|
53
|
+
}
|
|
54
|
+
}, [autofocus]);
|
|
55
|
+
|
|
56
|
+
const isUnavailable = outOfRange || disabled;
|
|
57
|
+
|
|
58
|
+
const onCalendarClick = (event: React.MouseEvent) => {
|
|
59
|
+
event.preventDefault();
|
|
60
|
+
onClick(item);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const onCalendarMouseDown = () => {
|
|
64
|
+
isMouseDown.current = true;
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const onCalendarFocus = () => {
|
|
68
|
+
if (!isProgrammaticFocus.current && !isMouseDown.current) {
|
|
69
|
+
onFocus?.(item);
|
|
70
|
+
}
|
|
71
|
+
isMouseDown.current = false;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const calculateAriaLabel = () => {
|
|
75
|
+
let label = longTitle || title || '';
|
|
76
|
+
if (active) {
|
|
77
|
+
label += `, ${intl.formatMessage(messages.selected)} ${intl.formatMessage(messages[type])}`;
|
|
78
|
+
}
|
|
79
|
+
if (isUnavailable) {
|
|
80
|
+
label += `, ${intl.formatMessage(messages.notAvailable)}`;
|
|
81
|
+
}
|
|
82
|
+
return label;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<SentimentSurface
|
|
87
|
+
// @ts-expect-error - Generic forwardRef limitation in SentimentSurface
|
|
88
|
+
ref={buttonRef}
|
|
89
|
+
as="button"
|
|
90
|
+
data-calendar-cell
|
|
91
|
+
aria-describedby={
|
|
92
|
+
isUnavailable || (autofocus && announceReasonOnFocus) ? disabledReasonId : undefined
|
|
93
|
+
}
|
|
94
|
+
aria-disabled={isUnavailable || undefined}
|
|
95
|
+
aria-label={calculateAriaLabel()}
|
|
96
|
+
aria-pressed={active}
|
|
97
|
+
className={clsx(
|
|
98
|
+
`wds-Calendar__option--${type}`,
|
|
99
|
+
`tw-date-lookup-${type}-option np-text-body-default-bold`,
|
|
100
|
+
{ active: !!active },
|
|
101
|
+
{ today: !!today },
|
|
102
|
+
{ disabled: !!isUnavailable },
|
|
103
|
+
)}
|
|
104
|
+
hasBaseStyles={false}
|
|
105
|
+
sentiment={isUnavailable ? 'negative' : 'neutral'}
|
|
106
|
+
tabIndex={autofocus ? 0 : -1}
|
|
107
|
+
type="button"
|
|
108
|
+
onClick={onCalendarClick}
|
|
109
|
+
onFocus={onCalendarFocus}
|
|
110
|
+
onMouseDown={onCalendarMouseDown}
|
|
111
|
+
>
|
|
112
|
+
{title || item}
|
|
113
|
+
</SentimentSurface>
|
|
114
|
+
);
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
export default TableLink;
|
|
@@ -2,10 +2,11 @@ import { formatDate } from '@transferwise/formatting';
|
|
|
2
2
|
import { useIntl } from 'react-intl';
|
|
3
3
|
|
|
4
4
|
import { getFocusableTime } from '../../GetFocusableTime/GetFocusableTime';
|
|
5
|
-
import TableLink from '../../
|
|
5
|
+
import TableLink from '../../TableLink';
|
|
6
6
|
|
|
7
7
|
interface YearCalendarTableProps {
|
|
8
8
|
selectedDate: Date | null;
|
|
9
|
+
focusedDate?: Date | null;
|
|
9
10
|
min: Date | null;
|
|
10
11
|
max: Date | null;
|
|
11
12
|
viewYear: number;
|
|
@@ -19,6 +20,7 @@ const YEAR_ONLY_FORMAT: Intl.DateTimeFormatOptions = { year: 'numeric' };
|
|
|
19
20
|
|
|
20
21
|
const YearCalendarTable = ({
|
|
21
22
|
selectedDate,
|
|
23
|
+
focusedDate,
|
|
22
24
|
min,
|
|
23
25
|
max,
|
|
24
26
|
viewYear,
|
|
@@ -34,7 +36,8 @@ const YearCalendarTable = ({
|
|
|
34
36
|
type="year"
|
|
35
37
|
title={formatDate(new Date(year, 0), locale, YEAR_ONLY_FORMAT)}
|
|
36
38
|
active={isActive(year)}
|
|
37
|
-
|
|
39
|
+
outOfRange={isDisabled(year)}
|
|
40
|
+
disabled={false}
|
|
38
41
|
today={isThisYear(year)}
|
|
39
42
|
autofocus={autofocusYear === year}
|
|
40
43
|
onClick={onSelect}
|
|
@@ -46,6 +49,10 @@ const YearCalendarTable = ({
|
|
|
46
49
|
return !!(year === selectedDate?.getFullYear());
|
|
47
50
|
};
|
|
48
51
|
|
|
52
|
+
const isFocused = (year: number) => {
|
|
53
|
+
return !!(year === focusedDate?.getFullYear());
|
|
54
|
+
};
|
|
55
|
+
|
|
49
56
|
const isThisYear = (year: number) => {
|
|
50
57
|
return year === new Date().getFullYear();
|
|
51
58
|
};
|
|
@@ -56,7 +63,12 @@ const YearCalendarTable = ({
|
|
|
56
63
|
|
|
57
64
|
const autofocusYear = (() => {
|
|
58
65
|
const years = Array.from({ length: ROWS * COLS }, (_, index) => startYear + index);
|
|
59
|
-
return getFocusableTime({
|
|
66
|
+
return getFocusableTime({
|
|
67
|
+
isActive: isFocused,
|
|
68
|
+
isNow: isThisYear,
|
|
69
|
+
isDisabled,
|
|
70
|
+
timeSpan: years,
|
|
71
|
+
});
|
|
60
72
|
})();
|
|
61
73
|
|
|
62
74
|
return (
|
|
@@ -8,6 +8,7 @@ import YearCalendarTable from './Table';
|
|
|
8
8
|
|
|
9
9
|
interface YearCalendarProps extends WrappedComponentProps {
|
|
10
10
|
selectedDate: Date | null;
|
|
11
|
+
focusedDate?: Date | null;
|
|
11
12
|
min: Date | null;
|
|
12
13
|
max: Date | null;
|
|
13
14
|
viewYear: number;
|
|
@@ -33,21 +34,29 @@ class YearCalendar extends PureComponent<YearCalendarProps> {
|
|
|
33
34
|
render() {
|
|
34
35
|
const {
|
|
35
36
|
selectedDate,
|
|
37
|
+
focusedDate,
|
|
36
38
|
min,
|
|
37
39
|
max,
|
|
38
40
|
viewYear,
|
|
39
41
|
placeholder,
|
|
40
42
|
intl: { formatMessage },
|
|
41
43
|
} = this.props;
|
|
44
|
+
const startYear = viewYear - (viewYear % 20);
|
|
45
|
+
const isPreviousDisabled = !!(min && startYear - 1 < min.getFullYear());
|
|
46
|
+
const isNextDisabled = !!(max && startYear + 20 > max.getFullYear());
|
|
47
|
+
|
|
42
48
|
return (
|
|
43
49
|
<>
|
|
44
50
|
<DateHeader
|
|
45
51
|
dateMode={formatMessage(messages.twentyYears)}
|
|
52
|
+
isPreviousDisabled={isPreviousDisabled}
|
|
53
|
+
isNextDisabled={isNextDisabled}
|
|
46
54
|
onPreviousClick={this.selectPreviousYears}
|
|
47
55
|
onNextClick={this.selectNextYears}
|
|
48
56
|
/>
|
|
49
57
|
<YearCalendarTable
|
|
50
58
|
selectedDate={selectedDate}
|
|
59
|
+
focusedDate={focusedDate}
|
|
51
60
|
min={min}
|
|
52
61
|
max={max}
|
|
53
62
|
viewYear={viewYear}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { clampToDayViewBoundary } from './clampToDayViewBoundary';
|
|
2
|
+
|
|
3
|
+
describe('clampToDayViewBoundary', () => {
|
|
4
|
+
describe('no boundaries', () => {
|
|
5
|
+
it('returns date unchanged when no min or max', () => {
|
|
6
|
+
const date = new Date(2000, 0, 15);
|
|
7
|
+
expect(clampToDayViewBoundary(date, null, null)).toBe(date);
|
|
8
|
+
});
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe('min boundary', () => {
|
|
12
|
+
it('returns date unchanged when within range', () => {
|
|
13
|
+
const date = new Date(2000, 0, 20);
|
|
14
|
+
const min = new Date(2000, 0, 15);
|
|
15
|
+
expect(clampToDayViewBoundary(date, min, null)).toBe(date);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('allows navigation past min within the same month', () => {
|
|
19
|
+
const date = new Date(2000, 0, 10);
|
|
20
|
+
const min = new Date(2000, 0, 15);
|
|
21
|
+
expect(clampToDayViewBoundary(date, min, null)).toBe(date);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('clamps to first of min month when navigating to a previous month', () => {
|
|
25
|
+
const date = new Date(1999, 11, 31);
|
|
26
|
+
const min = new Date(2000, 0, 15);
|
|
27
|
+
expect(clampToDayViewBoundary(date, min, null)).toEqual(new Date(2000, 0, 1));
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
it('clamps to first of min month across year boundary', () => {
|
|
31
|
+
const date = new Date(1999, 11, 25);
|
|
32
|
+
const min = new Date(2000, 0, 5);
|
|
33
|
+
expect(clampToDayViewBoundary(date, min, null)).toEqual(new Date(2000, 0, 1));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it('clamps to first of min month when min is on the 1st', () => {
|
|
37
|
+
const date = new Date(1999, 11, 31);
|
|
38
|
+
const min = new Date(2000, 0, 1);
|
|
39
|
+
expect(clampToDayViewBoundary(date, min, null)).toEqual(new Date(2000, 0, 1));
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('max boundary', () => {
|
|
44
|
+
it('returns date unchanged when within range', () => {
|
|
45
|
+
const date = new Date(2000, 0, 10);
|
|
46
|
+
const max = new Date(2000, 0, 15);
|
|
47
|
+
expect(clampToDayViewBoundary(date, null, max)).toBe(date);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('allows navigation past max within the same month', () => {
|
|
51
|
+
const date = new Date(2000, 0, 20);
|
|
52
|
+
const max = new Date(2000, 0, 15);
|
|
53
|
+
expect(clampToDayViewBoundary(date, null, max)).toBe(date);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('clamps to last of max month when navigating to a next month', () => {
|
|
57
|
+
const date = new Date(2000, 1, 1);
|
|
58
|
+
const max = new Date(2000, 0, 20);
|
|
59
|
+
expect(clampToDayViewBoundary(date, null, max)).toEqual(new Date(2000, 0, 31));
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('clamps to last of max month across year boundary', () => {
|
|
63
|
+
const date = new Date(2001, 0, 1);
|
|
64
|
+
const max = new Date(2000, 11, 25);
|
|
65
|
+
expect(clampToDayViewBoundary(date, null, max)).toEqual(new Date(2000, 11, 31));
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('clamps to last of max month when max is on the last day', () => {
|
|
69
|
+
const date = new Date(2000, 1, 1);
|
|
70
|
+
const max = new Date(2000, 0, 31);
|
|
71
|
+
expect(clampToDayViewBoundary(date, null, max)).toEqual(new Date(2000, 0, 31));
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('both boundaries', () => {
|
|
76
|
+
it('allows past min within same month when both min and max set', () => {
|
|
77
|
+
const date = new Date(2000, 0, 10);
|
|
78
|
+
const min = new Date(2000, 0, 15);
|
|
79
|
+
const max = new Date(2000, 0, 25);
|
|
80
|
+
expect(clampToDayViewBoundary(date, min, max)).toBe(date);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it('allows past max within same month when both min and max set', () => {
|
|
84
|
+
const date = new Date(2000, 0, 28);
|
|
85
|
+
const min = new Date(2000, 0, 15);
|
|
86
|
+
const max = new Date(2000, 0, 25);
|
|
87
|
+
expect(clampToDayViewBoundary(date, min, max)).toBe(date);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relaxed day-view keyboard clamping — allows navigation past min/max within the
|
|
3
|
+
* same calendar month, but blocks escape to a fully-outside month.
|
|
4
|
+
* Temporary measure; strict clamping may be restored.
|
|
5
|
+
* @see https://transferwise.atlassian.net/browse/DS-8518
|
|
6
|
+
*/
|
|
7
|
+
export function clampToDayViewBoundary(
|
|
8
|
+
date: Date,
|
|
9
|
+
min: Date | null,
|
|
10
|
+
max: Date | null,
|
|
11
|
+
): Date {
|
|
12
|
+
if (min && date < min) {
|
|
13
|
+
const sameMonth =
|
|
14
|
+
date.getMonth() === min.getMonth() && date.getFullYear() === min.getFullYear();
|
|
15
|
+
if (sameMonth) return date;
|
|
16
|
+
return new Date(min.getFullYear(), min.getMonth(), 1);
|
|
17
|
+
}
|
|
18
|
+
if (max && date > max) {
|
|
19
|
+
const sameMonth =
|
|
20
|
+
date.getMonth() === max.getMonth() && date.getFullYear() === max.getFullYear();
|
|
21
|
+
if (sameMonth) return date;
|
|
22
|
+
return new Date(max.getFullYear(), max.getMonth() + 1, 0);
|
|
23
|
+
}
|
|
24
|
+
return date;
|
|
25
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { hasDisabledDatesInMonth } from './hasDisabledDatesInMonth';
|
|
2
|
+
import type { DisabledRules } from '../../Calendar.types';
|
|
3
|
+
|
|
4
|
+
describe('hasDisabledDatesInMonth', () => {
|
|
5
|
+
describe('weekdays', () => {
|
|
6
|
+
it('returns true when weekday rules exist (every month has all weekdays)', () => {
|
|
7
|
+
const rules: DisabledRules = { weekdays: ['sat', 'sun'], reason: 'Closed' };
|
|
8
|
+
expect(hasDisabledDatesInMonth(6, 2024, rules)).toBe(true);
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
it('returns true for any month when weekday rules exist', () => {
|
|
12
|
+
const rules: DisabledRules = { weekdays: ['mon'], reason: 'Closed' };
|
|
13
|
+
expect(hasDisabledDatesInMonth(0, 2024, rules)).toBe(true);
|
|
14
|
+
expect(hasDisabledDatesInMonth(11, 2024, rules)).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('returns false when weekdays array is empty', () => {
|
|
18
|
+
const rules: DisabledRules = { weekdays: [], reason: 'None' };
|
|
19
|
+
expect(hasDisabledDatesInMonth(6, 2024, rules)).toBe(false);
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
describe('specific dates', () => {
|
|
24
|
+
const rules: DisabledRules = {
|
|
25
|
+
dates: [new Date(2024, 6, 11), new Date(2024, 6, 25), new Date(2025, 0, 1)],
|
|
26
|
+
reason: 'Bank holidays',
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
it('returns true when month contains a disabled date', () => {
|
|
30
|
+
expect(hasDisabledDatesInMonth(6, 2024, rules)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('returns false when month has no disabled dates', () => {
|
|
34
|
+
expect(hasDisabledDatesInMonth(5, 2024, rules)).toBe(false);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('returns true for another year with disabled date', () => {
|
|
38
|
+
expect(hasDisabledDatesInMonth(0, 2025, rules)).toBe(true);
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
describe('ranges', () => {
|
|
43
|
+
const rules: DisabledRules = {
|
|
44
|
+
ranges: [{ start: new Date(2024, 2, 29), end: new Date(2024, 3, 3) }],
|
|
45
|
+
reason: 'Maintenance',
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
it('returns true when range starts in the month', () => {
|
|
49
|
+
expect(hasDisabledDatesInMonth(2, 2024, rules)).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('returns true when range ends in the month', () => {
|
|
53
|
+
expect(hasDisabledDatesInMonth(3, 2024, rules)).toBe(true);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('returns true when range spans the entire month', () => {
|
|
57
|
+
const spanRules: DisabledRules = {
|
|
58
|
+
ranges: [{ start: new Date(2024, 0, 1), end: new Date(2024, 11, 31) }],
|
|
59
|
+
reason: 'Year-long',
|
|
60
|
+
};
|
|
61
|
+
expect(hasDisabledDatesInMonth(6, 2024, spanRules)).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('returns false when range does not overlap the month', () => {
|
|
65
|
+
expect(hasDisabledDatesInMonth(4, 2024, rules)).toBe(false);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe('min/max constraints', () => {
|
|
70
|
+
it('returns false when all disabled dates in month are outside min/max', () => {
|
|
71
|
+
const rules: DisabledRules = {
|
|
72
|
+
dates: [new Date(2024, 6, 1), new Date(2024, 6, 2)],
|
|
73
|
+
reason: 'Early',
|
|
74
|
+
};
|
|
75
|
+
const min = new Date(2024, 6, 10);
|
|
76
|
+
const max = new Date(2024, 6, 31);
|
|
77
|
+
expect(hasDisabledDatesInMonth(6, 2024, rules, min, max)).toBe(false);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
it('returns true when weekdays exist regardless of min/max', () => {
|
|
81
|
+
const rules: DisabledRules = { weekdays: ['sat'], reason: 'Closed' };
|
|
82
|
+
const min = new Date(2024, 6, 10);
|
|
83
|
+
const max = new Date(2024, 6, 20);
|
|
84
|
+
expect(hasDisabledDatesInMonth(6, 2024, rules, min, max)).toBe(true);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe('no rules', () => {
|
|
89
|
+
it('returns false when disabledRules is undefined', () => {
|
|
90
|
+
expect(hasDisabledDatesInMonth(6, 2024, undefined)).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('returns false when only reason is provided', () => {
|
|
94
|
+
const rules: DisabledRules = { reason: 'Reason only' };
|
|
95
|
+
expect(hasDisabledDatesInMonth(6, 2024, rules)).toBe(false);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|