@instructure/ui-date-input 9.6.0 → 10.0.1-pr-snapshot-1723800180253
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 +4 -10
- package/es/DateInput2/index.js +33 -42
- package/es/DateInput2/props.js +1 -2
- package/lib/DateInput2/index.js +33 -42
- package/lib/DateInput2/props.js +1 -2
- package/package.json +20 -20
- package/src/DateInput2/README.md +1 -103
- package/src/DateInput2/index.tsx +48 -53
- package/src/DateInput2/props.ts +6 -33
- package/src/index.ts +0 -1
- package/tsconfig.build.tsbuildinfo +1 -1
- package/types/DateInput2/index.d.ts +2 -34
- package/types/DateInput2/index.d.ts.map +1 -1
- package/types/DateInput2/props.d.ts +5 -10
- package/types/DateInput2/props.d.ts.map +1 -1
- package/types/index.d.ts +0 -1
- package/types/index.d.ts.map +1 -1
package/src/DateInput2/index.tsx
CHANGED
@@ -25,6 +25,7 @@
|
|
25
25
|
/** @jsx jsx */
|
26
26
|
import { useState, useEffect, useContext } from 'react'
|
27
27
|
import type { SyntheticEvent } from 'react'
|
28
|
+
import moment from 'moment-timezone'
|
28
29
|
import { Calendar } from '@instructure/ui-calendar'
|
29
30
|
import { IconButton } from '@instructure/ui-buttons'
|
30
31
|
import {
|
@@ -42,24 +43,25 @@ import { jsx } from '@instructure/emotion'
|
|
42
43
|
import { propTypes } from './props'
|
43
44
|
import type { DateInput2Props } from './props'
|
44
45
|
import type { FormMessage } from '@instructure/ui-form-field'
|
45
|
-
import type { Moment } from '@instructure/ui-i18n'
|
46
46
|
|
47
|
-
function
|
48
|
-
|
49
|
-
return isNaN(date.getTime()) ? '' : date.toISOString()
|
47
|
+
function isValidDate(dateString: string): boolean {
|
48
|
+
return !isNaN(new Date(dateString).getTime())
|
50
49
|
}
|
51
50
|
|
52
|
-
function
|
51
|
+
function isValidMomentDate(
|
53
52
|
dateString: string,
|
54
53
|
locale: string,
|
55
54
|
timezone: string
|
56
|
-
) {
|
57
|
-
return
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
55
|
+
): boolean {
|
56
|
+
return moment
|
57
|
+
.tz(
|
58
|
+
dateString,
|
59
|
+
[moment.ISO_8601, 'llll', 'LLLL', 'lll', 'LLL', 'll', 'LL', 'l', 'L'],
|
60
|
+
locale,
|
61
|
+
true,
|
62
|
+
timezone
|
63
|
+
)
|
64
|
+
.isValid()
|
63
65
|
}
|
64
66
|
|
65
67
|
/**
|
@@ -85,8 +87,6 @@ const DateInput2 = ({
|
|
85
87
|
locale,
|
86
88
|
timezone,
|
87
89
|
placeholder,
|
88
|
-
formatDate = defaultDateFormatter,
|
89
|
-
// margin, TODO enable this prop
|
90
90
|
...rest
|
91
91
|
}: DateInput2Props) => {
|
92
92
|
const [selectedDate, setSelectedDate] = useState<string>('')
|
@@ -97,8 +97,6 @@ const DateInput2 = ({
|
|
97
97
|
const localeContext = useContext(ApplyLocaleContext)
|
98
98
|
|
99
99
|
useEffect(() => {
|
100
|
-
// when `value` is changed, validation removes the error message if passes
|
101
|
-
// but it's NOT adding error message if validation fails for better UX
|
102
100
|
validateInput(true)
|
103
101
|
}, [value])
|
104
102
|
|
@@ -106,53 +104,46 @@ const DateInput2 = ({
|
|
106
104
|
setInputMessages(messages || [])
|
107
105
|
}, [messages])
|
108
106
|
|
109
|
-
|
110
|
-
|
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
|
119
|
-
if (e.type !== 'blur') {
|
120
|
-
setSelectedDate(parseDate(newValue))
|
121
|
-
}
|
122
|
-
onChange?.(e, newValue, parsedDate)
|
107
|
+
const handleInputChange = (e: SyntheticEvent, value: string) => {
|
108
|
+
onChange?.(e, value)
|
123
109
|
}
|
124
110
|
|
125
111
|
const handleDateSelected = (
|
126
112
|
dateString: string,
|
127
|
-
_momentDate: Moment
|
113
|
+
_momentDate: any, // real type is Moment but used `any` to avoid importing the moment lib
|
128
114
|
e: SyntheticEvent
|
129
115
|
) => {
|
130
|
-
const formattedDate =
|
131
|
-
|
132
|
-
|
133
|
-
|
116
|
+
const formattedDate = new Date(dateString).toLocaleDateString(getLocale(), {
|
117
|
+
month: 'long',
|
118
|
+
year: 'numeric',
|
119
|
+
day: 'numeric',
|
120
|
+
timeZone: getTimezone()
|
121
|
+
})
|
122
|
+
handleInputChange(e, formattedDate)
|
134
123
|
setShowPopover(false)
|
135
|
-
onRequestValidateDate?.(
|
124
|
+
onRequestValidateDate?.(formattedDate, true)
|
136
125
|
}
|
137
126
|
|
138
|
-
// onlyRemoveError is used to remove the error msg immediately when the user inputs a valid date (and don't wait for blur event)
|
139
127
|
const validateInput = (onlyRemoveError = false): boolean => {
|
140
|
-
//
|
141
|
-
|
128
|
+
// TODO `isValidDate` and `isValidMomentDate` basically have the same functionality but the latter is a bit more strict (e.g.: `33` is only valid in `isValidMomentDate`)
|
129
|
+
// in the future we should get rid of moment but currently Calendar is using it for validation too so we can only remove it simultaneously
|
130
|
+
// otherwise DateInput could pass invalid dates to Calendar and break it
|
131
|
+
if (
|
132
|
+
(isValidDate(value || '') &&
|
133
|
+
isValidMomentDate(value || '', getLocale(), getTimezone())) ||
|
134
|
+
value === ''
|
135
|
+
) {
|
136
|
+
setSelectedDate(value || '')
|
142
137
|
setInputMessages(messages || [])
|
143
138
|
return true
|
144
139
|
}
|
145
|
-
|
146
|
-
|
147
|
-
!onlyRemoveError &&
|
148
|
-
typeof invalidDateErrorMessage === 'string' &&
|
149
|
-
!onRequestValidateDate
|
150
|
-
) {
|
151
|
-
setInputMessages([
|
140
|
+
if (!onlyRemoveError && typeof invalidDateErrorMessage === 'string') {
|
141
|
+
setInputMessages((messages) => [
|
152
142
|
{
|
153
143
|
type: 'error',
|
154
144
|
text: invalidDateErrorMessage
|
155
|
-
}
|
145
|
+
},
|
146
|
+
...messages
|
156
147
|
])
|
157
148
|
}
|
158
149
|
|
@@ -180,9 +171,14 @@ const DateInput2 = ({
|
|
180
171
|
|
181
172
|
const handleBlur = (e: SyntheticEvent) => {
|
182
173
|
const isInputValid = validateInput(false)
|
183
|
-
if (isInputValid &&
|
184
|
-
const formattedDate =
|
185
|
-
|
174
|
+
if (isInputValid && value) {
|
175
|
+
const formattedDate = new Date(value).toLocaleDateString(getLocale(), {
|
176
|
+
month: 'long',
|
177
|
+
year: 'numeric',
|
178
|
+
day: 'numeric',
|
179
|
+
timeZone: getTimezone()
|
180
|
+
})
|
181
|
+
handleInputChange(e, formattedDate)
|
186
182
|
}
|
187
183
|
onRequestValidateDate?.(value, isInputValid)
|
188
184
|
onBlur?.(e)
|
@@ -191,7 +187,6 @@ const DateInput2 = ({
|
|
191
187
|
return (
|
192
188
|
<TextInput
|
193
189
|
{...passthroughProps(rest)}
|
194
|
-
// margin={'large'} TODO add this prop to TextInput
|
195
190
|
renderLabel={renderLabel}
|
196
191
|
onChange={handleInputChange}
|
197
192
|
onBlur={handleBlur}
|
@@ -230,8 +225,8 @@ const DateInput2 = ({
|
|
230
225
|
onDateSelected={handleDateSelected}
|
231
226
|
selectedDate={selectedDate}
|
232
227
|
visibleMonth={selectedDate}
|
233
|
-
locale={
|
234
|
-
timezone={
|
228
|
+
locale={locale}
|
229
|
+
timezone={timezone}
|
235
230
|
role="listbox"
|
236
231
|
renderNextMonthButton={
|
237
232
|
<IconButton
|
package/src/DateInput2/props.ts
CHANGED
@@ -23,18 +23,14 @@
|
|
23
23
|
*/
|
24
24
|
|
25
25
|
import PropTypes from 'prop-types'
|
26
|
-
import type { SyntheticEvent
|
26
|
+
import type { SyntheticEvent } from 'react'
|
27
27
|
|
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 {
|
32
|
-
OtherHTMLAttributes,
|
33
|
-
Renderable,
|
34
|
-
PropValidators
|
35
|
-
} from '@instructure/shared-types'
|
31
|
+
import type { Renderable, PropValidators } from '@instructure/shared-types'
|
36
32
|
|
37
|
-
type
|
33
|
+
type DateInput2Props = {
|
38
34
|
/**
|
39
35
|
* Specifies the input label.
|
40
36
|
*/
|
@@ -60,11 +56,7 @@ type DateInput2OwnProps = {
|
|
60
56
|
/**
|
61
57
|
* Callback fired when the input changes.
|
62
58
|
*/
|
63
|
-
onChange?: (
|
64
|
-
event: React.SyntheticEvent,
|
65
|
-
inputValue: string,
|
66
|
-
dateString: string
|
67
|
-
) => void
|
59
|
+
onChange?: (event: React.SyntheticEvent, value: string) => void
|
68
60
|
/**
|
69
61
|
* Callback executed when the input fires a blur event.
|
70
62
|
*/
|
@@ -165,27 +157,9 @@ type DateInput2OwnProps = {
|
|
165
157
|
startYear: number
|
166
158
|
endYear: number
|
167
159
|
}
|
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
|
180
160
|
}
|
181
161
|
|
182
|
-
type PropKeys = keyof
|
183
|
-
|
184
|
-
type DateInput2Props = DateInput2OwnProps &
|
185
|
-
OtherHTMLAttributes<
|
186
|
-
DateInput2OwnProps,
|
187
|
-
InputHTMLAttributes<DateInput2OwnProps & Element>
|
188
|
-
>
|
162
|
+
type PropKeys = keyof DateInput2Props
|
189
163
|
|
190
164
|
const propTypes: PropValidators<PropKeys> = {
|
191
165
|
renderLabel: PropTypes.oneOfType([PropTypes.node, PropTypes.func]).isRequired,
|
@@ -209,8 +183,7 @@ const propTypes: PropValidators<PropKeys> = {
|
|
209
183
|
]),
|
210
184
|
locale: PropTypes.string,
|
211
185
|
timezone: PropTypes.string,
|
212
|
-
withYearPicker: PropTypes.object
|
213
|
-
formatDate: PropTypes.func
|
186
|
+
withYearPicker: PropTypes.object
|
214
187
|
}
|
215
188
|
|
216
189
|
export type { DateInput2Props }
|
package/src/index.ts
CHANGED