@instructure/ui-date-input 9.6.0 → 9.6.1-pr-snapshot-1726659472372
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/CHANGELOG.md +11 -0
- package/es/DateInput2/index.js +144 -76
- package/es/DateInput2/props.js +2 -5
- package/lib/DateInput2/index.js +144 -76
- package/lib/DateInput2/props.js +2 -5
- package/package.json +20 -20
- package/src/DateInput/README.md +1 -1
- package/src/DateInput2/README.md +183 -163
- package/src/DateInput2/index.tsx +143 -91
- package/src/DateInput2/props.ts +19 -40
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/DateInput2/index.d.ts +10 -8
- package/types/DateInput2/index.d.ts.map +1 -1
- package/types/DateInput2/props.d.ts +12 -24
- package/types/DateInput2/props.d.ts.map +1 -1
package/CHANGELOG.md
CHANGED
@@ -3,6 +3,17 @@
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
4
4
|
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
5
5
|
|
6
|
+
## [9.6.1-pr-snapshot-1726659472372](https://github.com/instructure/instructure-ui/compare/v9.6.0...v9.6.1-pr-snapshot-1726659472372) (2024-09-18)
|
7
|
+
|
8
|
+
|
9
|
+
### Features
|
10
|
+
|
11
|
+
* **ui-date-input:** improve DateInput2 api, extend docs ([27bc27f](https://github.com/instructure/instructure-ui/commit/27bc27f8a5e18cf3cabee8b5ea18f75629728ab6))
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
|
6
17
|
# [9.6.0](https://github.com/instructure/instructure-ui/compare/v9.5.2...v9.6.0) (2024-08-14)
|
7
18
|
|
8
19
|
|
package/es/DateInput2/index.js
CHANGED
@@ -35,30 +35,85 @@ import { passthroughProps } from '@instructure/ui-react-utils';
|
|
35
35
|
import { ApplyLocaleContext, Locale } from '@instructure/ui-i18n';
|
36
36
|
import { jsx } from '@instructure/emotion';
|
37
37
|
import { propTypes } from './props';
|
38
|
-
function
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
38
|
+
function parseLocaleDate(dateString = '', locale, timeZone) {
|
39
|
+
// This function may seem complicated but it basically does one thing:
|
40
|
+
// Given a dateString, a locale and a timeZone. The dateString is assumed to be formatted according
|
41
|
+
// to the locale. So if the locale is `en-us` the dateString is expected to be in the format of M/D/YYYY.
|
42
|
+
// The dateString is also assumed to be in the given timeZone, so "1/1/2020" in "America/Los_Angeles" timezone is
|
43
|
+
// expected to be "2020-01-01T08:00:00.000Z" in UTC time.
|
44
|
+
// This function tries to parse the dateString taking these variables into account and return a javascript Date object
|
45
|
+
// that is adjusted to be in UTC.
|
46
|
+
|
47
|
+
// Split string on '.', whitespace, '/', ',' or '-' using regex: /[.\s/.-]+/.
|
48
|
+
// The '+' allows splitting on consecutive delimiters.
|
49
|
+
// `.filter(Boolean)` is needed because some locales have a delimeter at the end (e.g.: "hu-hu")
|
50
|
+
const splitDate = dateString.split(/[,.\s/.-]+/).filter(Boolean);
|
51
|
+
|
52
|
+
// create a locale formatted new date to later extract the order and delimeter information
|
53
|
+
const localeDate = new Intl.DateTimeFormat(locale).formatToParts(new Date());
|
54
|
+
let index = 0;
|
55
|
+
let day, month, year;
|
56
|
+
localeDate.forEach(part => {
|
57
|
+
if (part.type === 'month') {
|
58
|
+
month = parseInt(splitDate[index], 10);
|
59
|
+
index++;
|
60
|
+
} else if (part.type === 'day') {
|
61
|
+
day = parseInt(splitDate[index], 10);
|
62
|
+
index++;
|
63
|
+
} else if (part.type === 'year') {
|
64
|
+
year = parseInt(splitDate[index], 10);
|
65
|
+
index++;
|
66
|
+
}
|
48
67
|
});
|
68
|
+
|
69
|
+
// sensible year limitations
|
70
|
+
if (!year || !month || !day || year < 1000 || year > 9999) return null;
|
71
|
+
|
72
|
+
// create utc date from year, month (zero indexed) and day
|
73
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
74
|
+
|
75
|
+
// Format date string in the provided timezone
|
76
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
77
|
+
timeZone,
|
78
|
+
year: 'numeric',
|
79
|
+
month: '2-digit',
|
80
|
+
day: '2-digit',
|
81
|
+
hour: '2-digit',
|
82
|
+
minute: '2-digit',
|
83
|
+
second: '2-digit',
|
84
|
+
hour12: false
|
85
|
+
}).formatToParts(date);
|
86
|
+
|
87
|
+
// Extract the date and time parts from the formatted string
|
88
|
+
const dateStringInTimezone = parts.reduce((acc, part) => {
|
89
|
+
return part.type === 'literal' ? acc : {
|
90
|
+
...acc,
|
91
|
+
[part.type]: part.value
|
92
|
+
};
|
93
|
+
}, {});
|
94
|
+
|
95
|
+
// Create a date string in the format 'YYYY-MM-DDTHH:mm:ss'
|
96
|
+
const dateInTimezone = `${dateStringInTimezone.year}-${dateStringInTimezone.month}-${dateStringInTimezone.day}T${dateStringInTimezone.hour}:${dateStringInTimezone.minute}:${dateStringInTimezone.second}`;
|
97
|
+
|
98
|
+
// Calculate time difference for timezone offset
|
99
|
+
const timeDiff = new Date(dateInTimezone + 'Z').getTime() - date.getTime();
|
100
|
+
const newTime = new Date(date.getTime() - timeDiff);
|
101
|
+
// Return the UTC Date corresponding to the time in the specified timezone
|
102
|
+
return newTime;
|
49
103
|
}
|
50
104
|
|
51
105
|
/**
|
52
106
|
---
|
53
107
|
category: components
|
54
108
|
---
|
109
|
+
|
110
|
+
@module experimental
|
55
111
|
**/
|
56
112
|
const DateInput2 = ({
|
57
113
|
renderLabel,
|
58
114
|
screenReaderLabels,
|
59
115
|
isRequired = false,
|
60
116
|
interaction = 'enabled',
|
61
|
-
size = 'medium',
|
62
117
|
isInline = false,
|
63
118
|
value,
|
64
119
|
messages,
|
@@ -66,77 +121,23 @@ const DateInput2 = ({
|
|
66
121
|
onChange,
|
67
122
|
onBlur,
|
68
123
|
withYearPicker,
|
69
|
-
onRequestValidateDate,
|
70
124
|
invalidDateErrorMessage,
|
71
125
|
locale,
|
72
126
|
timezone,
|
73
127
|
placeholder,
|
74
|
-
|
128
|
+
dateFormat,
|
129
|
+
onRequestValidateDate,
|
75
130
|
// margin, TODO enable this prop
|
76
131
|
...rest
|
77
132
|
}) => {
|
78
|
-
const _useState = useState(''),
|
79
|
-
_useState2 = _slicedToArray(_useState, 2),
|
80
|
-
selectedDate = _useState2[0],
|
81
|
-
setSelectedDate = _useState2[1];
|
82
|
-
const _useState3 = useState(messages || []),
|
83
|
-
_useState4 = _slicedToArray(_useState3, 2),
|
84
|
-
inputMessages = _useState4[0],
|
85
|
-
setInputMessages = _useState4[1];
|
86
|
-
const _useState5 = useState(false),
|
87
|
-
_useState6 = _slicedToArray(_useState5, 2),
|
88
|
-
showPopover = _useState6[0],
|
89
|
-
setShowPopover = _useState6[1];
|
90
133
|
const localeContext = useContext(ApplyLocaleContext);
|
91
|
-
useEffect(() => {
|
92
|
-
// when `value` is changed, validation removes the error message if passes
|
93
|
-
// but it's NOT adding error message if validation fails for better UX
|
94
|
-
validateInput(true);
|
95
|
-
}, [value]);
|
96
|
-
useEffect(() => {
|
97
|
-
setInputMessages(messages || []);
|
98
|
-
}, [messages]);
|
99
|
-
useEffect(() => {
|
100
|
-
setSelectedDate(parseDate(value || ''));
|
101
|
-
}, []);
|
102
|
-
const handleInputChange = (e, newValue, parsedDate = '') => {
|
103
|
-
// blur event formats the input which shouldn't trigger parsing
|
104
|
-
if (e.type !== 'blur') {
|
105
|
-
setSelectedDate(parseDate(newValue));
|
106
|
-
}
|
107
|
-
onChange === null || onChange === void 0 ? void 0 : onChange(e, newValue, parsedDate);
|
108
|
-
};
|
109
|
-
const handleDateSelected = (dateString, _momentDate, e) => {
|
110
|
-
const formattedDate = formatDate(dateString, getLocale(), getTimezone());
|
111
|
-
const parsedDate = parseDate(dateString);
|
112
|
-
setSelectedDate(parsedDate);
|
113
|
-
handleInputChange(e, formattedDate, parsedDate);
|
114
|
-
setShowPopover(false);
|
115
|
-
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(dateString, true);
|
116
|
-
};
|
117
|
-
|
118
|
-
// onlyRemoveError is used to remove the error msg immediately when the user inputs a valid date (and don't wait for blur event)
|
119
|
-
const validateInput = (onlyRemoveError = false) => {
|
120
|
-
// don't validate empty input
|
121
|
-
if (!value || parseDate(value) || selectedDate) {
|
122
|
-
setInputMessages(messages || []);
|
123
|
-
return true;
|
124
|
-
}
|
125
|
-
// only show error if there is no user provided validation callback
|
126
|
-
if (!onlyRemoveError && typeof invalidDateErrorMessage === 'string' && !onRequestValidateDate) {
|
127
|
-
setInputMessages([{
|
128
|
-
type: 'error',
|
129
|
-
text: invalidDateErrorMessage
|
130
|
-
}]);
|
131
|
-
}
|
132
|
-
return false;
|
133
|
-
};
|
134
134
|
const getLocale = () => {
|
135
135
|
if (locale) {
|
136
136
|
return locale;
|
137
137
|
} else if (localeContext.locale) {
|
138
138
|
return localeContext.locale;
|
139
139
|
}
|
140
|
+
// default to the system's locale
|
140
141
|
return Locale.browserLocale();
|
141
142
|
};
|
142
143
|
const getTimezone = () => {
|
@@ -148,15 +149,84 @@ const DateInput2 = ({
|
|
148
149
|
// default to the system's timezone
|
149
150
|
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
150
151
|
};
|
152
|
+
const _useState = useState(messages || []),
|
153
|
+
_useState2 = _slicedToArray(_useState, 2),
|
154
|
+
inputMessages = _useState2[0],
|
155
|
+
setInputMessages = _useState2[1];
|
156
|
+
const _useState3 = useState(false),
|
157
|
+
_useState4 = _slicedToArray(_useState3, 2),
|
158
|
+
showPopover = _useState4[0],
|
159
|
+
setShowPopover = _useState4[1];
|
160
|
+
useEffect(() => {
|
161
|
+
setInputMessages(messages || []);
|
162
|
+
}, [messages]);
|
163
|
+
useEffect(() => {
|
164
|
+
const _parseDate = parseDate(value),
|
165
|
+
_parseDate2 = _slicedToArray(_parseDate, 2),
|
166
|
+
utcIsoDate = _parseDate2[1];
|
167
|
+
// clear error messages if date becomes valid
|
168
|
+
if (utcIsoDate || !value) {
|
169
|
+
setInputMessages(messages || []);
|
170
|
+
}
|
171
|
+
}, [value]);
|
172
|
+
const parseDate = (dateString = '') => {
|
173
|
+
let date = null;
|
174
|
+
if (dateFormat) {
|
175
|
+
if (typeof dateFormat === 'string') {
|
176
|
+
// use dateFormat instead of the user locale
|
177
|
+
date = parseLocaleDate(dateString, dateFormat, getTimezone());
|
178
|
+
} else if (dateFormat.parser) {
|
179
|
+
date = dateFormat.parser(dateString);
|
180
|
+
}
|
181
|
+
} else {
|
182
|
+
// no dateFormat prop passed, use locale for formatting
|
183
|
+
date = parseLocaleDate(dateString, getLocale(), getTimezone());
|
184
|
+
}
|
185
|
+
return date ? [formatDate(date), date.toISOString()] : ['', ''];
|
186
|
+
};
|
187
|
+
const formatDate = date => {
|
188
|
+
// use formatter function if provided
|
189
|
+
if (typeof dateFormat !== 'string' && dateFormat !== null && dateFormat !== void 0 && dateFormat.formatter) {
|
190
|
+
return dateFormat.formatter(date);
|
191
|
+
}
|
192
|
+
// if dateFormat set to a locale, use that, otherwise default to the user's locale
|
193
|
+
return date.toLocaleDateString(typeof dateFormat === 'string' ? dateFormat : getLocale(), {
|
194
|
+
timeZone: getTimezone(),
|
195
|
+
calendar: 'gregory',
|
196
|
+
numberingSystem: 'latn'
|
197
|
+
});
|
198
|
+
};
|
199
|
+
const handleInputChange = (e, newValue) => {
|
200
|
+
const _parseDate3 = parseDate(newValue),
|
201
|
+
_parseDate4 = _slicedToArray(_parseDate3, 2),
|
202
|
+
utcIsoDate = _parseDate4[1];
|
203
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e, newValue, utcIsoDate);
|
204
|
+
};
|
205
|
+
const handleDateSelected = (dateString, _momentDate, e) => {
|
206
|
+
setShowPopover(false);
|
207
|
+
const newValue = formatDate(new Date(dateString));
|
208
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e, newValue, dateString);
|
209
|
+
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(e, newValue, dateString);
|
210
|
+
};
|
151
211
|
const handleBlur = e => {
|
152
|
-
const
|
153
|
-
|
154
|
-
|
155
|
-
|
212
|
+
const _parseDate5 = parseDate(value),
|
213
|
+
_parseDate6 = _slicedToArray(_parseDate5, 2),
|
214
|
+
localeDate = _parseDate6[0],
|
215
|
+
utcIsoDate = _parseDate6[1];
|
216
|
+
if (localeDate) {
|
217
|
+
if (localeDate !== value) {
|
218
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e, localeDate, utcIsoDate);
|
219
|
+
}
|
220
|
+
} else if (value && invalidDateErrorMessage) {
|
221
|
+
setInputMessages([{
|
222
|
+
type: 'error',
|
223
|
+
text: invalidDateErrorMessage
|
224
|
+
}]);
|
156
225
|
}
|
157
|
-
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(value,
|
158
|
-
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
|
226
|
+
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(e, value || '', utcIsoDate);
|
227
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e, value || '', utcIsoDate);
|
159
228
|
};
|
229
|
+
const selectedDate = parseDate(value)[1];
|
160
230
|
return jsx(TextInput, Object.assign({}, passthroughProps(rest), {
|
161
231
|
// margin={'large'} TODO add this prop to TextInput
|
162
232
|
renderLabel: renderLabel,
|
@@ -166,7 +236,6 @@ const DateInput2 = ({
|
|
166
236
|
value: value,
|
167
237
|
placeholder: placeholder,
|
168
238
|
width: width,
|
169
|
-
size: size,
|
170
239
|
display: isInline ? 'inline-block' : 'block',
|
171
240
|
messages: inputMessages,
|
172
241
|
interaction: interaction,
|
@@ -176,7 +245,6 @@ const DateInput2 = ({
|
|
176
245
|
withBorder: false,
|
177
246
|
screenReaderLabel: screenReaderLabels.calendarIcon,
|
178
247
|
shape: "circle",
|
179
|
-
size: size,
|
180
248
|
interaction: interaction
|
181
249
|
}, _IconCalendarMonthLin || (_IconCalendarMonthLin = jsx(IconCalendarMonthLine, null))),
|
182
250
|
isShowingContent: showPopover,
|
package/es/DateInput2/props.js
CHANGED
@@ -29,7 +29,6 @@ const propTypes = {
|
|
29
29
|
renderLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
|
30
30
|
screenReaderLabels: PropTypes.object.isRequired,
|
31
31
|
value: controllable(PropTypes.string),
|
32
|
-
size: PropTypes.oneOf(['small', 'medium', 'large']),
|
33
32
|
placeholder: PropTypes.string,
|
34
33
|
onChange: PropTypes.func,
|
35
34
|
onBlur: PropTypes.func,
|
@@ -38,13 +37,11 @@ const propTypes = {
|
|
38
37
|
isInline: PropTypes.bool,
|
39
38
|
width: PropTypes.string,
|
40
39
|
messages: PropTypes.arrayOf(FormPropTypes.message),
|
41
|
-
onRequestShowCalendar: PropTypes.func,
|
42
|
-
onRequestHideCalendar: PropTypes.func,
|
43
|
-
onRequestValidateDate: PropTypes.func,
|
44
40
|
invalidDateErrorMessage: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
|
45
41
|
locale: PropTypes.string,
|
46
42
|
timezone: PropTypes.string,
|
47
43
|
withYearPicker: PropTypes.object,
|
48
|
-
|
44
|
+
dateFormat: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
45
|
+
onRequestValidateDate: PropTypes.func
|
49
46
|
};
|
50
47
|
export { propTypes };
|
package/lib/DateInput2/index.js
CHANGED
@@ -44,30 +44,85 @@ var _IconCalendarMonthLin, _IconArrowOpenEndSoli, _IconArrowOpenStartSo;
|
|
44
44
|
* SOFTWARE.
|
45
45
|
*/
|
46
46
|
/** @jsx jsx */
|
47
|
-
function
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
47
|
+
function parseLocaleDate(dateString = '', locale, timeZone) {
|
48
|
+
// This function may seem complicated but it basically does one thing:
|
49
|
+
// Given a dateString, a locale and a timeZone. The dateString is assumed to be formatted according
|
50
|
+
// to the locale. So if the locale is `en-us` the dateString is expected to be in the format of M/D/YYYY.
|
51
|
+
// The dateString is also assumed to be in the given timeZone, so "1/1/2020" in "America/Los_Angeles" timezone is
|
52
|
+
// expected to be "2020-01-01T08:00:00.000Z" in UTC time.
|
53
|
+
// This function tries to parse the dateString taking these variables into account and return a javascript Date object
|
54
|
+
// that is adjusted to be in UTC.
|
55
|
+
|
56
|
+
// Split string on '.', whitespace, '/', ',' or '-' using regex: /[.\s/.-]+/.
|
57
|
+
// The '+' allows splitting on consecutive delimiters.
|
58
|
+
// `.filter(Boolean)` is needed because some locales have a delimeter at the end (e.g.: "hu-hu")
|
59
|
+
const splitDate = dateString.split(/[,.\s/.-]+/).filter(Boolean);
|
60
|
+
|
61
|
+
// create a locale formatted new date to later extract the order and delimeter information
|
62
|
+
const localeDate = new Intl.DateTimeFormat(locale).formatToParts(new Date());
|
63
|
+
let index = 0;
|
64
|
+
let day, month, year;
|
65
|
+
localeDate.forEach(part => {
|
66
|
+
if (part.type === 'month') {
|
67
|
+
month = parseInt(splitDate[index], 10);
|
68
|
+
index++;
|
69
|
+
} else if (part.type === 'day') {
|
70
|
+
day = parseInt(splitDate[index], 10);
|
71
|
+
index++;
|
72
|
+
} else if (part.type === 'year') {
|
73
|
+
year = parseInt(splitDate[index], 10);
|
74
|
+
index++;
|
75
|
+
}
|
57
76
|
});
|
77
|
+
|
78
|
+
// sensible year limitations
|
79
|
+
if (!year || !month || !day || year < 1000 || year > 9999) return null;
|
80
|
+
|
81
|
+
// create utc date from year, month (zero indexed) and day
|
82
|
+
const date = new Date(Date.UTC(year, month - 1, day));
|
83
|
+
|
84
|
+
// Format date string in the provided timezone
|
85
|
+
const parts = new Intl.DateTimeFormat('en-US', {
|
86
|
+
timeZone,
|
87
|
+
year: 'numeric',
|
88
|
+
month: '2-digit',
|
89
|
+
day: '2-digit',
|
90
|
+
hour: '2-digit',
|
91
|
+
minute: '2-digit',
|
92
|
+
second: '2-digit',
|
93
|
+
hour12: false
|
94
|
+
}).formatToParts(date);
|
95
|
+
|
96
|
+
// Extract the date and time parts from the formatted string
|
97
|
+
const dateStringInTimezone = parts.reduce((acc, part) => {
|
98
|
+
return part.type === 'literal' ? acc : {
|
99
|
+
...acc,
|
100
|
+
[part.type]: part.value
|
101
|
+
};
|
102
|
+
}, {});
|
103
|
+
|
104
|
+
// Create a date string in the format 'YYYY-MM-DDTHH:mm:ss'
|
105
|
+
const dateInTimezone = `${dateStringInTimezone.year}-${dateStringInTimezone.month}-${dateStringInTimezone.day}T${dateStringInTimezone.hour}:${dateStringInTimezone.minute}:${dateStringInTimezone.second}`;
|
106
|
+
|
107
|
+
// Calculate time difference for timezone offset
|
108
|
+
const timeDiff = new Date(dateInTimezone + 'Z').getTime() - date.getTime();
|
109
|
+
const newTime = new Date(date.getTime() - timeDiff);
|
110
|
+
// Return the UTC Date corresponding to the time in the specified timezone
|
111
|
+
return newTime;
|
58
112
|
}
|
59
113
|
|
60
114
|
/**
|
61
115
|
---
|
62
116
|
category: components
|
63
117
|
---
|
118
|
+
|
119
|
+
@module experimental
|
64
120
|
**/
|
65
121
|
const DateInput2 = ({
|
66
122
|
renderLabel,
|
67
123
|
screenReaderLabels,
|
68
124
|
isRequired = false,
|
69
125
|
interaction = 'enabled',
|
70
|
-
size = 'medium',
|
71
126
|
isInline = false,
|
72
127
|
value,
|
73
128
|
messages,
|
@@ -75,77 +130,23 @@ const DateInput2 = ({
|
|
75
130
|
onChange,
|
76
131
|
onBlur,
|
77
132
|
withYearPicker,
|
78
|
-
onRequestValidateDate,
|
79
133
|
invalidDateErrorMessage,
|
80
134
|
locale,
|
81
135
|
timezone,
|
82
136
|
placeholder,
|
83
|
-
|
137
|
+
dateFormat,
|
138
|
+
onRequestValidateDate,
|
84
139
|
// margin, TODO enable this prop
|
85
140
|
...rest
|
86
141
|
}) => {
|
87
|
-
const _useState = (0, _react.useState)(''),
|
88
|
-
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
89
|
-
selectedDate = _useState2[0],
|
90
|
-
setSelectedDate = _useState2[1];
|
91
|
-
const _useState3 = (0, _react.useState)(messages || []),
|
92
|
-
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
|
93
|
-
inputMessages = _useState4[0],
|
94
|
-
setInputMessages = _useState4[1];
|
95
|
-
const _useState5 = (0, _react.useState)(false),
|
96
|
-
_useState6 = (0, _slicedToArray2.default)(_useState5, 2),
|
97
|
-
showPopover = _useState6[0],
|
98
|
-
setShowPopover = _useState6[1];
|
99
142
|
const localeContext = (0, _react.useContext)(_ApplyLocaleContext.ApplyLocaleContext);
|
100
|
-
(0, _react.useEffect)(() => {
|
101
|
-
// when `value` is changed, validation removes the error message if passes
|
102
|
-
// but it's NOT adding error message if validation fails for better UX
|
103
|
-
validateInput(true);
|
104
|
-
}, [value]);
|
105
|
-
(0, _react.useEffect)(() => {
|
106
|
-
setInputMessages(messages || []);
|
107
|
-
}, [messages]);
|
108
|
-
(0, _react.useEffect)(() => {
|
109
|
-
setSelectedDate(parseDate(value || ''));
|
110
|
-
}, []);
|
111
|
-
const handleInputChange = (e, newValue, parsedDate = '') => {
|
112
|
-
// blur event formats the input which shouldn't trigger parsing
|
113
|
-
if (e.type !== 'blur') {
|
114
|
-
setSelectedDate(parseDate(newValue));
|
115
|
-
}
|
116
|
-
onChange === null || onChange === void 0 ? void 0 : onChange(e, newValue, parsedDate);
|
117
|
-
};
|
118
|
-
const handleDateSelected = (dateString, _momentDate, e) => {
|
119
|
-
const formattedDate = formatDate(dateString, getLocale(), getTimezone());
|
120
|
-
const parsedDate = parseDate(dateString);
|
121
|
-
setSelectedDate(parsedDate);
|
122
|
-
handleInputChange(e, formattedDate, parsedDate);
|
123
|
-
setShowPopover(false);
|
124
|
-
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(dateString, true);
|
125
|
-
};
|
126
|
-
|
127
|
-
// onlyRemoveError is used to remove the error msg immediately when the user inputs a valid date (and don't wait for blur event)
|
128
|
-
const validateInput = (onlyRemoveError = false) => {
|
129
|
-
// don't validate empty input
|
130
|
-
if (!value || parseDate(value) || selectedDate) {
|
131
|
-
setInputMessages(messages || []);
|
132
|
-
return true;
|
133
|
-
}
|
134
|
-
// only show error if there is no user provided validation callback
|
135
|
-
if (!onlyRemoveError && typeof invalidDateErrorMessage === 'string' && !onRequestValidateDate) {
|
136
|
-
setInputMessages([{
|
137
|
-
type: 'error',
|
138
|
-
text: invalidDateErrorMessage
|
139
|
-
}]);
|
140
|
-
}
|
141
|
-
return false;
|
142
|
-
};
|
143
143
|
const getLocale = () => {
|
144
144
|
if (locale) {
|
145
145
|
return locale;
|
146
146
|
} else if (localeContext.locale) {
|
147
147
|
return localeContext.locale;
|
148
148
|
}
|
149
|
+
// default to the system's locale
|
149
150
|
return _Locale.Locale.browserLocale();
|
150
151
|
};
|
151
152
|
const getTimezone = () => {
|
@@ -157,15 +158,84 @@ const DateInput2 = ({
|
|
157
158
|
// default to the system's timezone
|
158
159
|
return Intl.DateTimeFormat().resolvedOptions().timeZone;
|
159
160
|
};
|
161
|
+
const _useState = (0, _react.useState)(messages || []),
|
162
|
+
_useState2 = (0, _slicedToArray2.default)(_useState, 2),
|
163
|
+
inputMessages = _useState2[0],
|
164
|
+
setInputMessages = _useState2[1];
|
165
|
+
const _useState3 = (0, _react.useState)(false),
|
166
|
+
_useState4 = (0, _slicedToArray2.default)(_useState3, 2),
|
167
|
+
showPopover = _useState4[0],
|
168
|
+
setShowPopover = _useState4[1];
|
169
|
+
(0, _react.useEffect)(() => {
|
170
|
+
setInputMessages(messages || []);
|
171
|
+
}, [messages]);
|
172
|
+
(0, _react.useEffect)(() => {
|
173
|
+
const _parseDate = parseDate(value),
|
174
|
+
_parseDate2 = (0, _slicedToArray2.default)(_parseDate, 2),
|
175
|
+
utcIsoDate = _parseDate2[1];
|
176
|
+
// clear error messages if date becomes valid
|
177
|
+
if (utcIsoDate || !value) {
|
178
|
+
setInputMessages(messages || []);
|
179
|
+
}
|
180
|
+
}, [value]);
|
181
|
+
const parseDate = (dateString = '') => {
|
182
|
+
let date = null;
|
183
|
+
if (dateFormat) {
|
184
|
+
if (typeof dateFormat === 'string') {
|
185
|
+
// use dateFormat instead of the user locale
|
186
|
+
date = parseLocaleDate(dateString, dateFormat, getTimezone());
|
187
|
+
} else if (dateFormat.parser) {
|
188
|
+
date = dateFormat.parser(dateString);
|
189
|
+
}
|
190
|
+
} else {
|
191
|
+
// no dateFormat prop passed, use locale for formatting
|
192
|
+
date = parseLocaleDate(dateString, getLocale(), getTimezone());
|
193
|
+
}
|
194
|
+
return date ? [formatDate(date), date.toISOString()] : ['', ''];
|
195
|
+
};
|
196
|
+
const formatDate = date => {
|
197
|
+
// use formatter function if provided
|
198
|
+
if (typeof dateFormat !== 'string' && dateFormat !== null && dateFormat !== void 0 && dateFormat.formatter) {
|
199
|
+
return dateFormat.formatter(date);
|
200
|
+
}
|
201
|
+
// if dateFormat set to a locale, use that, otherwise default to the user's locale
|
202
|
+
return date.toLocaleDateString(typeof dateFormat === 'string' ? dateFormat : getLocale(), {
|
203
|
+
timeZone: getTimezone(),
|
204
|
+
calendar: 'gregory',
|
205
|
+
numberingSystem: 'latn'
|
206
|
+
});
|
207
|
+
};
|
208
|
+
const handleInputChange = (e, newValue) => {
|
209
|
+
const _parseDate3 = parseDate(newValue),
|
210
|
+
_parseDate4 = (0, _slicedToArray2.default)(_parseDate3, 2),
|
211
|
+
utcIsoDate = _parseDate4[1];
|
212
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e, newValue, utcIsoDate);
|
213
|
+
};
|
214
|
+
const handleDateSelected = (dateString, _momentDate, e) => {
|
215
|
+
setShowPopover(false);
|
216
|
+
const newValue = formatDate(new Date(dateString));
|
217
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e, newValue, dateString);
|
218
|
+
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(e, newValue, dateString);
|
219
|
+
};
|
160
220
|
const handleBlur = e => {
|
161
|
-
const
|
162
|
-
|
163
|
-
|
164
|
-
|
221
|
+
const _parseDate5 = parseDate(value),
|
222
|
+
_parseDate6 = (0, _slicedToArray2.default)(_parseDate5, 2),
|
223
|
+
localeDate = _parseDate6[0],
|
224
|
+
utcIsoDate = _parseDate6[1];
|
225
|
+
if (localeDate) {
|
226
|
+
if (localeDate !== value) {
|
227
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e, localeDate, utcIsoDate);
|
228
|
+
}
|
229
|
+
} else if (value && invalidDateErrorMessage) {
|
230
|
+
setInputMessages([{
|
231
|
+
type: 'error',
|
232
|
+
text: invalidDateErrorMessage
|
233
|
+
}]);
|
165
234
|
}
|
166
|
-
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(value,
|
167
|
-
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
|
235
|
+
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(e, value || '', utcIsoDate);
|
236
|
+
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e, value || '', utcIsoDate);
|
168
237
|
};
|
238
|
+
const selectedDate = parseDate(value)[1];
|
169
239
|
return (0, _emotion.jsx)(_TextInput.TextInput, Object.assign({}, (0, _passthroughProps.passthroughProps)(rest), {
|
170
240
|
// margin={'large'} TODO add this prop to TextInput
|
171
241
|
renderLabel: renderLabel,
|
@@ -175,7 +245,6 @@ const DateInput2 = ({
|
|
175
245
|
value: value,
|
176
246
|
placeholder: placeholder,
|
177
247
|
width: width,
|
178
|
-
size: size,
|
179
248
|
display: isInline ? 'inline-block' : 'block',
|
180
249
|
messages: inputMessages,
|
181
250
|
interaction: interaction,
|
@@ -185,7 +254,6 @@ const DateInput2 = ({
|
|
185
254
|
withBorder: false,
|
186
255
|
screenReaderLabel: screenReaderLabels.calendarIcon,
|
187
256
|
shape: "circle",
|
188
|
-
size: size,
|
189
257
|
interaction: interaction
|
190
258
|
}, _IconCalendarMonthLin || (_IconCalendarMonthLin = (0, _emotion.jsx)(_IconCalendarMonthLine.IconCalendarMonthLine, null))),
|
191
259
|
isShowingContent: showPopover,
|
package/lib/DateInput2/props.js
CHANGED
@@ -36,7 +36,6 @@ const propTypes = exports.propTypes = {
|
|
36
36
|
renderLabel: _propTypes.default.oneOfType([_propTypes.default.node, _propTypes.default.func]).isRequired,
|
37
37
|
screenReaderLabels: _propTypes.default.object.isRequired,
|
38
38
|
value: (0, _controllable.controllable)(_propTypes.default.string),
|
39
|
-
size: _propTypes.default.oneOf(['small', 'medium', 'large']),
|
40
39
|
placeholder: _propTypes.default.string,
|
41
40
|
onChange: _propTypes.default.func,
|
42
41
|
onBlur: _propTypes.default.func,
|
@@ -45,12 +44,10 @@ const propTypes = exports.propTypes = {
|
|
45
44
|
isInline: _propTypes.default.bool,
|
46
45
|
width: _propTypes.default.string,
|
47
46
|
messages: _propTypes.default.arrayOf(_FormPropTypes.FormPropTypes.message),
|
48
|
-
onRequestShowCalendar: _propTypes.default.func,
|
49
|
-
onRequestHideCalendar: _propTypes.default.func,
|
50
|
-
onRequestValidateDate: _propTypes.default.func,
|
51
47
|
invalidDateErrorMessage: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.string]),
|
52
48
|
locale: _propTypes.default.string,
|
53
49
|
timezone: _propTypes.default.string,
|
54
50
|
withYearPicker: _propTypes.default.object,
|
55
|
-
|
51
|
+
dateFormat: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.object]),
|
52
|
+
onRequestValidateDate: _propTypes.default.func
|
56
53
|
};
|