@transferwise/components 0.0.0-experimental-ff4fa52 → 0.0.0-experimental-1413ac0
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/README.md +14 -1
- package/build/i18n/zh-HK.json +2 -2
- package/build/index.esm.js +182 -273
- package/build/index.esm.js.map +1 -1
- package/build/index.js +155 -246
- package/build/index.js.map +1 -1
- package/build/mocks.esm.js +40 -0
- package/build/mocks.esm.js.map +1 -0
- package/build/mocks.js +43 -0
- package/build/mocks.js.map +1 -0
- 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 +2 -0
- package/build/types/common/dateUtils/getDayNames/index.d.ts.map +1 -0
- 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 +2 -0
- package/build/types/common/dateUtils/getMonthNames/index.d.ts.map +1 -0
- 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 +2 -0
- package/build/types/common/dateUtils/isDateValid/index.d.ts.map +1 -0
- 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 +2 -0
- package/build/types/common/dateUtils/isMonthAndYearFormat/index.d.ts.map +1 -0
- 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 +2 -0
- package/build/types/common/dateUtils/isWithinRange/index.d.ts.map +1 -0
- 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 +2 -0
- package/build/types/common/dateUtils/moveToWithinRange/index.d.ts.map +1 -0
- 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 +41 -35
- package/build/types/dateInput/DateInput.d.ts.map +1 -1
- package/build/types/dateInput/DateInput.messages.d.ts +33 -24
- package/build/types/dateInput/DateInput.messages.d.ts.map +1 -1
- package/build/types/dateInput/index.d.ts +1 -1
- 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/explodeDate/explodeDate.d.ts +5 -2
- package/build/types/dateInput/utils/explodeDate/explodeDate.d.ts.map +1 -1
- package/build/types/dateInput/utils/explodeDate/index.d.ts +1 -1
- package/build/types/dateInput/utils/explodeDate/index.d.ts.map +1 -1
- package/build/types/dateInput/utils/index.d.ts +2 -2
- package/build/types/dateInput/utils/index.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/index.d.ts +1 -0
- package/build/types/index.d.ts.map +1 -1
- package/build/types/mocks.d.ts +9 -0
- package/build/types/mocks.d.ts.map +1 -0
- 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/build/types/test-utils/window-mock.d.ts.map +1 -1
- package/package.json +17 -1
- package/src/common/commonProps.ts +1 -1
- package/src/common/dateUtils/getDayNames/{getDayNames.ts → getDayNames.js} +2 -5
- package/src/common/dateUtils/getDayNames/getDayNames.spec.js +1 -1
- package/src/common/dateUtils/getDayNames/index.js +1 -0
- package/src/common/dateUtils/getMonthNames/{getMonthNames.ts → getMonthNames.js} +3 -5
- package/src/common/dateUtils/getMonthNames/getMonthNames.spec.js +8 -9
- package/src/common/dateUtils/getMonthNames/index.js +1 -0
- package/src/common/dateUtils/index.js +6 -0
- package/src/common/dateUtils/isDateValid/index.js +1 -0
- package/src/common/dateUtils/isDateValid/isDateValid.js +6 -0
- package/src/common/dateUtils/isDateValid/{isDateValid.spec.ts → isDateValid.spec.js} +1 -1
- package/src/common/dateUtils/isMonthAndYearFormat/index.js +1 -0
- package/src/common/dateUtils/isMonthAndYearFormat/isMonthAndYearFormat.js +4 -0
- package/src/common/dateUtils/isMonthAndYearFormat/isMonthAndYearFormat.spec.js +7 -3
- package/src/common/dateUtils/isWithinRange/index.js +1 -0
- package/src/common/dateUtils/isWithinRange/{isWithinRange.ts → isWithinRange.js} +1 -1
- package/src/common/dateUtils/isWithinRange/{isWithinRange.spec.ts → isWithinRange.spec.js} +1 -1
- package/src/common/dateUtils/moveToWithinRange/index.js +1 -0
- package/src/common/dateUtils/moveToWithinRange/{moveToWithinRange.ts → moveToWithinRange.js} +2 -2
- package/src/dateInput/{DateInput.tsx → DateInput.js} +97 -120
- package/src/dateInput/DateInput.spec.js +55 -6
- package/src/dateInput/DateInput.story.tsx +7 -9
- package/src/dateInput/utils/convertToLocalMidnight/{convertToLocalMidnight.ts → convertToLocalMidnight.js} +1 -1
- package/src/dateInput/utils/explodeDate/{explodeDate.ts → explodeDate.js} +1 -3
- package/src/dateLookup/dayCalendar/table/DayCalendarTable.js +0 -1
- package/src/decision/Decision.spec.js +53 -59
- package/src/decision/{Decision.story.js → Decision.story.tsx} +5 -5
- package/src/decision/Decision.tsx +136 -0
- package/src/decision/index.ts +1 -0
- package/src/i18n/zh-HK.json +2 -2
- package/src/index.ts +1 -0
- package/src/mocks.ts +48 -0
- package/src/promoCard/PromoCard.tsx +1 -2
- package/src/test-utils/window-mock.ts +7 -23
- package/src/tile/Tile.js +1 -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/index.ts +0 -6
- package/src/common/dateUtils/isDateValid/isDateValid.ts +0 -13
- package/src/common/dateUtils/isMonthAndYearFormat/isMonthAndYearFormat.ts +0 -4
- 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/dateInput/{DateInput.messages.ts → DateInput.messages.js} +0 -0
- /package/src/dateInput/{index.ts → index.js} +0 -0
- /package/src/dateInput/utils/convertToLocalMidnight/{convertToLocalMidnight.spec.ts → convertToLocalMidnight.spec.js} +0 -0
- /package/src/dateInput/utils/convertToLocalMidnight/{index.ts → index.js} +0 -0
- /package/src/dateInput/utils/explodeDate/{explodeDate.spec.ts → explodeDate.spec.js} +0 -0
- /package/src/dateInput/utils/explodeDate/{index.ts → index.js} +0 -0
- /package/src/dateInput/utils/{index.ts → index.js} +0 -0
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
import classNames from 'classnames';
|
|
2
|
-
import
|
|
2
|
+
import PropTypes from 'prop-types';
|
|
3
|
+
import { useState } from 'react';
|
|
3
4
|
import { useIntl } from 'react-intl';
|
|
4
5
|
|
|
5
|
-
import {
|
|
6
|
-
Input,
|
|
7
|
-
SelectInput,
|
|
8
|
-
SelectInputProps,
|
|
9
|
-
SelectInputOptionItem,
|
|
10
|
-
SelectInputOptionContent,
|
|
11
|
-
} from '..';
|
|
6
|
+
import { Input, SelectInput, SelectInputOptionContent } from '..';
|
|
12
7
|
import { Size, DateMode, MonthFormat } from '../common';
|
|
13
8
|
import { getMonthNames, isDateValid, isMonthAndYearFormat } from '../common/dateUtils';
|
|
14
9
|
|
|
@@ -16,79 +11,44 @@ import messages from './DateInput.messages';
|
|
|
16
11
|
import { explodeDate, convertToLocalMidnight } from './utils';
|
|
17
12
|
|
|
18
13
|
const MonthBeforeDay = new Set(['en-US', 'ja-JP']);
|
|
19
|
-
const INITIAL_DEFAULT_STATE
|
|
20
|
-
|
|
21
|
-
export interface ExplodedDate {
|
|
22
|
-
year: number | null;
|
|
23
|
-
month: number | null;
|
|
24
|
-
day: number | null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface DateInputProps {
|
|
28
|
-
'aria-label'?: string;
|
|
29
|
-
'aria-labelledby'?: string;
|
|
30
|
-
disabled?: boolean;
|
|
31
|
-
size?: Size.SMALL | Size.MEDIUM | Size.LARGE;
|
|
32
|
-
value?: Date | string | null;
|
|
33
|
-
onChange: (value: string | null) => void;
|
|
34
|
-
onFocus?: React.FocusEventHandler<HTMLInputElement>;
|
|
35
|
-
onBlur?: React.FocusEventHandler<HTMLInputElement>;
|
|
36
|
-
dayLabel?: string;
|
|
37
|
-
dayAutoComplete?: string;
|
|
38
|
-
monthLabel?: string;
|
|
39
|
-
yearLabel?: string;
|
|
40
|
-
yearAutoComplete?: string;
|
|
41
|
-
monthFormat?: MonthFormat;
|
|
42
|
-
mode?: DateMode;
|
|
43
|
-
placeholders?: {
|
|
44
|
-
day?: string;
|
|
45
|
-
month?: string;
|
|
46
|
-
year?: string;
|
|
47
|
-
};
|
|
48
|
-
id?: string;
|
|
49
|
-
selectProps?: Partial<SelectInputProps<number>>;
|
|
50
|
-
}
|
|
14
|
+
const INITIAL_DEFAULT_STATE = { year: null, month: null, day: null };
|
|
51
15
|
|
|
52
16
|
const DateInput = ({
|
|
53
17
|
'aria-labelledby': ariaLabelledBy,
|
|
54
18
|
'aria-label': ariaLabel,
|
|
55
|
-
disabled
|
|
56
|
-
size
|
|
57
|
-
value
|
|
19
|
+
disabled,
|
|
20
|
+
size,
|
|
21
|
+
value,
|
|
58
22
|
dayLabel,
|
|
59
23
|
dayAutoComplete,
|
|
60
24
|
monthLabel,
|
|
61
25
|
yearLabel,
|
|
62
26
|
yearAutoComplete,
|
|
63
|
-
monthFormat
|
|
64
|
-
mode
|
|
27
|
+
monthFormat,
|
|
28
|
+
mode,
|
|
65
29
|
onChange,
|
|
66
30
|
onFocus,
|
|
67
|
-
onBlur
|
|
31
|
+
onBlur,
|
|
68
32
|
placeholders,
|
|
69
|
-
id
|
|
33
|
+
id,
|
|
70
34
|
selectProps,
|
|
71
|
-
}
|
|
35
|
+
}) => {
|
|
72
36
|
const { locale, formatMessage } = useIntl();
|
|
73
|
-
|
|
74
|
-
const getDateObject = (): Date | null => {
|
|
37
|
+
const getDateObject = () => {
|
|
75
38
|
if (value && isDateValid(value)) {
|
|
76
39
|
return typeof value === 'string' ? convertToLocalMidnight(value) : value;
|
|
77
40
|
}
|
|
78
41
|
return null;
|
|
79
42
|
};
|
|
80
43
|
|
|
81
|
-
const getExplodedDate = (unit
|
|
82
|
-
let explodedDate
|
|
44
|
+
const getExplodedDate = (unit) => {
|
|
45
|
+
let explodedDate = INITIAL_DEFAULT_STATE;
|
|
83
46
|
|
|
84
47
|
if (value && isDateValid(value)) {
|
|
85
48
|
const dateObject = getDateObject();
|
|
49
|
+
explodedDate = explodeDate(dateObject);
|
|
86
50
|
|
|
87
|
-
if (
|
|
88
|
-
explodedDate = explodeDate(dateObject);
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
if (typeof value === 'string' && isMonthAndYearFormat(value)) {
|
|
51
|
+
if (isMonthAndYearFormat(value)) {
|
|
92
52
|
explodedDate.day = null;
|
|
93
53
|
}
|
|
94
54
|
}
|
|
@@ -96,11 +56,9 @@ const DateInput = ({
|
|
|
96
56
|
};
|
|
97
57
|
|
|
98
58
|
const [day, setDay] = useState(() => getExplodedDate('day'));
|
|
99
|
-
const [month, setMonth] = useState
|
|
59
|
+
const [month, setMonth] = useState(() => getExplodedDate('month'));
|
|
100
60
|
const [year, setYear] = useState(() => getExplodedDate('year'));
|
|
101
|
-
const [lastBroadcastedValue, setLastBroadcastedValue] = useState
|
|
102
|
-
getDateObject,
|
|
103
|
-
);
|
|
61
|
+
const [lastBroadcastedValue, setLastBroadcastedValue] = useState(getDateObject);
|
|
104
62
|
|
|
105
63
|
dayLabel = dayLabel || formatMessage(messages.dayLabel);
|
|
106
64
|
monthLabel = monthLabel || formatMessage(messages.monthLabel);
|
|
@@ -111,8 +69,8 @@ const DateInput = ({
|
|
|
111
69
|
year: placeholders?.year || formatMessage(messages.yearPlaceholder),
|
|
112
70
|
};
|
|
113
71
|
|
|
114
|
-
const getDateAsString = (date
|
|
115
|
-
if (
|
|
72
|
+
const getDateAsString = (date) => {
|
|
73
|
+
if (!isDateValid(date)) {
|
|
116
74
|
return '';
|
|
117
75
|
}
|
|
118
76
|
switch (mode) {
|
|
@@ -137,13 +95,11 @@ const DateInput = ({
|
|
|
137
95
|
<SelectInput
|
|
138
96
|
name="month"
|
|
139
97
|
disabled={disabled}
|
|
140
|
-
placeholder={placeholders
|
|
98
|
+
placeholder={placeholders.month}
|
|
141
99
|
items={monthOptions}
|
|
142
100
|
size={size}
|
|
143
|
-
value={monthOptions.find((item) => item.value === month)?.value}
|
|
144
|
-
renderValue={(
|
|
145
|
-
<SelectInputOptionContent title={getMonthNames(locale, monthFormat)[selectedValue]} />
|
|
146
|
-
)}
|
|
101
|
+
value={monthOptions.find((item) => item.value.value === month)?.value}
|
|
102
|
+
renderValue={({ label }) => <SelectInputOptionContent title={label} />}
|
|
147
103
|
onChange={(selectedValue) => handleMonthChange(selectedValue)}
|
|
148
104
|
{...selectProps}
|
|
149
105
|
/>
|
|
@@ -152,19 +108,16 @@ const DateInput = ({
|
|
|
152
108
|
};
|
|
153
109
|
|
|
154
110
|
const getMonthsOptions = () => {
|
|
155
|
-
const options
|
|
111
|
+
const options = [];
|
|
112
|
+
const months = getMonthNames(locale, monthFormat);
|
|
156
113
|
|
|
157
|
-
|
|
158
|
-
options.push({ type: 'option', value: index });
|
|
114
|
+
months.forEach((label, index) => {
|
|
115
|
+
options.push({ type: 'option', value: { label, value: index } });
|
|
159
116
|
});
|
|
160
117
|
return options;
|
|
161
118
|
};
|
|
162
119
|
|
|
163
|
-
const handleInternalValue = (
|
|
164
|
-
newDay: number | null = day,
|
|
165
|
-
newMonth: number | null = month,
|
|
166
|
-
newYear: number | null = year,
|
|
167
|
-
) => {
|
|
120
|
+
const handleInternalValue = (newDay = day, newMonth = month, newYear = year) => {
|
|
168
121
|
const dateValue =
|
|
169
122
|
newDay != null && newMonth != null && newYear != null
|
|
170
123
|
? new Date(newYear, newMonth, newDay)
|
|
@@ -177,12 +130,11 @@ const DateInput = ({
|
|
|
177
130
|
}
|
|
178
131
|
|
|
179
132
|
if (mode === DateMode.MONTH_YEAR) {
|
|
180
|
-
if (newMonth
|
|
133
|
+
if (newMonth >= 0 && newYear && (newMonth !== month || newYear !== year)) {
|
|
181
134
|
broadcastNewValue(newValue);
|
|
182
135
|
}
|
|
183
136
|
} else if (
|
|
184
137
|
newDay &&
|
|
185
|
-
newMonth !== null &&
|
|
186
138
|
newMonth >= 0 &&
|
|
187
139
|
newYear &&
|
|
188
140
|
(newDay !== day || newMonth !== month || newYear !== year)
|
|
@@ -191,75 +143,70 @@ const DateInput = ({
|
|
|
191
143
|
}
|
|
192
144
|
};
|
|
193
145
|
|
|
194
|
-
const handleDayChange = (event
|
|
195
|
-
const { checkedDay } = checkDate(
|
|
146
|
+
const handleDayChange = (event) => {
|
|
147
|
+
const { checkedDay } = checkDate(event.target.value, month, year);
|
|
196
148
|
setDay(checkedDay);
|
|
197
149
|
handleInternalValue(checkedDay, month, year);
|
|
198
150
|
};
|
|
199
151
|
|
|
200
|
-
const handleMonthChange = (selectedValue
|
|
201
|
-
if (selectedValue
|
|
152
|
+
const handleMonthChange = (selectedValue) => {
|
|
153
|
+
if (!selectedValue) {
|
|
202
154
|
setMonth(null);
|
|
203
155
|
handleInternalValue(day, null, year);
|
|
204
156
|
return;
|
|
205
157
|
}
|
|
206
|
-
const selectedMonth = selectedValue ? selectedValue : 0;
|
|
158
|
+
const selectedMonth = selectedValue ? selectedValue.value : 0;
|
|
207
159
|
const { checkedDay } = checkDate(day, selectedMonth, year);
|
|
208
160
|
setMonth(selectedMonth);
|
|
209
|
-
if (day
|
|
210
|
-
|
|
161
|
+
if (day) {
|
|
162
|
+
if (checkedDay !== day) {
|
|
163
|
+
setDay(checkedDay);
|
|
164
|
+
}
|
|
211
165
|
}
|
|
212
166
|
handleInternalValue(checkedDay, selectedMonth, year);
|
|
213
167
|
};
|
|
214
168
|
|
|
215
|
-
const handleYearChange = (event
|
|
169
|
+
const handleYearChange = (event) => {
|
|
216
170
|
const newValue = event.target.value;
|
|
217
171
|
const slicedYear = newValue.length > 4 ? newValue.slice(0, 4) : newValue;
|
|
218
172
|
|
|
219
173
|
if (slicedYear.toString().length === 4) {
|
|
220
174
|
// Correct day based on year and month.
|
|
221
|
-
const { checkedDay } = checkDate(day, month,
|
|
175
|
+
const { checkedDay } = checkDate(day, month, newValue);
|
|
222
176
|
|
|
223
|
-
if (day
|
|
224
|
-
|
|
177
|
+
if (day) {
|
|
178
|
+
if (checkedDay !== day) {
|
|
179
|
+
setDay(checkedDay);
|
|
180
|
+
}
|
|
225
181
|
}
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
handleInternalValue(checkedDay, month, parseInt(slicedYear));
|
|
182
|
+
setYear(slicedYear);
|
|
183
|
+
handleInternalValue(checkedDay, month, slicedYear);
|
|
229
184
|
} else {
|
|
230
|
-
setYear(
|
|
185
|
+
setYear(slicedYear);
|
|
231
186
|
handleInternalValue(day, month, null);
|
|
232
187
|
}
|
|
233
188
|
};
|
|
234
189
|
|
|
235
|
-
const broadcastNewValue = (newValue
|
|
190
|
+
const broadcastNewValue = (newValue) => {
|
|
236
191
|
if (newValue !== lastBroadcastedValue) {
|
|
237
192
|
setLastBroadcastedValue(newValue);
|
|
238
|
-
|
|
239
|
-
onChange(null);
|
|
240
|
-
} else {
|
|
241
|
-
onChange(getDateAsString(newValue as Date) || null);
|
|
242
|
-
}
|
|
193
|
+
onChange(getDateAsString(newValue) || null);
|
|
243
194
|
}
|
|
244
195
|
};
|
|
245
196
|
|
|
246
|
-
const checkDate = (
|
|
247
|
-
newDay: number | null = null,
|
|
248
|
-
newMonth: number | null = 0,
|
|
249
|
-
newYear: number | null = null,
|
|
250
|
-
) => {
|
|
197
|
+
const checkDate = (newDay = null, newMonth = 0, newYear = null) => {
|
|
251
198
|
let checkedDay = newDay;
|
|
252
|
-
const maxDay = new Date(newYear || 2000, newMonth
|
|
199
|
+
const maxDay = new Date(newYear || 2000, newMonth + 1, 0).getDate();
|
|
253
200
|
|
|
254
201
|
if (!newDay) {
|
|
255
202
|
checkedDay = null;
|
|
256
203
|
}
|
|
257
204
|
|
|
258
|
-
if (newDay && newDay < 0) {
|
|
205
|
+
if ((newDay && newDay < 0) || newDay === '00') {
|
|
259
206
|
checkedDay = 1;
|
|
260
207
|
}
|
|
261
208
|
|
|
262
|
-
if ((newDay && newMonth) ||
|
|
209
|
+
if ((newDay && newMonth) || newDay > 31) {
|
|
263
210
|
checkedDay = newDay > maxDay ? maxDay : newDay;
|
|
264
211
|
}
|
|
265
212
|
|
|
@@ -281,12 +228,11 @@ const DateInput = ({
|
|
|
281
228
|
id={id}
|
|
282
229
|
aria-labelledby={ariaLabelledBy}
|
|
283
230
|
aria-label={ariaLabel}
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
shouldPropagateOnFocus(event) ? onFocus && onFocus(event) : event.stopPropagation()
|
|
231
|
+
onFocus={(event) =>
|
|
232
|
+
shouldPropagateOnFocus(event) ? onFocus && onFocus() : event.stopPropagation()
|
|
287
233
|
}
|
|
288
|
-
onBlur={(event
|
|
289
|
-
shouldPropagateOnBlur(event) ? onBlur && onBlur(
|
|
234
|
+
onBlur={(event) =>
|
|
235
|
+
shouldPropagateOnBlur(event) ? onBlur && onBlur() : event.stopPropagation()
|
|
290
236
|
}
|
|
291
237
|
>
|
|
292
238
|
<div className="row">
|
|
@@ -336,20 +282,14 @@ const DateInput = ({
|
|
|
336
282
|
};
|
|
337
283
|
|
|
338
284
|
// Should only propagate if the relatedTarget is not part of this DateInput component.
|
|
339
|
-
function shouldPropagateOnFocus({
|
|
340
|
-
target,
|
|
341
|
-
relatedTarget,
|
|
342
|
-
}: Pick<React.FocusEvent, 'target' | 'relatedTarget'>) {
|
|
285
|
+
function shouldPropagateOnFocus({ target, relatedTarget }) {
|
|
343
286
|
const targetParent = target.closest('.tw-date');
|
|
344
287
|
const relatedParent = relatedTarget && relatedTarget.closest('.tw-date');
|
|
345
288
|
return targetParent !== relatedParent;
|
|
346
289
|
}
|
|
347
290
|
|
|
348
291
|
// Should only propagate if the relatedTarget or the activeElement is not part of this DateInput component.
|
|
349
|
-
function shouldPropagateOnBlur({
|
|
350
|
-
target,
|
|
351
|
-
relatedTarget,
|
|
352
|
-
}: Pick<React.FocusEvent, 'target' | 'relatedTarget'>) {
|
|
292
|
+
function shouldPropagateOnBlur({ target, relatedTarget }) {
|
|
353
293
|
const blurElementParent = target.closest('.tw-date');
|
|
354
294
|
// Even though FocusEvent.relatedTarget is supported by IE
|
|
355
295
|
// (https://developer.mozilla.org/en-US/docs/Web/API/FocusEvent/relatedTarget)
|
|
@@ -363,4 +303,41 @@ function shouldPropagateOnBlur({
|
|
|
363
303
|
return blurElementParent !== focusElementParent;
|
|
364
304
|
}
|
|
365
305
|
|
|
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
|
+
|
|
366
343
|
export default DateInput;
|
|
@@ -4,12 +4,44 @@ import { useIntl } from 'react-intl';
|
|
|
4
4
|
import { DateInput, Input } from '..';
|
|
5
5
|
import { mockMatchMedia, mockResizeObserver } from '../test-utils';
|
|
6
6
|
|
|
7
|
+
const MONTHS_FR = [
|
|
8
|
+
'janvier',
|
|
9
|
+
'février',
|
|
10
|
+
'mars',
|
|
11
|
+
'avril',
|
|
12
|
+
'mai',
|
|
13
|
+
'juin',
|
|
14
|
+
'juillet',
|
|
15
|
+
'août',
|
|
16
|
+
'septembre',
|
|
17
|
+
'octobre',
|
|
18
|
+
'novembre',
|
|
19
|
+
'décembre',
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
const MONTHS_EN = [
|
|
23
|
+
'January',
|
|
24
|
+
'February',
|
|
25
|
+
'March',
|
|
26
|
+
'April',
|
|
27
|
+
'May',
|
|
28
|
+
'June',
|
|
29
|
+
'July',
|
|
30
|
+
'August',
|
|
31
|
+
'September',
|
|
32
|
+
'October',
|
|
33
|
+
'November',
|
|
34
|
+
'December',
|
|
35
|
+
];
|
|
36
|
+
|
|
7
37
|
const LOCALES = {
|
|
38
|
+
fr: 'fr-FR',
|
|
8
39
|
jp: 'ja-JP',
|
|
9
40
|
};
|
|
41
|
+
|
|
10
42
|
const DEFAULT_LOCALE = 'en-GB';
|
|
11
|
-
|
|
12
|
-
const
|
|
43
|
+
|
|
44
|
+
const FEBRUARY_OPTION = { value: MONTHS_EN[1] };
|
|
13
45
|
|
|
14
46
|
const DAY_SELECTOR = 'ForwardRef(Input)[name="day"]';
|
|
15
47
|
const MONTH_SELECTOR = 'SelectInput';
|
|
@@ -41,6 +73,12 @@ jest.mock('./DateInput.messages', () => ({
|
|
|
41
73
|
}));
|
|
42
74
|
|
|
43
75
|
jest.mock('react-intl');
|
|
76
|
+
jest.mock('@transferwise/formatting', () => {
|
|
77
|
+
return {
|
|
78
|
+
formatDate: (month, locale) =>
|
|
79
|
+
locale === LOCALES.fr ? MONTHS_FR[month.getMonth()] : MONTHS_EN[month.getMonth()],
|
|
80
|
+
};
|
|
81
|
+
});
|
|
44
82
|
|
|
45
83
|
describe('Date Input Component', () => {
|
|
46
84
|
let selectMonth;
|
|
@@ -105,7 +143,7 @@ describe('Date Input Component', () => {
|
|
|
105
143
|
inputYear = component.find(YEAR_SELECTOR);
|
|
106
144
|
|
|
107
145
|
expect(inputDay.prop('value')).toBe(1);
|
|
108
|
-
expect(selectMonth.prop('value')).toBe(
|
|
146
|
+
expect(selectMonth.prop('value').label).toBe(FEBRUARY_OPTION.value);
|
|
109
147
|
expect(inputYear.prop('value')).toBe(1971);
|
|
110
148
|
});
|
|
111
149
|
});
|
|
@@ -119,7 +157,7 @@ describe('Date Input Component', () => {
|
|
|
119
157
|
inputYear = component.find(YEAR_SELECTOR);
|
|
120
158
|
|
|
121
159
|
expect(inputDay.prop('value')).toBe(22);
|
|
122
|
-
expect(selectMonth.prop('value')).toBe(
|
|
160
|
+
expect(selectMonth.prop('value').label).toBe(MONTHS_EN[7]);
|
|
123
161
|
expect(inputYear.prop('value')).toBe(1990);
|
|
124
162
|
});
|
|
125
163
|
});
|
|
@@ -133,7 +171,7 @@ describe('Date Input Component', () => {
|
|
|
133
171
|
inputYear = component.find(YEAR_SELECTOR);
|
|
134
172
|
|
|
135
173
|
expect(inputDay.prop('value')).toBe('');
|
|
136
|
-
expect(selectMonth.prop('value')).toBe(
|
|
174
|
+
expect(selectMonth.prop('value').label).toBe(MONTHS_EN[7]);
|
|
137
175
|
expect(inputYear.prop('value')).toBe(1990);
|
|
138
176
|
});
|
|
139
177
|
});
|
|
@@ -147,7 +185,7 @@ describe('Date Input Component', () => {
|
|
|
147
185
|
inputYear = component.find(YEAR_SELECTOR);
|
|
148
186
|
|
|
149
187
|
expect(inputDay.prop('value')).toBe(28);
|
|
150
|
-
expect(selectMonth.prop('value')).toBe(
|
|
188
|
+
expect(selectMonth.prop('value').label).toBe(FEBRUARY_OPTION.value);
|
|
151
189
|
expect(inputYear.prop('value')).toBe(1990);
|
|
152
190
|
});
|
|
153
191
|
});
|
|
@@ -182,6 +220,17 @@ describe('Date Input Component', () => {
|
|
|
182
220
|
});
|
|
183
221
|
|
|
184
222
|
describe('when locale is provided', () => {
|
|
223
|
+
it('updates selectMonth based on locale', () => {
|
|
224
|
+
useIntl.mockReturnValue({
|
|
225
|
+
locale: LOCALES.fr,
|
|
226
|
+
formatMessage: (message) => message.defaultMessage,
|
|
227
|
+
});
|
|
228
|
+
component = shallow(<DateInput {...props} />);
|
|
229
|
+
selectMonth = component.find(MONTH_SELECTOR);
|
|
230
|
+
|
|
231
|
+
expect(selectMonth.props().items[0].value.label).toStrictEqual(MONTHS_FR[0]);
|
|
232
|
+
});
|
|
233
|
+
|
|
185
234
|
it('shows day before month if locale not US', () => {
|
|
186
235
|
component = shallow(<DateInput {...props} />);
|
|
187
236
|
|
|
@@ -1,32 +1,30 @@
|
|
|
1
1
|
import { Meta, StoryObj } from '@storybook/react';
|
|
2
2
|
import { userEvent, within } from '@storybook/test';
|
|
3
|
-
import React from 'react';
|
|
4
3
|
|
|
5
|
-
import { DateInput,
|
|
4
|
+
import { DateInput, Info, InlineAlert, Title, Typography } from '..';
|
|
6
5
|
import { lorem10, storyConfig } from '../test-utils';
|
|
7
6
|
|
|
8
|
-
import { DateInputProps } from './DateInput';
|
|
9
|
-
|
|
10
7
|
export default {
|
|
11
8
|
component: DateInput,
|
|
12
9
|
title: 'Forms/DateInput',
|
|
13
10
|
args: {
|
|
14
11
|
dayLabel: 'Day input',
|
|
15
12
|
dayAutoComplete: 'bday-day',
|
|
16
|
-
value: null,
|
|
17
13
|
monthLabel: 'Month select',
|
|
18
14
|
yearLabel: 'Year input',
|
|
19
15
|
yearAutoComplete: 'bday-year',
|
|
20
16
|
selectProps: {
|
|
21
|
-
|
|
17
|
+
buttonProps: {
|
|
18
|
+
'aria-label': 'Select month',
|
|
19
|
+
},
|
|
22
20
|
},
|
|
23
21
|
},
|
|
24
22
|
tags: ['autodocs'],
|
|
25
23
|
} satisfies Meta<typeof DateInput>;
|
|
26
24
|
|
|
27
|
-
type Story = StoryObj<
|
|
25
|
+
type Story = StoryObj<typeof DateInput>;
|
|
28
26
|
|
|
29
|
-
export const Basic:
|
|
27
|
+
export const Basic: Story = {};
|
|
30
28
|
|
|
31
29
|
Basic.play = async ({ canvasElement }) => {
|
|
32
30
|
const canvas = within(canvasElement);
|
|
@@ -58,7 +56,7 @@ export const WithLabel: Story = {
|
|
|
58
56
|
{label}{' '}
|
|
59
57
|
<Info aria-label="Fast transfer hint" title="Fast transfer hint" content={lorem10} />
|
|
60
58
|
</div>
|
|
61
|
-
<DateInput {...args}
|
|
59
|
+
<DateInput {...args} aria-label={label} />
|
|
62
60
|
|
|
63
61
|
<br />
|
|
64
62
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export const convertToLocalMidnight = (date
|
|
1
|
+
export const convertToLocalMidnight = (date) => {
|
|
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());
|