@transferwise/components 46.13.0 → 46.15.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.
Files changed (168) hide show
  1. package/build/index.esm.js +246 -525
  2. package/build/index.esm.js.map +1 -1
  3. package/build/index.js +222 -501
  4. package/build/index.js.map +1 -1
  5. package/build/main.css +6 -82
  6. package/build/styles/decision/Decision.css +6 -82
  7. package/build/styles/main.css +6 -82
  8. package/build/types/actionButton/ActionButton.d.ts +1 -1
  9. package/build/types/body/Body.d.ts +1 -1
  10. package/build/types/common/Option/Option.d.ts.map +1 -1
  11. package/build/types/common/RadioButton/RadioButton.d.ts +10 -34
  12. package/build/types/common/RadioButton/RadioButton.d.ts.map +1 -1
  13. package/build/types/common/RadioButton/index.d.ts +1 -1
  14. package/build/types/common/RadioButton/index.d.ts.map +1 -1
  15. package/build/types/common/commonProps.d.ts +1 -1
  16. package/build/types/common/commonProps.d.ts.map +1 -1
  17. package/build/types/common/dateUtils/getDayNames/getDayNames.d.ts +1 -1
  18. package/build/types/common/dateUtils/getDayNames/getDayNames.d.ts.map +1 -1
  19. package/build/types/common/dateUtils/getDayNames/index.d.ts +1 -1
  20. package/build/types/common/dateUtils/getDayNames/index.d.ts.map +1 -1
  21. package/build/types/common/dateUtils/getMonthNames/getMonthNames.d.ts +1 -1
  22. package/build/types/common/dateUtils/getMonthNames/getMonthNames.d.ts.map +1 -1
  23. package/build/types/common/dateUtils/getMonthNames/index.d.ts +1 -1
  24. package/build/types/common/dateUtils/getMonthNames/index.d.ts.map +1 -1
  25. package/build/types/common/dateUtils/index.d.ts +6 -6
  26. package/build/types/common/dateUtils/index.d.ts.map +1 -1
  27. package/build/types/common/dateUtils/isDateValid/index.d.ts +1 -1
  28. package/build/types/common/dateUtils/isDateValid/index.d.ts.map +1 -1
  29. package/build/types/common/dateUtils/isDateValid/isDateValid.d.ts +2 -2
  30. package/build/types/common/dateUtils/isDateValid/isDateValid.d.ts.map +1 -1
  31. package/build/types/common/dateUtils/isMonthAndYearFormat/index.d.ts +1 -1
  32. package/build/types/common/dateUtils/isMonthAndYearFormat/index.d.ts.map +1 -1
  33. package/build/types/common/dateUtils/isMonthAndYearFormat/isMonthAndYearFormat.d.ts +1 -1
  34. package/build/types/common/dateUtils/isMonthAndYearFormat/isMonthAndYearFormat.d.ts.map +1 -1
  35. package/build/types/common/dateUtils/isWithinRange/index.d.ts +1 -1
  36. package/build/types/common/dateUtils/isWithinRange/index.d.ts.map +1 -1
  37. package/build/types/common/dateUtils/isWithinRange/isWithinRange.d.ts +1 -1
  38. package/build/types/common/dateUtils/isWithinRange/isWithinRange.d.ts.map +1 -1
  39. package/build/types/common/dateUtils/moveToWithinRange/index.d.ts +1 -1
  40. package/build/types/common/dateUtils/moveToWithinRange/index.d.ts.map +1 -1
  41. package/build/types/common/dateUtils/moveToWithinRange/moveToWithinRange.d.ts +1 -1
  42. package/build/types/common/dateUtils/moveToWithinRange/moveToWithinRange.d.ts.map +1 -1
  43. package/build/types/common/panel/Panel.d.ts +1 -1
  44. package/build/types/common/responsivePanel/ResponsivePanel.d.ts +1 -1
  45. package/build/types/dateInput/DateInput.d.ts +30 -41
  46. package/build/types/dateInput/DateInput.d.ts.map +1 -1
  47. package/build/types/dateInput/DateInput.messages.d.ts +24 -33
  48. package/build/types/dateInput/DateInput.messages.d.ts.map +1 -1
  49. package/build/types/dateInput/index.d.ts +2 -2
  50. package/build/types/dateInput/index.d.ts.map +1 -1
  51. package/build/types/dateInput/utils/convertToLocalMidnight/convertToLocalMidnight.d.ts +1 -1
  52. package/build/types/dateInput/utils/convertToLocalMidnight/convertToLocalMidnight.d.ts.map +1 -1
  53. package/build/types/dateInput/utils/convertToLocalMidnight/index.d.ts +1 -1
  54. package/build/types/dateInput/utils/convertToLocalMidnight/index.d.ts.map +1 -1
  55. package/build/types/dateInput/utils/index.d.ts +1 -2
  56. package/build/types/dateInput/utils/index.d.ts.map +1 -1
  57. package/build/types/dateLookup/DateLookup.d.ts +1 -0
  58. package/build/types/dateLookup/DateLookup.d.ts.map +1 -1
  59. package/build/types/decision/Decision.d.ts +39 -52
  60. package/build/types/decision/Decision.d.ts.map +1 -1
  61. package/build/types/decision/index.d.ts +1 -2
  62. package/build/types/decision/index.d.ts.map +1 -1
  63. package/build/types/dimmer/Dimmer.d.ts +1 -1
  64. package/build/types/index.d.ts +5 -0
  65. package/build/types/index.d.ts.map +1 -1
  66. package/build/types/promoCard/PromoCard.d.ts +1 -2
  67. package/build/types/promoCard/PromoCard.d.ts.map +1 -1
  68. package/build/types/radio/Radio.d.ts +10 -21
  69. package/build/types/radio/Radio.d.ts.map +1 -1
  70. package/build/types/radio/index.d.ts +2 -2
  71. package/build/types/radio/index.d.ts.map +1 -1
  72. package/build/types/radioGroup/RadioGroup.d.ts +10 -26
  73. package/build/types/radioGroup/RadioGroup.d.ts.map +1 -1
  74. package/build/types/radioGroup/index.d.ts +2 -1
  75. package/build/types/radioGroup/index.d.ts.map +1 -1
  76. package/build/types/radioOption/RadioOption.d.ts +15 -23
  77. package/build/types/radioOption/RadioOption.d.ts.map +1 -1
  78. package/build/types/radioOption/index.d.ts +2 -1
  79. package/build/types/radioOption/index.d.ts.map +1 -1
  80. package/build/types/select/searchBox/SearchBox.d.ts +1 -1
  81. package/package.json +1 -1
  82. package/src/common/Option/Option.tsx +0 -1
  83. package/src/common/RadioButton/RadioButton.tsx +43 -0
  84. package/src/common/commonProps.ts +1 -1
  85. package/src/common/dateUtils/getDayNames/getDayNames.spec.js +2 -2
  86. package/src/common/dateUtils/getDayNames/{getDayNames.js → getDayNames.ts} +5 -2
  87. package/src/common/dateUtils/getMonthNames/getMonthNames.spec.js +9 -8
  88. package/src/common/dateUtils/getMonthNames/{getMonthNames.js → getMonthNames.ts} +5 -3
  89. package/src/common/dateUtils/isDateValid/{isDateValid.spec.js → isDateValid.spec.ts} +1 -1
  90. package/src/common/dateUtils/isDateValid/isDateValid.ts +13 -0
  91. package/src/common/dateUtils/isMonthAndYearFormat/isMonthAndYearFormat.spec.js +3 -7
  92. package/src/common/dateUtils/isMonthAndYearFormat/{isMonthAndYearFormat.js → isMonthAndYearFormat.ts} +1 -1
  93. package/src/common/dateUtils/isWithinRange/{isWithinRange.spec.js → isWithinRange.spec.ts} +0 -10
  94. package/src/common/dateUtils/isWithinRange/{isWithinRange.js → isWithinRange.ts} +1 -1
  95. package/src/common/dateUtils/moveToWithinRange/{moveToWithinRange.js → moveToWithinRange.ts} +2 -2
  96. package/src/dateInput/DateInput.spec.js +7 -56
  97. package/src/dateInput/DateInput.story.tsx +11 -8
  98. package/src/dateInput/{DateInput.js → DateInput.tsx} +116 -123
  99. package/src/dateInput/index.ts +2 -0
  100. package/src/dateInput/utils/convertToLocalMidnight/{convertToLocalMidnight.js → convertToLocalMidnight.ts} +1 -1
  101. package/src/dateInput/utils/{index.js → index.ts} +0 -1
  102. package/src/dateLookup/DateLookup.js +12 -1
  103. package/src/dateLookup/DateLookup.testingLibrary.spec.js +12 -1
  104. package/src/dateLookup/dayCalendar/table/DayCalendarTable.js +1 -0
  105. package/src/decision/Decision.css +6 -82
  106. package/src/decision/Decision.less +3 -41
  107. package/src/decision/Decision.spec.js +56 -61
  108. package/src/decision/{Decision.story.js → Decision.story.tsx} +5 -5
  109. package/src/decision/Decision.tsx +133 -0
  110. package/src/decision/index.ts +1 -0
  111. package/src/index.ts +5 -0
  112. package/src/main.css +6 -82
  113. package/src/promoCard/PromoCard.tsx +1 -2
  114. package/src/radio/{Radio.story.js → Radio.story.tsx} +0 -3
  115. package/src/radio/{Radio.js → Radio.tsx} +18 -28
  116. package/src/radio/index.ts +2 -0
  117. package/src/radioGroup/RadioGroup.spec.js +24 -26
  118. package/src/radioGroup/{RadioGroup.story.js → RadioGroup.story.tsx} +0 -3
  119. package/src/radioGroup/RadioGroup.tsx +39 -0
  120. package/src/radioGroup/index.ts +2 -0
  121. package/src/radioOption/RadioOption.spec.js +4 -4
  122. package/src/radioOption/{RadioOption.story.js → RadioOption.story.tsx} +4 -4
  123. package/src/radioOption/RadioOption.tsx +60 -0
  124. package/src/radioOption/index.ts +2 -0
  125. package/src/tile/Tile.js +1 -1
  126. package/build/types/dateInput/utils/explodeDate/explodeDate.d.ts +0 -6
  127. package/build/types/dateInput/utils/explodeDate/explodeDate.d.ts.map +0 -1
  128. package/build/types/dateInput/utils/explodeDate/index.d.ts +0 -2
  129. package/build/types/dateInput/utils/explodeDate/index.d.ts.map +0 -1
  130. package/build/types/decision/decisionEnums.d.ts +0 -9
  131. package/build/types/decision/decisionEnums.d.ts.map +0 -1
  132. package/build/types/sizeSwapper/SizeSwapper.d.ts +0 -3
  133. package/build/types/sizeSwapper/SizeSwapper.d.ts.map +0 -1
  134. package/build/types/sizeSwapper/index.d.ts +0 -2
  135. package/build/types/sizeSwapper/index.d.ts.map +0 -1
  136. package/src/common/RadioButton/RadioButton.js +0 -41
  137. package/src/common/dateUtils/isDateValid/isDateValid.js +0 -6
  138. package/src/dateInput/index.js +0 -3
  139. package/src/dateInput/utils/explodeDate/explodeDate.js +0 -7
  140. package/src/dateInput/utils/explodeDate/explodeDate.spec.js +0 -11
  141. package/src/dateInput/utils/explodeDate/index.js +0 -1
  142. package/src/decision/Decision.js +0 -148
  143. package/src/decision/decisionEnums.ts +0 -11
  144. package/src/decision/index.js +0 -2
  145. package/src/radio/index.js +0 -3
  146. package/src/radioGroup/RadioGroup.js +0 -66
  147. package/src/radioGroup/index.js +0 -1
  148. package/src/radioOption/RadioOption.js +0 -81
  149. package/src/radioOption/index.js +0 -1
  150. package/src/sizeSwapper/SizeSwapper.js +0 -69
  151. package/src/sizeSwapper/SizeSwapper.spec.js +0 -100
  152. package/src/sizeSwapper/SizeSwapper.story.js +0 -34
  153. package/src/sizeSwapper/index.js +0 -1
  154. /package/src/common/RadioButton/{RadioButton.spec.js → RadioButton.spec.tsx} +0 -0
  155. /package/src/common/RadioButton/__snapshots__/{RadioButton.spec.js.snap → RadioButton.spec.tsx.snap} +0 -0
  156. /package/src/common/RadioButton/{index.js → index.ts} +0 -0
  157. /package/src/common/dateUtils/getDayNames/{index.js → index.ts} +0 -0
  158. /package/src/common/dateUtils/getMonthNames/{index.js → index.ts} +0 -0
  159. /package/src/common/dateUtils/{index.js → index.ts} +0 -0
  160. /package/src/common/dateUtils/isDateValid/{index.js → index.ts} +0 -0
  161. /package/src/common/dateUtils/isMonthAndYearFormat/{index.js → index.ts} +0 -0
  162. /package/src/common/dateUtils/isWithinRange/{index.js → index.ts} +0 -0
  163. /package/src/common/dateUtils/moveToWithinRange/{index.js → index.ts} +0 -0
  164. /package/src/dateInput/{DateInput.messages.js → DateInput.messages.ts} +0 -0
  165. /package/src/dateInput/utils/convertToLocalMidnight/{convertToLocalMidnight.spec.js → convertToLocalMidnight.spec.ts} +0 -0
  166. /package/src/dateInput/utils/convertToLocalMidnight/{index.js → index.ts} +0 -0
  167. /package/src/radio/{Radio.rtl.spec.js → Radio.rtl.spec.tsx} +0 -0
  168. /package/src/radio/__snapshots__/{Radio.rtl.spec.js.snap → Radio.rtl.spec.tsx.snap} +0 -0
@@ -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 { explodeDate, convertToLocalMidnight } from './utils';
10
+ import { convertToLocalMidnight } from './utils';
12
11
 
13
12
  const MonthBeforeDay = new Set(['en-US', 'ja-JP']);
14
- const INITIAL_DEFAULT_STATE = { year: null, month: null, day: null };
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
- const getDateObject = () => {
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 null;
65
+ return undefined;
42
66
  };
43
67
 
44
- const getExplodedDate = (unit) => {
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
- explodedDate.day = null;
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 explodedDate[unit];
89
+ return null;
56
90
  };
57
91
 
58
- const [day, setDay] = useState(() => getExplodedDate('day'));
59
- const [month, setMonth] = useState(() => getExplodedDate('month'));
60
- const [year, setYear] = useState(() => getExplodedDate('year'));
61
- const [lastBroadcastedValue, setLastBroadcastedValue] = useState(getDateObject);
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.month}
99
- items={monthOptions}
133
+ placeholder={placeholders?.month}
134
+ items={Array.from({ length: 12 }, (_, index) => ({ type: 'option', value: index }))}
100
135
  size={size}
101
- value={monthOptions.find((item) => item.value.value === month)?.value}
102
- renderValue={({ label }) => <SelectInputOptionContent title={label} />}
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
- const dateValue =
122
- newDay != null && newMonth != null && newYear != null
123
- ? new Date(newYear, newMonth, newDay)
124
- : null;
148
+ if (newDay == null || newMonth == null || newYear == null) {
149
+ broadcastNewValue(null);
150
+ return;
151
+ }
125
152
 
126
- const newValue = isDateValid(dateValue) ? dateValue : null;
153
+ const dateValue = new Date(newYear, newMonth, newDay);
127
154
 
128
- if (!newValue) {
129
- broadcastNewValue(INITIAL_DEFAULT_STATE);
155
+ if (!isDateValid(dateValue)) {
156
+ broadcastNewValue(null);
157
+ return;
130
158
  }
131
159
 
132
160
  if (mode === DateMode.MONTH_YEAR) {
133
- if (newMonth >= 0 && newYear && (newMonth !== month || newYear !== year)) {
134
- broadcastNewValue(newValue);
161
+ if (newMonth !== month || newYear !== year) {
162
+ broadcastNewValue(dateValue);
135
163
  }
136
- } else if (
137
- newDay &&
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 = (selectedValue) => {
153
- if (!selectedValue) {
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
- if (checkedDay !== day) {
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
- if (checkedDay !== day) {
179
- setDay(checkedDay);
180
- }
197
+ if (day && checkedDay !== day) {
198
+ setDay(checkedDay);
181
199
  }
182
- setYear(slicedYear);
183
- handleInternalValue(checkedDay, month, slicedYear);
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) || null);
212
+ onChange(newValue != null ? getDateAsString(newValue) : null);
194
213
  }
195
214
  };
196
215
 
197
- const checkDate = (newDay = null, newMonth = 0, newYear = null) => {
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 ((newDay && newDay < 0) || newDay === '00') {
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
- onFocus={(event) =>
232
- shouldPropagateOnFocus(event) ? onFocus && onFocus() : event.stopPropagation()
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({ target, relatedTarget }) {
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({ target, relatedTarget }) {
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;
@@ -0,0 +1,2 @@
1
+ export { default } from './DateInput';
2
+ export type { DateInputProps } from './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());
@@ -1,2 +1 @@
1
- export { explodeDate } from './explodeDate';
2
1
  export { convertToLocalMidnight } from './convertToLocalMidnight';
@@ -240,10 +240,20 @@ class DateLookup extends PureComponent {
240
240
  render() {
241
241
  const { selectedDate, open } = this.state;
242
242
 
243
- const { size, placeholder, label, monthFormat, disabled, clearable, value } = this.props;
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(<DateLookup {...props} />));
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 };
@@ -58,6 +58,7 @@ class DayCalendarTable extends PureComponent {
58
58
  isDisabled = (day) => {
59
59
  const { min, max, viewMonth, viewYear } = this.props;
60
60
  const date = new Date(viewYear, viewMonth, day);
61
+
61
62
  return !isWithinRange(date, min, max);
62
63
  };
63
64
 
@@ -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.np-decision--grid.np-decision--small .np-size-swapper {
85
- margin-right: calc(0 - 16px);
86
- margin-right: calc(0 - var(--size-16));
10
+ .np-decision:not(.flex-column) {
11
+ gap: 24px;
12
+ gap: var(--size-24);
87
13
  }
88
- [dir="rtl"] .np-decision.np-decision--grid.np-decision--small .np-size-swapper {
89
- margin-left: calc(0 - 16px);
90
- margin-left: calc(0 - var(--size-16));
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
- &.np-decision--grid &__tile {
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
- .np-size-swapper {
56
- .margin(right, calc(0 - var(--size-16)));
57
- }
19
+ gap: var(--size-16);
58
20
  }
59
21
  }
60
22
  }