@transferwise/components 46.12.0 → 46.14.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/index.esm.js +193 -352
- package/build/index.esm.js.map +1 -1
- package/build/index.js +166 -325
- package/build/index.js.map +1 -1
- package/build/main.css +6 -82
- package/build/styles/decision/Decision.css +6 -82
- package/build/styles/main.css +6 -82
- package/build/types/body/Body.d.ts +1 -1
- package/build/types/common/commonProps.d.ts +1 -1
- package/build/types/common/commonProps.d.ts.map +1 -1
- package/build/types/common/dateUtils/getDayNames/getDayNames.d.ts +1 -1
- package/build/types/common/dateUtils/getDayNames/getDayNames.d.ts.map +1 -1
- package/build/types/common/dateUtils/getDayNames/index.d.ts +1 -1
- package/build/types/common/dateUtils/getDayNames/index.d.ts.map +1 -1
- package/build/types/common/dateUtils/getMonthNames/getMonthNames.d.ts +1 -1
- package/build/types/common/dateUtils/getMonthNames/getMonthNames.d.ts.map +1 -1
- package/build/types/common/dateUtils/getMonthNames/index.d.ts +1 -1
- package/build/types/common/dateUtils/getMonthNames/index.d.ts.map +1 -1
- package/build/types/common/dateUtils/index.d.ts +6 -6
- package/build/types/common/dateUtils/index.d.ts.map +1 -1
- package/build/types/common/dateUtils/isDateValid/index.d.ts +1 -1
- package/build/types/common/dateUtils/isDateValid/index.d.ts.map +1 -1
- package/build/types/common/dateUtils/isDateValid/isDateValid.d.ts +2 -2
- package/build/types/common/dateUtils/isDateValid/isDateValid.d.ts.map +1 -1
- package/build/types/common/dateUtils/isMonthAndYearFormat/index.d.ts +1 -1
- package/build/types/common/dateUtils/isMonthAndYearFormat/index.d.ts.map +1 -1
- package/build/types/common/dateUtils/isMonthAndYearFormat/isMonthAndYearFormat.d.ts +1 -1
- package/build/types/common/dateUtils/isMonthAndYearFormat/isMonthAndYearFormat.d.ts.map +1 -1
- package/build/types/common/dateUtils/isWithinRange/index.d.ts +1 -1
- package/build/types/common/dateUtils/isWithinRange/index.d.ts.map +1 -1
- package/build/types/common/dateUtils/isWithinRange/isWithinRange.d.ts +1 -1
- package/build/types/common/dateUtils/isWithinRange/isWithinRange.d.ts.map +1 -1
- package/build/types/common/dateUtils/moveToWithinRange/index.d.ts +1 -1
- package/build/types/common/dateUtils/moveToWithinRange/index.d.ts.map +1 -1
- package/build/types/common/dateUtils/moveToWithinRange/moveToWithinRange.d.ts +1 -1
- package/build/types/common/dateUtils/moveToWithinRange/moveToWithinRange.d.ts.map +1 -1
- package/build/types/common/panel/Panel.d.ts +1 -1
- package/build/types/common/responsivePanel/ResponsivePanel.d.ts +1 -1
- package/build/types/dateInput/DateInput.d.ts +30 -41
- package/build/types/dateInput/DateInput.d.ts.map +1 -1
- package/build/types/dateInput/DateInput.messages.d.ts +24 -33
- package/build/types/dateInput/DateInput.messages.d.ts.map +1 -1
- package/build/types/dateInput/index.d.ts +2 -2
- package/build/types/dateInput/index.d.ts.map +1 -1
- package/build/types/dateInput/utils/convertToLocalMidnight/convertToLocalMidnight.d.ts +1 -1
- package/build/types/dateInput/utils/convertToLocalMidnight/convertToLocalMidnight.d.ts.map +1 -1
- package/build/types/dateInput/utils/convertToLocalMidnight/index.d.ts +1 -1
- package/build/types/dateInput/utils/convertToLocalMidnight/index.d.ts.map +1 -1
- package/build/types/dateInput/utils/index.d.ts +1 -2
- package/build/types/dateInput/utils/index.d.ts.map +1 -1
- package/build/types/dateLookup/DateLookup.d.ts +1 -0
- package/build/types/dateLookup/DateLookup.d.ts.map +1 -1
- package/build/types/decision/Decision.d.ts +39 -52
- package/build/types/decision/Decision.d.ts.map +1 -1
- package/build/types/decision/index.d.ts +1 -2
- package/build/types/decision/index.d.ts.map +1 -1
- package/build/types/dimmer/Dimmer.d.ts +1 -1
- package/build/types/drawer/Drawer.d.ts.map +1 -1
- package/build/types/index.d.ts +2 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/modal/Modal.d.ts.map +1 -1
- package/build/types/popover/Popover.d.ts +1 -0
- package/build/types/popover/Popover.d.ts.map +1 -1
- package/build/types/promoCard/PromoCard.d.ts +1 -2
- package/build/types/promoCard/PromoCard.d.ts.map +1 -1
- package/build/types/select/searchBox/SearchBox.d.ts +1 -1
- package/package.json +1 -1
- package/src/common/commonProps.ts +1 -1
- package/src/common/dateUtils/getDayNames/getDayNames.spec.js +2 -2
- package/src/common/dateUtils/getDayNames/{getDayNames.js → getDayNames.ts} +5 -2
- package/src/common/dateUtils/getMonthNames/getMonthNames.spec.js +9 -8
- package/src/common/dateUtils/getMonthNames/{getMonthNames.js → getMonthNames.ts} +5 -3
- package/src/common/dateUtils/isDateValid/{isDateValid.spec.js → isDateValid.spec.ts} +1 -1
- package/src/common/dateUtils/isDateValid/isDateValid.ts +13 -0
- package/src/common/dateUtils/isMonthAndYearFormat/isMonthAndYearFormat.spec.js +3 -7
- package/src/common/dateUtils/isMonthAndYearFormat/{isMonthAndYearFormat.js → isMonthAndYearFormat.ts} +1 -1
- package/src/common/dateUtils/isWithinRange/{isWithinRange.spec.js → isWithinRange.spec.ts} +0 -10
- package/src/common/dateUtils/isWithinRange/{isWithinRange.js → isWithinRange.ts} +1 -1
- package/src/common/dateUtils/moveToWithinRange/{moveToWithinRange.js → moveToWithinRange.ts} +2 -2
- package/src/dateInput/DateInput.spec.js +7 -56
- package/src/dateInput/DateInput.story.tsx +11 -8
- package/src/dateInput/{DateInput.js → DateInput.tsx} +116 -123
- package/src/dateInput/index.ts +2 -0
- package/src/dateInput/utils/convertToLocalMidnight/{convertToLocalMidnight.js → convertToLocalMidnight.ts} +1 -1
- package/src/dateInput/utils/{index.js → index.ts} +0 -1
- package/src/dateLookup/DateLookup.js +12 -1
- package/src/dateLookup/DateLookup.testingLibrary.spec.js +12 -1
- package/src/dateLookup/dayCalendar/table/DayCalendarTable.js +1 -0
- package/src/decision/Decision.css +6 -82
- package/src/decision/Decision.less +3 -41
- package/src/decision/Decision.spec.js +56 -61
- package/src/decision/{Decision.story.js → Decision.story.tsx} +5 -5
- package/src/decision/Decision.tsx +133 -0
- package/src/decision/index.ts +1 -0
- package/src/drawer/Drawer.js +13 -2
- package/src/drawer/__snapshots__/Drawer.rtl.spec.js.snap +1 -0
- package/src/index.ts +2 -0
- package/src/main.css +6 -82
- package/src/modal/Modal.tsx +6 -3
- package/src/popover/Popover.js +7 -3
- package/src/popover/Popover.spec.js +16 -8
- package/src/popover/__snapshots__/Popover.spec.js.snap +0 -56
- package/src/promoCard/PromoCard.tsx +1 -2
- package/src/tile/Tile.js +1 -1
- package/build/types/dateInput/utils/explodeDate/explodeDate.d.ts +0 -6
- package/build/types/dateInput/utils/explodeDate/explodeDate.d.ts.map +0 -1
- package/build/types/dateInput/utils/explodeDate/index.d.ts +0 -2
- package/build/types/dateInput/utils/explodeDate/index.d.ts.map +0 -1
- package/build/types/decision/decisionEnums.d.ts +0 -9
- package/build/types/decision/decisionEnums.d.ts.map +0 -1
- package/build/types/sizeSwapper/SizeSwapper.d.ts +0 -3
- package/build/types/sizeSwapper/SizeSwapper.d.ts.map +0 -1
- package/build/types/sizeSwapper/index.d.ts +0 -2
- package/build/types/sizeSwapper/index.d.ts.map +0 -1
- package/src/common/dateUtils/isDateValid/isDateValid.js +0 -6
- package/src/dateInput/index.js +0 -3
- package/src/dateInput/utils/explodeDate/explodeDate.js +0 -7
- package/src/dateInput/utils/explodeDate/explodeDate.spec.js +0 -11
- package/src/dateInput/utils/explodeDate/index.js +0 -1
- package/src/decision/Decision.js +0 -148
- package/src/decision/decisionEnums.ts +0 -11
- package/src/decision/index.js +0 -2
- package/src/sizeSwapper/SizeSwapper.js +0 -69
- package/src/sizeSwapper/SizeSwapper.spec.js +0 -100
- package/src/sizeSwapper/SizeSwapper.story.js +0 -34
- package/src/sizeSwapper/index.js +0 -1
- /package/src/common/dateUtils/getDayNames/{index.js → index.ts} +0 -0
- /package/src/common/dateUtils/getMonthNames/{index.js → index.ts} +0 -0
- /package/src/common/dateUtils/{index.js → index.ts} +0 -0
- /package/src/common/dateUtils/isDateValid/{index.js → index.ts} +0 -0
- /package/src/common/dateUtils/isMonthAndYearFormat/{index.js → index.ts} +0 -0
- /package/src/common/dateUtils/isWithinRange/{index.js → index.ts} +0 -0
- /package/src/common/dateUtils/moveToWithinRange/{index.js → index.ts} +0 -0
- /package/src/dateInput/{DateInput.messages.js → DateInput.messages.ts} +0 -0
- /package/src/dateInput/utils/convertToLocalMidnight/{convertToLocalMidnight.spec.js → convertToLocalMidnight.spec.ts} +0 -0
- /package/src/dateInput/utils/convertToLocalMidnight/{index.js → index.ts} +0 -0
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { userEvent, within } from '@storybook/test';
|
|
3
|
+
import * as React from 'react';
|
|
3
4
|
|
|
4
|
-
import { DateInput, Info, InlineAlert, Title, Typography } from '..';
|
|
5
|
+
import { DateInput, DateMode, Info, InlineAlert, Title, Typography } from '..';
|
|
5
6
|
import { lorem10, storyConfig } from '../test-utils';
|
|
6
7
|
|
|
8
|
+
import { DateInputProps } from './DateInput';
|
|
9
|
+
|
|
7
10
|
export default {
|
|
8
11
|
component: DateInput,
|
|
9
12
|
title: 'Forms/DateInput',
|
|
@@ -14,17 +17,15 @@ export default {
|
|
|
14
17
|
yearLabel: 'Year input',
|
|
15
18
|
yearAutoComplete: 'bday-year',
|
|
16
19
|
selectProps: {
|
|
17
|
-
|
|
18
|
-
'aria-label': 'Select month',
|
|
19
|
-
},
|
|
20
|
+
placeholder: 'Month',
|
|
20
21
|
},
|
|
21
22
|
},
|
|
22
23
|
tags: ['autodocs'],
|
|
23
24
|
} satisfies Meta<typeof DateInput>;
|
|
24
25
|
|
|
25
|
-
type Story = StoryObj<
|
|
26
|
+
type Story = StoryObj<DateInputProps>;
|
|
26
27
|
|
|
27
|
-
export const Basic:
|
|
28
|
+
export const Basic: StoryObj = {};
|
|
28
29
|
|
|
29
30
|
Basic.play = async ({ canvasElement }) => {
|
|
30
31
|
const canvas = within(canvasElement);
|
|
@@ -65,8 +66,10 @@ export const WithLabel: Story = {
|
|
|
65
66
|
label (rare use case)
|
|
66
67
|
</Title>
|
|
67
68
|
<fieldset>
|
|
68
|
-
<legend className="control-label">
|
|
69
|
-
|
|
69
|
+
<legend className="control-label">
|
|
70
|
+
Expiry Date for Credit Card (example of MONTH_YEAR mode)
|
|
71
|
+
</legend>
|
|
72
|
+
<DateInput {...args} mode={DateMode.MONTH_YEAR} />
|
|
70
73
|
</fieldset>
|
|
71
74
|
</>
|
|
72
75
|
);
|
|
@@ -1,64 +1,101 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
2
|
import { useState } from 'react';
|
|
4
3
|
import { useIntl } from 'react-intl';
|
|
5
4
|
|
|
6
|
-
import { Input, SelectInput, SelectInputOptionContent } from '..';
|
|
5
|
+
import { Input, SelectInput, SelectInputProps, SelectInputOptionContent } from '..';
|
|
7
6
|
import { Size, DateMode, MonthFormat } from '../common';
|
|
8
7
|
import { getMonthNames, isDateValid, isMonthAndYearFormat } from '../common/dateUtils';
|
|
9
8
|
|
|
10
9
|
import messages from './DateInput.messages';
|
|
11
|
-
import {
|
|
10
|
+
import { convertToLocalMidnight } from './utils';
|
|
12
11
|
|
|
13
12
|
const MonthBeforeDay = new Set(['en-US', 'ja-JP']);
|
|
14
|
-
|
|
13
|
+
|
|
14
|
+
export interface DateInputProps {
|
|
15
|
+
'aria-label'?: string;
|
|
16
|
+
'aria-labelledby'?: string;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
size?: Size.SMALL | Size.MEDIUM | Size.LARGE;
|
|
19
|
+
value?: Date | string;
|
|
20
|
+
onChange: (value: string | null) => void;
|
|
21
|
+
onFocus?: React.FocusEventHandler<HTMLInputElement>;
|
|
22
|
+
onBlur?: React.FocusEventHandler<HTMLInputElement>;
|
|
23
|
+
dayLabel?: string;
|
|
24
|
+
dayAutoComplete?: string;
|
|
25
|
+
monthLabel?: string;
|
|
26
|
+
yearLabel?: string;
|
|
27
|
+
yearAutoComplete?: string;
|
|
28
|
+
monthFormat?: `${MonthFormat}`;
|
|
29
|
+
mode?: `${DateMode}`;
|
|
30
|
+
placeholders?: {
|
|
31
|
+
day?: string;
|
|
32
|
+
month?: string;
|
|
33
|
+
year?: string;
|
|
34
|
+
};
|
|
35
|
+
id?: string;
|
|
36
|
+
selectProps?: Partial<SelectInputProps<number | null>>;
|
|
37
|
+
}
|
|
15
38
|
|
|
16
39
|
const DateInput = ({
|
|
17
40
|
'aria-labelledby': ariaLabelledBy,
|
|
18
41
|
'aria-label': ariaLabel,
|
|
19
|
-
disabled,
|
|
20
|
-
size,
|
|
42
|
+
disabled = false,
|
|
43
|
+
size = Size.MEDIUM,
|
|
21
44
|
value,
|
|
22
45
|
dayLabel,
|
|
23
46
|
dayAutoComplete,
|
|
24
47
|
monthLabel,
|
|
25
48
|
yearLabel,
|
|
26
49
|
yearAutoComplete,
|
|
27
|
-
monthFormat,
|
|
28
|
-
mode,
|
|
50
|
+
monthFormat = MonthFormat.LONG,
|
|
51
|
+
mode = DateMode.DAY_MONTH_YEAR,
|
|
29
52
|
onChange,
|
|
30
53
|
onFocus,
|
|
31
54
|
onBlur,
|
|
32
55
|
placeholders,
|
|
33
56
|
id,
|
|
34
|
-
selectProps,
|
|
35
|
-
}) => {
|
|
57
|
+
selectProps = {},
|
|
58
|
+
}: DateInputProps) => {
|
|
36
59
|
const { locale, formatMessage } = useIntl();
|
|
37
|
-
|
|
60
|
+
|
|
61
|
+
const getDateObject = (): Date | undefined => {
|
|
38
62
|
if (value && isDateValid(value)) {
|
|
39
63
|
return typeof value === 'string' ? convertToLocalMidnight(value) : value;
|
|
40
64
|
}
|
|
41
|
-
return
|
|
65
|
+
return undefined;
|
|
42
66
|
};
|
|
43
67
|
|
|
44
|
-
const
|
|
45
|
-
let explodedDate = INITIAL_DEFAULT_STATE;
|
|
46
|
-
|
|
68
|
+
const getInitialDate = (unit: 'year' | 'month' | 'day'): number | null => {
|
|
47
69
|
if (value && isDateValid(value)) {
|
|
48
70
|
const dateObject = getDateObject();
|
|
49
|
-
explodedDate = explodeDate(dateObject);
|
|
50
71
|
|
|
51
|
-
if (isMonthAndYearFormat(value)) {
|
|
52
|
-
|
|
72
|
+
if (typeof value === 'string' && isMonthAndYearFormat(value) && unit === 'day') {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (dateObject !== undefined) {
|
|
77
|
+
switch (unit) {
|
|
78
|
+
case 'year':
|
|
79
|
+
return dateObject.getFullYear();
|
|
80
|
+
case 'month':
|
|
81
|
+
return dateObject.getMonth();
|
|
82
|
+
case 'day':
|
|
83
|
+
return dateObject.getDate();
|
|
84
|
+
default:
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
53
87
|
}
|
|
54
88
|
}
|
|
55
|
-
return
|
|
89
|
+
return null;
|
|
56
90
|
};
|
|
57
91
|
|
|
58
|
-
const [day, setDay] = useState(() =>
|
|
59
|
-
const [month, setMonth] = useState(() =>
|
|
60
|
-
const [year, setYear] = useState(() =>
|
|
61
|
-
const [lastBroadcastedValue, setLastBroadcastedValue] = useState(
|
|
92
|
+
const [day, setDay] = useState(() => getInitialDate('day'));
|
|
93
|
+
const [month, setMonth] = useState(() => getInitialDate('month'));
|
|
94
|
+
const [year, setYear] = useState(() => getInitialDate('year'));
|
|
95
|
+
const [lastBroadcastedValue, setLastBroadcastedValue] = useState<Date | null | undefined>(
|
|
96
|
+
getDateObject,
|
|
97
|
+
);
|
|
98
|
+
const monthNames = getMonthNames(locale, monthFormat);
|
|
62
99
|
|
|
63
100
|
dayLabel = dayLabel || formatMessage(messages.dayLabel);
|
|
64
101
|
monthLabel = monthLabel || formatMessage(messages.monthLabel);
|
|
@@ -69,7 +106,7 @@ const DateInput = ({
|
|
|
69
106
|
year: placeholders?.year || formatMessage(messages.yearPlaceholder),
|
|
70
107
|
};
|
|
71
108
|
|
|
72
|
-
const getDateAsString = (date) => {
|
|
109
|
+
const getDateAsString = (date: Date) => {
|
|
73
110
|
if (!isDateValid(date)) {
|
|
74
111
|
return '';
|
|
75
112
|
}
|
|
@@ -87,19 +124,19 @@ const DateInput = ({
|
|
|
87
124
|
};
|
|
88
125
|
|
|
89
126
|
const getSelectElement = () => {
|
|
90
|
-
const monthOptions = getMonthsOptions();
|
|
91
|
-
|
|
92
127
|
return (
|
|
93
128
|
<label className="d-flex flex-column">
|
|
94
129
|
<span className="sr-only">{monthLabel}</span>
|
|
95
130
|
<SelectInput
|
|
96
131
|
name="month"
|
|
97
132
|
disabled={disabled}
|
|
98
|
-
placeholder={placeholders
|
|
99
|
-
items={
|
|
133
|
+
placeholder={placeholders?.month}
|
|
134
|
+
items={Array.from({ length: 12 }, (_, index) => ({ type: 'option', value: index }))}
|
|
100
135
|
size={size}
|
|
101
|
-
value={
|
|
102
|
-
renderValue={(
|
|
136
|
+
value={month}
|
|
137
|
+
renderValue={(selectedValue) => (
|
|
138
|
+
<SelectInputOptionContent title={monthNames[selectedValue]} />
|
|
139
|
+
)}
|
|
103
140
|
onChange={(selectedValue) => handleMonthChange(selectedValue)}
|
|
104
141
|
{...selectProps}
|
|
105
142
|
/>
|
|
@@ -107,106 +144,92 @@ const DateInput = ({
|
|
|
107
144
|
);
|
|
108
145
|
};
|
|
109
146
|
|
|
110
|
-
const getMonthsOptions = () => {
|
|
111
|
-
const options = [];
|
|
112
|
-
const months = getMonthNames(locale, monthFormat);
|
|
113
|
-
|
|
114
|
-
months.forEach((label, index) => {
|
|
115
|
-
options.push({ type: 'option', value: { label, value: index } });
|
|
116
|
-
});
|
|
117
|
-
return options;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
147
|
const handleInternalValue = (newDay = day, newMonth = month, newYear = year) => {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
148
|
+
if (newDay == null || newMonth == null || newYear == null) {
|
|
149
|
+
broadcastNewValue(null);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
125
152
|
|
|
126
|
-
const
|
|
153
|
+
const dateValue = new Date(newYear, newMonth, newDay);
|
|
127
154
|
|
|
128
|
-
if (!
|
|
129
|
-
broadcastNewValue(
|
|
155
|
+
if (!isDateValid(dateValue)) {
|
|
156
|
+
broadcastNewValue(null);
|
|
157
|
+
return;
|
|
130
158
|
}
|
|
131
159
|
|
|
132
160
|
if (mode === DateMode.MONTH_YEAR) {
|
|
133
|
-
if (newMonth
|
|
134
|
-
broadcastNewValue(
|
|
161
|
+
if (newMonth !== month || newYear !== year) {
|
|
162
|
+
broadcastNewValue(dateValue);
|
|
135
163
|
}
|
|
136
|
-
} else if (
|
|
137
|
-
|
|
138
|
-
newMonth >= 0 &&
|
|
139
|
-
newYear &&
|
|
140
|
-
(newDay !== day || newMonth !== month || newYear !== year)
|
|
141
|
-
) {
|
|
142
|
-
broadcastNewValue(newValue);
|
|
164
|
+
} else if (newDay !== day || newMonth !== month || newYear !== year) {
|
|
165
|
+
broadcastNewValue(dateValue);
|
|
143
166
|
}
|
|
144
167
|
};
|
|
145
168
|
|
|
146
|
-
const handleDayChange = (event) => {
|
|
147
|
-
const { checkedDay } = checkDate(event.target.value, month, year);
|
|
169
|
+
const handleDayChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
170
|
+
const { checkedDay } = checkDate(Number.parseInt(event.target.value, 10), month, year);
|
|
148
171
|
setDay(checkedDay);
|
|
149
172
|
handleInternalValue(checkedDay, month, year);
|
|
150
173
|
};
|
|
151
174
|
|
|
152
|
-
const handleMonthChange = (
|
|
153
|
-
if (
|
|
175
|
+
const handleMonthChange = (selectedMonth: number | null) => {
|
|
176
|
+
if (selectedMonth === null) {
|
|
154
177
|
setMonth(null);
|
|
155
178
|
handleInternalValue(day, null, year);
|
|
156
179
|
return;
|
|
157
180
|
}
|
|
158
|
-
const selectedMonth = selectedValue ? selectedValue.value : 0;
|
|
159
181
|
const { checkedDay } = checkDate(day, selectedMonth, year);
|
|
160
182
|
setMonth(selectedMonth);
|
|
161
|
-
if (day) {
|
|
162
|
-
|
|
163
|
-
setDay(checkedDay);
|
|
164
|
-
}
|
|
183
|
+
if (day && checkedDay !== day) {
|
|
184
|
+
setDay(checkedDay);
|
|
165
185
|
}
|
|
166
186
|
handleInternalValue(checkedDay, selectedMonth, year);
|
|
167
187
|
};
|
|
168
188
|
|
|
169
|
-
const handleYearChange = (event) => {
|
|
189
|
+
const handleYearChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
170
190
|
const newValue = event.target.value;
|
|
171
191
|
const slicedYear = newValue.length > 4 ? newValue.slice(0, 4) : newValue;
|
|
172
192
|
|
|
173
193
|
if (slicedYear.toString().length === 4) {
|
|
174
194
|
// Correct day based on year and month.
|
|
175
|
-
const { checkedDay } = checkDate(day, month, newValue);
|
|
195
|
+
const { checkedDay } = checkDate(day, month, Number.parseInt(newValue, 10));
|
|
176
196
|
|
|
177
|
-
if (day) {
|
|
178
|
-
|
|
179
|
-
setDay(checkedDay);
|
|
180
|
-
}
|
|
197
|
+
if (day && checkedDay !== day) {
|
|
198
|
+
setDay(checkedDay);
|
|
181
199
|
}
|
|
182
|
-
|
|
183
|
-
|
|
200
|
+
|
|
201
|
+
setYear(Number.parseInt(slicedYear, 10));
|
|
202
|
+
handleInternalValue(checkedDay, month, Number.parseInt(slicedYear, 10));
|
|
184
203
|
} else {
|
|
185
|
-
setYear(slicedYear);
|
|
204
|
+
setYear(Number.parseInt(slicedYear, 10));
|
|
186
205
|
handleInternalValue(day, month, null);
|
|
187
206
|
}
|
|
188
207
|
};
|
|
189
208
|
|
|
190
|
-
const broadcastNewValue = (newValue) => {
|
|
209
|
+
const broadcastNewValue = (newValue: Date | null) => {
|
|
191
210
|
if (newValue !== lastBroadcastedValue) {
|
|
192
211
|
setLastBroadcastedValue(newValue);
|
|
193
|
-
onChange(getDateAsString(newValue)
|
|
212
|
+
onChange(newValue != null ? getDateAsString(newValue) : null);
|
|
194
213
|
}
|
|
195
214
|
};
|
|
196
215
|
|
|
197
|
-
const checkDate = (
|
|
216
|
+
const checkDate = (
|
|
217
|
+
newDay: number | null = null,
|
|
218
|
+
newMonth: number | null = 0,
|
|
219
|
+
newYear: number | null = null,
|
|
220
|
+
) => {
|
|
198
221
|
let checkedDay = newDay;
|
|
199
|
-
const maxDay = new Date(newYear || 2000, newMonth + 1, 0).getDate();
|
|
222
|
+
const maxDay = new Date(newYear || 2000, newMonth != null ? newMonth + 1 : 1, 0).getDate();
|
|
200
223
|
|
|
201
224
|
if (!newDay) {
|
|
202
225
|
checkedDay = null;
|
|
203
226
|
}
|
|
204
227
|
|
|
205
|
-
if (
|
|
228
|
+
if (newDay && newDay < 0) {
|
|
206
229
|
checkedDay = 1;
|
|
207
230
|
}
|
|
208
231
|
|
|
209
|
-
if ((newDay && newMonth) || newDay > 31) {
|
|
232
|
+
if ((newDay && newMonth) || (newDay && newDay > 31)) {
|
|
210
233
|
checkedDay = newDay > maxDay ? maxDay : newDay;
|
|
211
234
|
}
|
|
212
235
|
|
|
@@ -228,11 +251,12 @@ const DateInput = ({
|
|
|
228
251
|
id={id}
|
|
229
252
|
aria-labelledby={ariaLabelledBy}
|
|
230
253
|
aria-label={ariaLabel}
|
|
231
|
-
|
|
232
|
-
|
|
254
|
+
role="group" // Add role attribute to indicate container for interactive elements
|
|
255
|
+
onFocus={(event: React.FocusEvent<HTMLInputElement>) =>
|
|
256
|
+
shouldPropagateOnFocus(event) ? onFocus && onFocus(event) : event.stopPropagation()
|
|
233
257
|
}
|
|
234
|
-
onBlur={(event) =>
|
|
235
|
-
shouldPropagateOnBlur(event) ? onBlur && onBlur() : event.stopPropagation()
|
|
258
|
+
onBlur={(event: React.FocusEvent<HTMLInputElement>) =>
|
|
259
|
+
shouldPropagateOnBlur(event) ? onBlur && onBlur(event) : event.stopPropagation()
|
|
236
260
|
}
|
|
237
261
|
>
|
|
238
262
|
<div className="row">
|
|
@@ -282,14 +306,20 @@ const DateInput = ({
|
|
|
282
306
|
};
|
|
283
307
|
|
|
284
308
|
// Should only propagate if the relatedTarget is not part of this DateInput component.
|
|
285
|
-
function shouldPropagateOnFocus({
|
|
309
|
+
function shouldPropagateOnFocus({
|
|
310
|
+
target,
|
|
311
|
+
relatedTarget,
|
|
312
|
+
}: Pick<React.FocusEvent, 'target' | 'relatedTarget'>) {
|
|
286
313
|
const targetParent = target.closest('.tw-date');
|
|
287
314
|
const relatedParent = relatedTarget && relatedTarget.closest('.tw-date');
|
|
288
315
|
return targetParent !== relatedParent;
|
|
289
316
|
}
|
|
290
317
|
|
|
291
318
|
// Should only propagate if the relatedTarget or the activeElement is not part of this DateInput component.
|
|
292
|
-
function shouldPropagateOnBlur({
|
|
319
|
+
function shouldPropagateOnBlur({
|
|
320
|
+
target,
|
|
321
|
+
relatedTarget,
|
|
322
|
+
}: Pick<React.FocusEvent, 'target' | 'relatedTarget'>) {
|
|
293
323
|
const blurElementParent = target.closest('.tw-date');
|
|
294
324
|
// Even though FocusEvent.relatedTarget is supported by IE
|
|
295
325
|
// (https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget)
|
|
@@ -303,41 +333,4 @@ function shouldPropagateOnBlur({ target, relatedTarget }) {
|
|
|
303
333
|
return blurElementParent !== focusElementParent;
|
|
304
334
|
}
|
|
305
335
|
|
|
306
|
-
DateInput.propTypes = {
|
|
307
|
-
'aria-label': PropTypes.string,
|
|
308
|
-
'aria-labelledby': PropTypes.string,
|
|
309
|
-
disabled: PropTypes.bool,
|
|
310
|
-
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
311
|
-
value: PropTypes.oneOfType([PropTypes.string, PropTypes.instanceOf(Date)]),
|
|
312
|
-
onChange: PropTypes.func.isRequired,
|
|
313
|
-
onFocus: PropTypes.func,
|
|
314
|
-
onBlur: PropTypes.func,
|
|
315
|
-
dayLabel: PropTypes.string,
|
|
316
|
-
dayAutoComplete: PropTypes.string,
|
|
317
|
-
monthLabel: PropTypes.string,
|
|
318
|
-
yearLabel: PropTypes.string,
|
|
319
|
-
yearAutoComplete: PropTypes.string,
|
|
320
|
-
monthFormat: PropTypes.oneOf(['long', 'short']),
|
|
321
|
-
mode: PropTypes.oneOf(['day-month-year', 'month-year']),
|
|
322
|
-
placeholders: PropTypes.shape({
|
|
323
|
-
day: PropTypes.node,
|
|
324
|
-
month: PropTypes.node,
|
|
325
|
-
year: PropTypes.node,
|
|
326
|
-
}),
|
|
327
|
-
id: PropTypes.string,
|
|
328
|
-
selectProps: PropTypes.object,
|
|
329
|
-
};
|
|
330
|
-
|
|
331
|
-
DateInput.defaultProps = {
|
|
332
|
-
disabled: false,
|
|
333
|
-
size: Size.MEDIUM,
|
|
334
|
-
value: null,
|
|
335
|
-
onFocus: null,
|
|
336
|
-
onBlur: null,
|
|
337
|
-
monthFormat: MonthFormat.LONG,
|
|
338
|
-
mode: DateMode.DAY_MONTH_YEAR,
|
|
339
|
-
id: '',
|
|
340
|
-
selectProps: {},
|
|
341
|
-
};
|
|
342
|
-
|
|
343
336
|
export default DateInput;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const convertToLocalMidnight = (date) => {
|
|
1
|
+
export const convertToLocalMidnight = (date: string) => {
|
|
2
2
|
const utcDate = new Date(date.split('T')[0]); // using YYYY-MM-DD creates UTC date
|
|
3
3
|
|
|
4
4
|
return new Date(utcDate.getUTCFullYear(), utcDate.getUTCMonth(), utcDate.getUTCDate());
|
|
@@ -240,10 +240,20 @@ class DateLookup extends PureComponent {
|
|
|
240
240
|
render() {
|
|
241
241
|
const { selectedDate, open } = this.state;
|
|
242
242
|
|
|
243
|
-
const {
|
|
243
|
+
const {
|
|
244
|
+
size,
|
|
245
|
+
placeholder,
|
|
246
|
+
label,
|
|
247
|
+
'aria-labelledby': ariaLabelledBy,
|
|
248
|
+
monthFormat,
|
|
249
|
+
disabled,
|
|
250
|
+
clearable,
|
|
251
|
+
value,
|
|
252
|
+
} = this.props;
|
|
244
253
|
return (
|
|
245
254
|
<div // eslint-disable-line jsx-a11y/no-static-element-interactions
|
|
246
255
|
ref={this.element}
|
|
256
|
+
aria-labelledby={ariaLabelledBy}
|
|
247
257
|
className="input-group"
|
|
248
258
|
onKeyDown={this.handleKeyDown}
|
|
249
259
|
>
|
|
@@ -273,6 +283,7 @@ DateLookup.propTypes = {
|
|
|
273
283
|
size: PropTypes.oneOf(['sm', 'md', 'lg']),
|
|
274
284
|
placeholder: PropTypes.string,
|
|
275
285
|
label: PropTypes.string,
|
|
286
|
+
'aria-labelledby': PropTypes.string,
|
|
276
287
|
monthFormat: PropTypes.oneOf(['long', 'short']),
|
|
277
288
|
disabled: PropTypes.bool,
|
|
278
289
|
onChange: PropTypes.func.isRequired,
|
|
@@ -32,12 +32,19 @@ describe('DateLookup (events)', () => {
|
|
|
32
32
|
size: 'lg',
|
|
33
33
|
placeholder: 'Asd..',
|
|
34
34
|
label: 'label',
|
|
35
|
+
'aria-labelledby': 'prioritized-label',
|
|
35
36
|
onChange: jest.fn(),
|
|
36
37
|
onClick: jest.fn(),
|
|
37
38
|
disabled: false,
|
|
38
39
|
clearable: false,
|
|
39
40
|
};
|
|
40
|
-
({ container } = render(
|
|
41
|
+
({ container } = render(
|
|
42
|
+
<>
|
|
43
|
+
{/* eslint-disable-next-line jsx-a11y/label-has-associated-control */}
|
|
44
|
+
<label id="prioritized-label">Prioritized label</label>
|
|
45
|
+
<DateLookup {...props} />
|
|
46
|
+
</>,
|
|
47
|
+
));
|
|
41
48
|
});
|
|
42
49
|
|
|
43
50
|
afterEach(() => {
|
|
@@ -114,6 +121,10 @@ describe('DateLookup (events)', () => {
|
|
|
114
121
|
expect(screen.getByRole('button', { name: /selected day/i })).toBeInTheDocument();
|
|
115
122
|
});
|
|
116
123
|
|
|
124
|
+
it('supports custom `aria-labelledby` attribute', () => {
|
|
125
|
+
expect(screen.getByLabelText('Prioritized label')).toHaveClass('input-group');
|
|
126
|
+
});
|
|
127
|
+
|
|
117
128
|
describe('when is clearable', () => {
|
|
118
129
|
beforeEach(() => {
|
|
119
130
|
props = { value: date, onChange: jest.fn(), clearable: true };
|
|
@@ -1,93 +1,17 @@
|
|
|
1
1
|
.np-decision .decision {
|
|
2
2
|
display: flex;
|
|
3
3
|
}
|
|
4
|
-
.np-decision__tile--small + .np-decision__tile--small {
|
|
5
|
-
margin-left: 16px;
|
|
6
|
-
margin-left: var(--size-16);
|
|
7
|
-
}
|
|
8
|
-
[dir="rtl"] .np-decision__tile--small + .np-decision__tile--small {
|
|
9
|
-
margin-right: 16px;
|
|
10
|
-
margin-right: var(--size-16);
|
|
11
|
-
margin-left: 0;
|
|
12
|
-
margin-left: initial;
|
|
13
|
-
}
|
|
14
|
-
.np-decision.np-decision--grid .np-decision__tile--small {
|
|
15
|
-
margin-left: 0;
|
|
16
|
-
margin-bottom: 16px !important;
|
|
17
|
-
margin-bottom: var(--size-16) !important;
|
|
18
|
-
}
|
|
19
|
-
[dir="rtl"] .np-decision.np-decision--grid .np-decision__tile--small {
|
|
20
|
-
margin-right: 0;
|
|
21
|
-
margin-left: 0;
|
|
22
|
-
margin-left: initial;
|
|
23
|
-
}
|
|
24
|
-
.np-decision.np-decision--grid .np-decision__tile--small:not(:last-of-type) {
|
|
25
|
-
margin-right: 16px;
|
|
26
|
-
margin-right: var(--size-16);
|
|
27
|
-
}
|
|
28
|
-
[dir="rtl"] .np-decision.np-decision--grid .np-decision__tile--small:not(:last-of-type) {
|
|
29
|
-
margin-left: 16px;
|
|
30
|
-
margin-left: var(--size-16);
|
|
31
|
-
margin-right: 0;
|
|
32
|
-
margin-right: initial;
|
|
33
|
-
}
|
|
34
|
-
.np-decision__tile + .np-decision__tile {
|
|
35
|
-
margin-left: 24px;
|
|
36
|
-
margin-left: var(--size-24);
|
|
37
|
-
}
|
|
38
|
-
[dir="rtl"] .np-decision__tile + .np-decision__tile {
|
|
39
|
-
margin-right: 24px;
|
|
40
|
-
margin-right: var(--size-24);
|
|
41
|
-
margin-left: 0;
|
|
42
|
-
margin-left: initial;
|
|
43
|
-
}
|
|
44
|
-
.np-decision .np-text-title-subsection {
|
|
45
|
-
margin-bottom: 0;
|
|
46
|
-
}
|
|
47
4
|
.np-decision__tile--small .np-text-title-subsection {
|
|
48
5
|
font-size: 1rem;
|
|
49
6
|
font-size: var(--font-size-16);
|
|
50
7
|
line-height: 1.2;
|
|
51
8
|
line-height: var(--line-height-title);
|
|
52
|
-
margin-bottom: 0;
|
|
53
|
-
}
|
|
54
|
-
.np-decision.np-decision--grid .np-decision__tile {
|
|
55
|
-
margin-left: 0;
|
|
56
|
-
margin-bottom: 24px !important;
|
|
57
|
-
margin-bottom: var(--size-24) !important;
|
|
58
|
-
}
|
|
59
|
-
[dir="rtl"] .np-decision.np-decision--grid .np-decision__tile {
|
|
60
|
-
margin-right: 0;
|
|
61
|
-
margin-left: 0;
|
|
62
|
-
margin-left: initial;
|
|
63
|
-
}
|
|
64
|
-
.np-decision.np-decision--grid .np-decision__tile:not(:last-of-type) {
|
|
65
|
-
margin-right: 24px;
|
|
66
|
-
margin-right: var(--size-24);
|
|
67
|
-
}
|
|
68
|
-
[dir="rtl"] .np-decision.np-decision--grid .np-decision__tile:not(:last-of-type) {
|
|
69
|
-
margin-left: 24px;
|
|
70
|
-
margin-left: var(--size-24);
|
|
71
|
-
margin-right: 0;
|
|
72
|
-
margin-right: initial;
|
|
73
|
-
}
|
|
74
|
-
.np-decision.np-decision--grid .np-size-swapper {
|
|
75
|
-
margin-right: calc(0 - 24px);
|
|
76
|
-
margin-right: calc(0 - var(--size-24));
|
|
77
|
-
}
|
|
78
|
-
[dir="rtl"] .np-decision.np-decision--grid .np-size-swapper {
|
|
79
|
-
margin-left: calc(0 - 24px);
|
|
80
|
-
margin-left: calc(0 - var(--size-24));
|
|
81
|
-
margin-right: 0;
|
|
82
|
-
margin-right: initial;
|
|
83
9
|
}
|
|
84
|
-
.np-decision.
|
|
85
|
-
|
|
86
|
-
|
|
10
|
+
.np-decision:not(.flex-column) {
|
|
11
|
+
gap: 24px;
|
|
12
|
+
gap: var(--size-24);
|
|
87
13
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
margin-right: 0;
|
|
92
|
-
margin-right: initial;
|
|
14
|
+
.np-decision:not(.flex-column).np-decision--small {
|
|
15
|
+
gap: 16px;
|
|
16
|
+
gap: var(--size-16);
|
|
93
17
|
}
|
|
@@ -5,56 +5,18 @@
|
|
|
5
5
|
display: flex;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
&__tile--small + &__tile--small {
|
|
9
|
-
.margin(left, var(--size-16));
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
&.np-decision--grid &__tile--small {
|
|
13
|
-
.margin(left, 0);
|
|
14
|
-
|
|
15
|
-
margin-bottom: var(--size-16) !important;
|
|
16
|
-
|
|
17
|
-
&:not(:last-of-type) {
|
|
18
|
-
.margin(right, var(--size-16));
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
&__tile + &__tile {
|
|
23
|
-
.margin(left, var(--size-24));
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
.np-text-title-subsection {
|
|
27
|
-
margin-bottom: 0;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
8
|
&__tile--small {
|
|
31
9
|
.np-text-title-subsection {
|
|
32
10
|
font-size: var(--font-size-16);
|
|
33
11
|
line-height: var(--line-height-title);
|
|
34
|
-
margin-bottom: 0;
|
|
35
12
|
}
|
|
36
13
|
}
|
|
37
14
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
.margin(left, 0);
|
|
41
|
-
|
|
42
|
-
margin-bottom: var(--size-24) !important;
|
|
43
|
-
|
|
44
|
-
&:not(:last-of-type) {
|
|
45
|
-
.margin(right, var(--size-24));
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
&.np-decision--grid {
|
|
50
|
-
.np-size-swapper {
|
|
51
|
-
.margin(right, calc(0 - var(--size-24)));
|
|
52
|
-
}
|
|
15
|
+
&:not(.flex-column) {
|
|
16
|
+
gap: var(--size-24);
|
|
53
17
|
|
|
54
18
|
&.np-decision--small {
|
|
55
|
-
|
|
56
|
-
.margin(right, calc(0 - var(--size-16)));
|
|
57
|
-
}
|
|
19
|
+
gap: var(--size-16);
|
|
58
20
|
}
|
|
59
21
|
}
|
|
60
22
|
}
|