@instructure/ui-date-input 9.5.2 → 9.6.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/CHANGELOG.md +11 -0
- package/es/DateInput2/index.js +27 -23
- package/es/DateInput2/props.js +2 -1
- package/lib/DateInput2/index.js +27 -23
- package/lib/DateInput2/props.js +2 -1
- package/package.json +20 -20
- package/src/DateInput2/README.md +103 -1
- package/src/DateInput2/index.tsx +37 -26
- package/src/DateInput2/props.ts +24 -3
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/DateInput2/index.d.ts +3 -8
- package/types/DateInput2/index.d.ts.map +1 -1
- package/types/DateInput2/props.d.ts +5 -1
- 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.0](https://github.com/instructure/instructure-ui/compare/v9.5.2...v9.6.0) (2024-08-14)
|
7
|
+
|
8
|
+
|
9
|
+
### Features
|
10
|
+
|
11
|
+
* **ui-calendar,ui-date-input:** improve DateInput2 onChange callback, add date formatting option, extend docs ([4e2c23c](https://github.com/instructure/instructure-ui/commit/4e2c23c3288885e49030f1f471d61b2fed29b54c))
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
|
16
|
+
|
6
17
|
## [9.5.2](https://github.com/instructure/instructure-ui/compare/v9.5.1...v9.5.2) (2024-08-05)
|
7
18
|
|
8
19
|
|
package/es/DateInput2/index.js
CHANGED
@@ -37,9 +37,16 @@ import { jsx } from '@instructure/emotion';
|
|
37
37
|
import { propTypes } from './props';
|
38
38
|
function parseDate(dateString) {
|
39
39
|
const date = new Date(dateString);
|
40
|
-
// return empty string if not a valid date
|
41
40
|
return isNaN(date.getTime()) ? '' : date.toISOString();
|
42
41
|
}
|
42
|
+
function defaultDateFormatter(dateString, locale, timezone) {
|
43
|
+
return new Date(dateString).toLocaleDateString(locale, {
|
44
|
+
month: 'long',
|
45
|
+
year: 'numeric',
|
46
|
+
day: 'numeric',
|
47
|
+
timeZone: timezone
|
48
|
+
});
|
49
|
+
}
|
43
50
|
|
44
51
|
/**
|
45
52
|
---
|
@@ -64,6 +71,8 @@ const DateInput2 = ({
|
|
64
71
|
locale,
|
65
72
|
timezone,
|
66
73
|
placeholder,
|
74
|
+
formatDate = defaultDateFormatter,
|
75
|
+
// margin, TODO enable this prop
|
67
76
|
...rest
|
68
77
|
}) => {
|
69
78
|
const _useState = useState(''),
|
@@ -80,29 +89,28 @@ const DateInput2 = ({
|
|
80
89
|
setShowPopover = _useState6[1];
|
81
90
|
const localeContext = useContext(ApplyLocaleContext);
|
82
91
|
useEffect(() => {
|
83
|
-
// when `value` is changed, validation
|
92
|
+
// when `value` is changed, validation removes the error message if passes
|
84
93
|
// but it's NOT adding error message if validation fails for better UX
|
85
94
|
validateInput(true);
|
86
95
|
}, [value]);
|
87
96
|
useEffect(() => {
|
88
97
|
setInputMessages(messages || []);
|
89
98
|
}, [messages]);
|
90
|
-
|
91
|
-
|
92
|
-
|
99
|
+
useEffect(() => {
|
100
|
+
setSelectedDate(parseDate(value || ''));
|
101
|
+
}, []);
|
102
|
+
const handleInputChange = (e, newValue, parsedDate = '') => {
|
103
|
+
// blur event formats the input which shouldn't trigger parsing
|
93
104
|
if (e.type !== 'blur') {
|
94
|
-
setSelectedDate(parseDate(
|
105
|
+
setSelectedDate(parseDate(newValue));
|
95
106
|
}
|
107
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e, newValue, parsedDate);
|
96
108
|
};
|
97
109
|
const handleDateSelected = (dateString, _momentDate, e) => {
|
98
|
-
const formattedDate =
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
timeZone: getTimezone()
|
103
|
-
});
|
104
|
-
handleInputChange(e, formattedDate);
|
105
|
-
setSelectedDate(dateString);
|
110
|
+
const formattedDate = formatDate(dateString, getLocale(), getTimezone());
|
111
|
+
const parsedDate = parseDate(dateString);
|
112
|
+
setSelectedDate(parsedDate);
|
113
|
+
handleInputChange(e, formattedDate, parsedDate);
|
106
114
|
setShowPopover(false);
|
107
115
|
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(dateString, true);
|
108
116
|
};
|
@@ -143,18 +151,14 @@ const DateInput2 = ({
|
|
143
151
|
const handleBlur = e => {
|
144
152
|
const isInputValid = validateInput(false);
|
145
153
|
if (isInputValid && selectedDate) {
|
146
|
-
const formattedDate =
|
147
|
-
|
148
|
-
year: 'numeric',
|
149
|
-
day: 'numeric',
|
150
|
-
timeZone: getTimezone()
|
151
|
-
});
|
152
|
-
handleInputChange(e, formattedDate);
|
154
|
+
const formattedDate = formatDate(selectedDate, getLocale(), getTimezone());
|
155
|
+
handleInputChange(e, formattedDate, selectedDate);
|
153
156
|
}
|
154
157
|
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(value, isInputValid);
|
155
158
|
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
|
156
159
|
};
|
157
160
|
return jsx(TextInput, Object.assign({}, passthroughProps(rest), {
|
161
|
+
// margin={'large'} TODO add this prop to TextInput
|
158
162
|
renderLabel: renderLabel,
|
159
163
|
onChange: handleInputChange,
|
160
164
|
onBlur: handleBlur,
|
@@ -187,8 +191,8 @@ const DateInput2 = ({
|
|
187
191
|
onDateSelected: handleDateSelected,
|
188
192
|
selectedDate: selectedDate,
|
189
193
|
visibleMonth: selectedDate,
|
190
|
-
locale:
|
191
|
-
timezone:
|
194
|
+
locale: getLocale(),
|
195
|
+
timezone: getTimezone(),
|
192
196
|
role: "listbox",
|
193
197
|
renderNextMonthButton: jsx(IconButton, {
|
194
198
|
size: "small",
|
package/es/DateInput2/props.js
CHANGED
@@ -44,6 +44,7 @@ const propTypes = {
|
|
44
44
|
invalidDateErrorMessage: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
|
45
45
|
locale: PropTypes.string,
|
46
46
|
timezone: PropTypes.string,
|
47
|
-
withYearPicker: PropTypes.object
|
47
|
+
withYearPicker: PropTypes.object,
|
48
|
+
formatDate: PropTypes.func
|
48
49
|
};
|
49
50
|
export { propTypes };
|
package/lib/DateInput2/index.js
CHANGED
@@ -46,9 +46,16 @@ var _IconCalendarMonthLin, _IconArrowOpenEndSoli, _IconArrowOpenStartSo;
|
|
46
46
|
/** @jsx jsx */
|
47
47
|
function parseDate(dateString) {
|
48
48
|
const date = new Date(dateString);
|
49
|
-
// return empty string if not a valid date
|
50
49
|
return isNaN(date.getTime()) ? '' : date.toISOString();
|
51
50
|
}
|
51
|
+
function defaultDateFormatter(dateString, locale, timezone) {
|
52
|
+
return new Date(dateString).toLocaleDateString(locale, {
|
53
|
+
month: 'long',
|
54
|
+
year: 'numeric',
|
55
|
+
day: 'numeric',
|
56
|
+
timeZone: timezone
|
57
|
+
});
|
58
|
+
}
|
52
59
|
|
53
60
|
/**
|
54
61
|
---
|
@@ -73,6 +80,8 @@ const DateInput2 = ({
|
|
73
80
|
locale,
|
74
81
|
timezone,
|
75
82
|
placeholder,
|
83
|
+
formatDate = defaultDateFormatter,
|
84
|
+
// margin, TODO enable this prop
|
76
85
|
...rest
|
77
86
|
}) => {
|
78
87
|
const _useState = (0, _react.useState)(''),
|
@@ -89,29 +98,28 @@ const DateInput2 = ({
|
|
89
98
|
setShowPopover = _useState6[1];
|
90
99
|
const localeContext = (0, _react.useContext)(_ApplyLocaleContext.ApplyLocaleContext);
|
91
100
|
(0, _react.useEffect)(() => {
|
92
|
-
// when `value` is changed, validation
|
101
|
+
// when `value` is changed, validation removes the error message if passes
|
93
102
|
// but it's NOT adding error message if validation fails for better UX
|
94
103
|
validateInput(true);
|
95
104
|
}, [value]);
|
96
105
|
(0, _react.useEffect)(() => {
|
97
106
|
setInputMessages(messages || []);
|
98
107
|
}, [messages]);
|
99
|
-
|
100
|
-
|
101
|
-
|
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
|
102
113
|
if (e.type !== 'blur') {
|
103
|
-
setSelectedDate(parseDate(
|
114
|
+
setSelectedDate(parseDate(newValue));
|
104
115
|
}
|
116
|
+
onChange === null || onChange === void 0 ? void 0 : onChange(e, newValue, parsedDate);
|
105
117
|
};
|
106
118
|
const handleDateSelected = (dateString, _momentDate, e) => {
|
107
|
-
const formattedDate =
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
timeZone: getTimezone()
|
112
|
-
});
|
113
|
-
handleInputChange(e, formattedDate);
|
114
|
-
setSelectedDate(dateString);
|
119
|
+
const formattedDate = formatDate(dateString, getLocale(), getTimezone());
|
120
|
+
const parsedDate = parseDate(dateString);
|
121
|
+
setSelectedDate(parsedDate);
|
122
|
+
handleInputChange(e, formattedDate, parsedDate);
|
115
123
|
setShowPopover(false);
|
116
124
|
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(dateString, true);
|
117
125
|
};
|
@@ -152,18 +160,14 @@ const DateInput2 = ({
|
|
152
160
|
const handleBlur = e => {
|
153
161
|
const isInputValid = validateInput(false);
|
154
162
|
if (isInputValid && selectedDate) {
|
155
|
-
const formattedDate =
|
156
|
-
|
157
|
-
year: 'numeric',
|
158
|
-
day: 'numeric',
|
159
|
-
timeZone: getTimezone()
|
160
|
-
});
|
161
|
-
handleInputChange(e, formattedDate);
|
163
|
+
const formattedDate = formatDate(selectedDate, getLocale(), getTimezone());
|
164
|
+
handleInputChange(e, formattedDate, selectedDate);
|
162
165
|
}
|
163
166
|
onRequestValidateDate === null || onRequestValidateDate === void 0 ? void 0 : onRequestValidateDate(value, isInputValid);
|
164
167
|
onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
|
165
168
|
};
|
166
169
|
return (0, _emotion.jsx)(_TextInput.TextInput, Object.assign({}, (0, _passthroughProps.passthroughProps)(rest), {
|
170
|
+
// margin={'large'} TODO add this prop to TextInput
|
167
171
|
renderLabel: renderLabel,
|
168
172
|
onChange: handleInputChange,
|
169
173
|
onBlur: handleBlur,
|
@@ -196,8 +200,8 @@ const DateInput2 = ({
|
|
196
200
|
onDateSelected: handleDateSelected,
|
197
201
|
selectedDate: selectedDate,
|
198
202
|
visibleMonth: selectedDate,
|
199
|
-
locale:
|
200
|
-
timezone:
|
203
|
+
locale: getLocale(),
|
204
|
+
timezone: getTimezone(),
|
201
205
|
role: "listbox",
|
202
206
|
renderNextMonthButton: (0, _emotion.jsx)(_IconButton.IconButton, {
|
203
207
|
size: "small",
|
package/lib/DateInput2/props.js
CHANGED
@@ -51,5 +51,6 @@ const propTypes = exports.propTypes = {
|
|
51
51
|
invalidDateErrorMessage: _propTypes.default.oneOfType([_propTypes.default.func, _propTypes.default.string]),
|
52
52
|
locale: _propTypes.default.string,
|
53
53
|
timezone: _propTypes.default.string,
|
54
|
-
withYearPicker: _propTypes.default.object
|
54
|
+
withYearPicker: _propTypes.default.object,
|
55
|
+
formatDate: _propTypes.default.func
|
55
56
|
};
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@instructure/ui-date-input",
|
3
|
-
"version": "9.
|
3
|
+
"version": "9.6.0",
|
4
4
|
"description": "A UI component library made by Instructure Inc.",
|
5
5
|
"author": "Instructure, Inc. Engineering and Product Design",
|
6
6
|
"module": "./es/index.js",
|
@@ -23,11 +23,11 @@
|
|
23
23
|
},
|
24
24
|
"license": "MIT",
|
25
25
|
"devDependencies": {
|
26
|
-
"@instructure/ui-axe-check": "9.
|
27
|
-
"@instructure/ui-babel-preset": "9.
|
28
|
-
"@instructure/ui-buttons": "9.
|
29
|
-
"@instructure/ui-scripts": "9.
|
30
|
-
"@instructure/ui-test-utils": "9.
|
26
|
+
"@instructure/ui-axe-check": "9.6.0",
|
27
|
+
"@instructure/ui-babel-preset": "9.6.0",
|
28
|
+
"@instructure/ui-buttons": "9.6.0",
|
29
|
+
"@instructure/ui-scripts": "9.6.0",
|
30
|
+
"@instructure/ui-test-utils": "9.6.0",
|
31
31
|
"@testing-library/jest-dom": "^6.4.6",
|
32
32
|
"@testing-library/react": "^15.0.7",
|
33
33
|
"@testing-library/user-event": "^14.5.2",
|
@@ -35,20 +35,20 @@
|
|
35
35
|
},
|
36
36
|
"dependencies": {
|
37
37
|
"@babel/runtime": "^7.24.5",
|
38
|
-
"@instructure/emotion": "9.
|
39
|
-
"@instructure/shared-types": "9.
|
40
|
-
"@instructure/ui-calendar": "9.
|
41
|
-
"@instructure/ui-form-field": "9.
|
42
|
-
"@instructure/ui-i18n": "9.
|
43
|
-
"@instructure/ui-icons": "9.
|
44
|
-
"@instructure/ui-popover": "9.
|
45
|
-
"@instructure/ui-position": "9.
|
46
|
-
"@instructure/ui-prop-types": "9.
|
47
|
-
"@instructure/ui-react-utils": "9.
|
48
|
-
"@instructure/ui-selectable": "9.
|
49
|
-
"@instructure/ui-testable": "9.
|
50
|
-
"@instructure/ui-text-input": "9.
|
51
|
-
"@instructure/ui-utils": "9.
|
38
|
+
"@instructure/emotion": "9.6.0",
|
39
|
+
"@instructure/shared-types": "9.6.0",
|
40
|
+
"@instructure/ui-calendar": "9.6.0",
|
41
|
+
"@instructure/ui-form-field": "9.6.0",
|
42
|
+
"@instructure/ui-i18n": "9.6.0",
|
43
|
+
"@instructure/ui-icons": "9.6.0",
|
44
|
+
"@instructure/ui-popover": "9.6.0",
|
45
|
+
"@instructure/ui-position": "9.6.0",
|
46
|
+
"@instructure/ui-prop-types": "9.6.0",
|
47
|
+
"@instructure/ui-react-utils": "9.6.0",
|
48
|
+
"@instructure/ui-selectable": "9.6.0",
|
49
|
+
"@instructure/ui-testable": "9.6.0",
|
50
|
+
"@instructure/ui-text-input": "9.6.0",
|
51
|
+
"@instructure/ui-utils": "9.6.0",
|
52
52
|
"moment-timezone": "^0.5.45",
|
53
53
|
"prop-types": "^15.8.1"
|
54
54
|
},
|
package/src/DateInput2/README.md
CHANGED
@@ -113,7 +113,11 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier
|
|
113
113
|
render(<Example />)
|
114
114
|
```
|
115
115
|
|
116
|
-
###
|
116
|
+
### Date validation
|
117
|
+
|
118
|
+
By default `DateInput2` only does date validation if the `invalidDateErrorMessage` prop is provided. This uses the browser's `Date` object to try an parse the user provided date and displays the error message if it fails. Validation is only triggered on the blur event of the input field.
|
119
|
+
|
120
|
+
If you want to do a more complex validation than the above (e.g. only allow a subset of dates) you can use the `onRequestValidateDate` prop to pass a validation function. This function will run on blur or on selecting the date from the picker. The result of the internal validation will be passed to this function. Then you have to set the error messages accordingly. Check the following example for more details:
|
117
121
|
|
118
122
|
```js
|
119
123
|
---
|
@@ -163,3 +167,101 @@ const Example = () => {
|
|
163
167
|
|
164
168
|
render(<Example />)
|
165
169
|
```
|
170
|
+
|
171
|
+
### Date formatting
|
172
|
+
|
173
|
+
The display format of the dates can be set via the `formatDate` property. It will be applied if the user clicks on a date in the date picker of after blur event from the input field.
|
174
|
+
Something to pay attention to is that the date string passed back in the callback function **is in UTC timezone**.
|
175
|
+
|
176
|
+
```js
|
177
|
+
---
|
178
|
+
type: example
|
179
|
+
---
|
180
|
+
const Example = () => {
|
181
|
+
const [value1, setValue1] = useState('')
|
182
|
+
const [value2, setValue2] = useState('')
|
183
|
+
const [value3, setValue3] = useState('')
|
184
|
+
|
185
|
+
const shortDateFormatFn = (dateString, locale, timezone) => {
|
186
|
+
return new Date(dateString).toLocaleDateString(locale, {
|
187
|
+
month: 'numeric',
|
188
|
+
year: 'numeric',
|
189
|
+
day: 'numeric',
|
190
|
+
timeZone: timezone,
|
191
|
+
})
|
192
|
+
}
|
193
|
+
|
194
|
+
const isoDateFormatFn = (dateString, locale, timezone) => {
|
195
|
+
// this is a simple way to get ISO8601 date in a specific timezone but should not be used in production
|
196
|
+
// please use a proper date library instead like date-fns, luxon or dayjs
|
197
|
+
const localeDate = new Date(dateString).toLocaleDateString('sv', {
|
198
|
+
month: 'numeric',
|
199
|
+
year: 'numeric',
|
200
|
+
day: 'numeric',
|
201
|
+
timeZone: timezone,
|
202
|
+
})
|
203
|
+
|
204
|
+
return localeDate
|
205
|
+
}
|
206
|
+
|
207
|
+
return (
|
208
|
+
<div style={{display: 'flex', flexDirection: 'column', gap: '1.5rem'}}>
|
209
|
+
<DateInput2
|
210
|
+
renderLabel="Default format"
|
211
|
+
screenReaderLabels={{
|
212
|
+
calendarIcon: 'Calendar',
|
213
|
+
nextMonthButton: 'Next month',
|
214
|
+
prevMonthButton: 'Previous month'
|
215
|
+
}}
|
216
|
+
isInline
|
217
|
+
width="20rem"
|
218
|
+
value={value1}
|
219
|
+
onChange={(e, value) => setValue1(value)}
|
220
|
+
withYearPicker={{
|
221
|
+
screenReaderLabel: 'Year picker',
|
222
|
+
startYear: 1900,
|
223
|
+
endYear: 2024
|
224
|
+
}}
|
225
|
+
/>
|
226
|
+
<DateInput2
|
227
|
+
renderLabel="Short format in current locale"
|
228
|
+
screenReaderLabels={{
|
229
|
+
calendarIcon: 'Calendar',
|
230
|
+
nextMonthButton: 'Next month',
|
231
|
+
prevMonthButton: 'Previous month'
|
232
|
+
}}
|
233
|
+
isInline
|
234
|
+
width="20rem"
|
235
|
+
value={value2}
|
236
|
+
onChange={(e, value) => setValue2(value)}
|
237
|
+
formatDate={shortDateFormatFn}
|
238
|
+
withYearPicker={{
|
239
|
+
screenReaderLabel: 'Year picker',
|
240
|
+
startYear: 1900,
|
241
|
+
endYear: 2024
|
242
|
+
}}
|
243
|
+
/>
|
244
|
+
<DateInput2
|
245
|
+
renderLabel="ISO8601"
|
246
|
+
screenReaderLabels={{
|
247
|
+
calendarIcon: 'Calendar',
|
248
|
+
nextMonthButton: 'Next month',
|
249
|
+
prevMonthButton: 'Previous month'
|
250
|
+
}}
|
251
|
+
isInline
|
252
|
+
width="20rem"
|
253
|
+
value={value3}
|
254
|
+
onChange={(e, value) => setValue3(value)}
|
255
|
+
formatDate={isoDateFormatFn}
|
256
|
+
withYearPicker={{
|
257
|
+
screenReaderLabel: 'Year picker',
|
258
|
+
startYear: 1900,
|
259
|
+
endYear: 2024
|
260
|
+
}}
|
261
|
+
/>
|
262
|
+
</div>
|
263
|
+
)
|
264
|
+
}
|
265
|
+
|
266
|
+
render(<Example />)
|
267
|
+
```
|
package/src/DateInput2/index.tsx
CHANGED
@@ -46,10 +46,22 @@ import type { Moment } from '@instructure/ui-i18n'
|
|
46
46
|
|
47
47
|
function parseDate(dateString: string): string {
|
48
48
|
const date = new Date(dateString)
|
49
|
-
// return empty string if not a valid date
|
50
49
|
return isNaN(date.getTime()) ? '' : date.toISOString()
|
51
50
|
}
|
52
51
|
|
52
|
+
function defaultDateFormatter(
|
53
|
+
dateString: string,
|
54
|
+
locale: string,
|
55
|
+
timezone: string
|
56
|
+
) {
|
57
|
+
return new Date(dateString).toLocaleDateString(locale, {
|
58
|
+
month: 'long',
|
59
|
+
year: 'numeric',
|
60
|
+
day: 'numeric',
|
61
|
+
timeZone: timezone
|
62
|
+
})
|
63
|
+
}
|
64
|
+
|
53
65
|
/**
|
54
66
|
---
|
55
67
|
category: components
|
@@ -73,6 +85,8 @@ const DateInput2 = ({
|
|
73
85
|
locale,
|
74
86
|
timezone,
|
75
87
|
placeholder,
|
88
|
+
formatDate = defaultDateFormatter,
|
89
|
+
// margin, TODO enable this prop
|
76
90
|
...rest
|
77
91
|
}: DateInput2Props) => {
|
78
92
|
const [selectedDate, setSelectedDate] = useState<string>('')
|
@@ -83,7 +97,7 @@ const DateInput2 = ({
|
|
83
97
|
const localeContext = useContext(ApplyLocaleContext)
|
84
98
|
|
85
99
|
useEffect(() => {
|
86
|
-
// when `value` is changed, validation
|
100
|
+
// when `value` is changed, validation removes the error message if passes
|
87
101
|
// but it's NOT adding error message if validation fails for better UX
|
88
102
|
validateInput(true)
|
89
103
|
}, [value])
|
@@ -92,12 +106,20 @@ const DateInput2 = ({
|
|
92
106
|
setInputMessages(messages || [])
|
93
107
|
}, [messages])
|
94
108
|
|
95
|
-
|
96
|
-
|
97
|
-
|
109
|
+
useEffect(() => {
|
110
|
+
setSelectedDate(parseDate(value || ''))
|
111
|
+
}, [])
|
112
|
+
|
113
|
+
const handleInputChange = (
|
114
|
+
e: SyntheticEvent,
|
115
|
+
newValue: string,
|
116
|
+
parsedDate: string = ''
|
117
|
+
) => {
|
118
|
+
// blur event formats the input which shouldn't trigger parsing
|
98
119
|
if (e.type !== 'blur') {
|
99
|
-
setSelectedDate(parseDate(
|
120
|
+
setSelectedDate(parseDate(newValue))
|
100
121
|
}
|
122
|
+
onChange?.(e, newValue, parsedDate)
|
101
123
|
}
|
102
124
|
|
103
125
|
const handleDateSelected = (
|
@@ -105,14 +127,10 @@ const DateInput2 = ({
|
|
105
127
|
_momentDate: Moment,
|
106
128
|
e: SyntheticEvent
|
107
129
|
) => {
|
108
|
-
const formattedDate =
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
timeZone: getTimezone()
|
113
|
-
})
|
114
|
-
handleInputChange(e, formattedDate)
|
115
|
-
setSelectedDate(dateString)
|
130
|
+
const formattedDate = formatDate(dateString, getLocale(), getTimezone())
|
131
|
+
const parsedDate = parseDate(dateString)
|
132
|
+
setSelectedDate(parsedDate)
|
133
|
+
handleInputChange(e, formattedDate, parsedDate)
|
116
134
|
setShowPopover(false)
|
117
135
|
onRequestValidateDate?.(dateString, true)
|
118
136
|
}
|
@@ -163,16 +181,8 @@ const DateInput2 = ({
|
|
163
181
|
const handleBlur = (e: SyntheticEvent) => {
|
164
182
|
const isInputValid = validateInput(false)
|
165
183
|
if (isInputValid && selectedDate) {
|
166
|
-
const formattedDate =
|
167
|
-
|
168
|
-
{
|
169
|
-
month: 'long',
|
170
|
-
year: 'numeric',
|
171
|
-
day: 'numeric',
|
172
|
-
timeZone: getTimezone()
|
173
|
-
}
|
174
|
-
)
|
175
|
-
handleInputChange(e, formattedDate)
|
184
|
+
const formattedDate = formatDate(selectedDate, getLocale(), getTimezone())
|
185
|
+
handleInputChange(e, formattedDate, selectedDate)
|
176
186
|
}
|
177
187
|
onRequestValidateDate?.(value, isInputValid)
|
178
188
|
onBlur?.(e)
|
@@ -181,6 +191,7 @@ const DateInput2 = ({
|
|
181
191
|
return (
|
182
192
|
<TextInput
|
183
193
|
{...passthroughProps(rest)}
|
194
|
+
// margin={'large'} TODO add this prop to TextInput
|
184
195
|
renderLabel={renderLabel}
|
185
196
|
onChange={handleInputChange}
|
186
197
|
onBlur={handleBlur}
|
@@ -219,8 +230,8 @@ const DateInput2 = ({
|
|
219
230
|
onDateSelected={handleDateSelected}
|
220
231
|
selectedDate={selectedDate}
|
221
232
|
visibleMonth={selectedDate}
|
222
|
-
locale={
|
223
|
-
timezone={
|
233
|
+
locale={getLocale()}
|
234
|
+
timezone={getTimezone()}
|
224
235
|
role="listbox"
|
225
236
|
renderNextMonthButton={
|
226
237
|
<IconButton
|
package/src/DateInput2/props.ts
CHANGED
@@ -28,7 +28,11 @@ import type { SyntheticEvent, InputHTMLAttributes } from 'react'
|
|
28
28
|
import { controllable } from '@instructure/ui-prop-types'
|
29
29
|
import { FormPropTypes } from '@instructure/ui-form-field'
|
30
30
|
import type { FormMessage } from '@instructure/ui-form-field'
|
31
|
-
import type {
|
31
|
+
import type {
|
32
|
+
OtherHTMLAttributes,
|
33
|
+
Renderable,
|
34
|
+
PropValidators
|
35
|
+
} from '@instructure/shared-types'
|
32
36
|
|
33
37
|
type DateInput2OwnProps = {
|
34
38
|
/**
|
@@ -56,7 +60,11 @@ type DateInput2OwnProps = {
|
|
56
60
|
/**
|
57
61
|
* Callback fired when the input changes.
|
58
62
|
*/
|
59
|
-
onChange?: (
|
63
|
+
onChange?: (
|
64
|
+
event: React.SyntheticEvent,
|
65
|
+
inputValue: string,
|
66
|
+
dateString: string
|
67
|
+
) => void
|
60
68
|
/**
|
61
69
|
* Callback executed when the input fires a blur event.
|
62
70
|
*/
|
@@ -157,6 +165,18 @@ type DateInput2OwnProps = {
|
|
157
165
|
startYear: number
|
158
166
|
endYear: number
|
159
167
|
}
|
168
|
+
|
169
|
+
/**
|
170
|
+
* Formatting function for how the date should be displayed inside the input field. It will be applied if the user clicks on a date in the date picker of after blur event from the input field.
|
171
|
+
*/
|
172
|
+
formatDate?: (isoDate: string, locale: string, timezone: string) => string
|
173
|
+
|
174
|
+
/**
|
175
|
+
* Valid values are `0`, `none`, `auto`, `xxx-small`, `xx-small`, `x-small`,
|
176
|
+
* `small`, `medium`, `large`, `x-large`, `xx-large`. Apply these values via
|
177
|
+
* familiar CSS-like shorthand. For example: `margin="small auto large"`.
|
178
|
+
*/
|
179
|
+
// margin?: Spacing TODO enable this prop
|
160
180
|
}
|
161
181
|
|
162
182
|
type PropKeys = keyof DateInput2OwnProps
|
@@ -189,7 +209,8 @@ const propTypes: PropValidators<PropKeys> = {
|
|
189
209
|
]),
|
190
210
|
locale: PropTypes.string,
|
191
211
|
timezone: PropTypes.string,
|
192
|
-
withYearPicker: PropTypes.object
|
212
|
+
withYearPicker: PropTypes.object,
|
213
|
+
formatDate: PropTypes.func
|
193
214
|
}
|
194
215
|
|
195
216
|
export type { DateInput2Props }
|