diff --git a/packages/ui-date-input/src/DateInput2/README.md b/packages/ui-date-input/src/DateInput2/README.md index 86c7a2d837..3b933f210b 100644 --- a/packages/ui-date-input/src/DateInput2/README.md +++ b/packages/ui-date-input/src/DateInput2/README.md @@ -2,7 +2,7 @@ describes: DateInput2 --- -This component is an updated version of [`DateInput`](/#DateInput) that's easier to configure for developers, has a better UX, better accessibility features and a year picker. We recommend using this instead of `DateInput` which will be deprecated in the future. +`DateInput2` is an experimental upgrade to the existing [`DateInput`](/#DateInput) component, offering easier configuration, better UX, improved accessibility, and a year picker. While it addresses key limitations of `DateInput`, it's still in the experimental phase, with some missing unit tests and potential (though unlikely) API changes. `DateInput` will be deprecated in the future, but for now, developers can start using `DateInput2` and provide feedback. ### Minimal config @@ -21,7 +21,10 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier }} value={this.state.value} width="20rem" - onChange={(e, value) => this.setState({ value })} + onChange={(e, dateString, inputValue) => { + console.log({ dateString, inputValue }) + this.setState({ value: dateString }) + }} invalidDateErrorMessage="Invalid date" /> ) @@ -115,9 +118,9 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier ### Date validation -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. +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 triggered on the blur event of the input field. -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: +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 a 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: ```js --- @@ -170,7 +173,7 @@ render() ### Date formatting -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. +The display format of the date value can be set via the `formatDate` property. It will be applied if the user clicks on a date in the date picker or after the blur event from the input field. Something to pay attention to is that the date string passed back in the callback function **is in UTC timezone**. ```js diff --git a/packages/ui-date-input/src/DateInput2/index.tsx b/packages/ui-date-input/src/DateInput2/index.tsx index 9f56e5ac68..c97ab27754 100644 --- a/packages/ui-date-input/src/DateInput2/index.tsx +++ b/packages/ui-date-input/src/DateInput2/index.tsx @@ -66,6 +66,8 @@ function defaultDateFormatter( --- category: components --- + +@module experimental **/ const DateInput2 = ({ renderLabel, @@ -89,37 +91,25 @@ const DateInput2 = ({ // margin, TODO enable this prop ...rest }: DateInput2Props) => { - const [selectedDate, setSelectedDate] = useState('') + const [inputValue, setInputValue] = useState('') const [inputMessages, setInputMessages] = useState( messages || [] ) const [showPopover, setShowPopover] = useState(false) const localeContext = useContext(ApplyLocaleContext) - useEffect(() => { - // when `value` is changed, validation removes the error message if passes - // but it's NOT adding error message if validation fails for better UX - validateInput(true) - }, [value]) - useEffect(() => { setInputMessages(messages || []) }, [messages]) useEffect(() => { - setSelectedDate(parseDate(value || '')) - }, []) + validateInput(true) + }, [inputValue]) - const handleInputChange = ( - e: SyntheticEvent, - newValue: string, - parsedDate: string = '' - ) => { - // blur event formats the input which shouldn't trigger parsing - if (e.type !== 'blur') { - setSelectedDate(parseDate(newValue)) - } - onChange?.(e, newValue, parsedDate) + const handleInputChange = (e: SyntheticEvent, newValue: string) => { + setInputValue(newValue) + const parsedInput = parseDate(newValue) + onChange?.(e, parsedInput, newValue) } const handleDateSelected = ( @@ -129,16 +119,16 @@ const DateInput2 = ({ ) => { const formattedDate = formatDate(dateString, getLocale(), getTimezone()) const parsedDate = parseDate(dateString) - setSelectedDate(parsedDate) - handleInputChange(e, formattedDate, parsedDate) + setInputValue(formattedDate) setShowPopover(false) + onChange?.(e, parsedDate, formattedDate) onRequestValidateDate?.(dateString, true) } // onlyRemoveError is used to remove the error msg immediately when the user inputs a valid date (and don't wait for blur event) const validateInput = (onlyRemoveError = false): boolean => { // don't validate empty input - if (!value || parseDate(value) || selectedDate) { + if (!inputValue || parseDate(inputValue) || value) { setInputMessages(messages || []) return true } @@ -159,12 +149,25 @@ const DateInput2 = ({ return false } + const handleBlur = (e: SyntheticEvent) => { + const parsedDate = parseDate(inputValue) + if (parsedDate) { + const formattedDate = formatDate(parsedDate, getLocale(), getTimezone()) + setInputValue(formattedDate) + onChange?.(e, parsedDate, formattedDate) + } + validateInput(false) + onRequestValidateDate?.(value, !!parsedDate) + onBlur?.(e) + } + const getLocale = () => { if (locale) { return locale } else if (localeContext.locale) { return localeContext.locale } + // default to the system's locale return Locale.browserLocale() } @@ -178,16 +181,6 @@ const DateInput2 = ({ return Intl.DateTimeFormat().resolvedOptions().timeZone } - const handleBlur = (e: SyntheticEvent) => { - const isInputValid = validateInput(false) - if (isInputValid && selectedDate) { - const formattedDate = formatDate(selectedDate, getLocale(), getTimezone()) - handleInputChange(e, formattedDate, selectedDate) - } - onRequestValidateDate?.(value, isInputValid) - onBlur?.(e) - } - return ( void /** * Callback executed when the input fires a blur event. @@ -99,21 +99,12 @@ type DateInput2OwnProps = { */ messages?: FormMessage[] /** - * Callback fired requesting the calendar be shown. - */ - onRequestShowCalendar?: (event: SyntheticEvent) => void - /** - * Callback fired requesting the calendar be hidden. - */ - onRequestHideCalendar?: (event: SyntheticEvent) => void - /** - * Callback fired when the input is blurred. Feedback should be provided - * to the user when this function is called if the selected date or input - * value is invalid. The component has an internal check whether the date can - * be parsed to a valid date. + * Callback fired when the input is blurred or a date is selected from the calendar. + * Feedback should be provided to the user when this function is called if the selected date or input + * value is invalid. The component has an internal check whether the date can be parsed to a valid date. */ onRequestValidateDate?: ( - value?: string, + isoDateString?: string, internalValidationPassed?: boolean ) => void | FormMessage[] /** @@ -200,8 +191,6 @@ const propTypes: PropValidators = { isInline: PropTypes.bool, width: PropTypes.string, messages: PropTypes.arrayOf(FormPropTypes.message), - onRequestShowCalendar: PropTypes.func, - onRequestHideCalendar: PropTypes.func, onRequestValidateDate: PropTypes.func, invalidDateErrorMessage: PropTypes.oneOfType([ PropTypes.func,