diff --git a/packages/ui-date-input/src/DateInput2/README.md b/packages/ui-date-input/src/DateInput2/README.md index 0d0554c0d0..f9b383c1e5 100644 --- a/packages/ui-date-input/src/DateInput2/README.md +++ b/packages/ui-date-input/src/DateInput2/README.md @@ -1,5 +1,5 @@ --- -describes: DateInput +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. @@ -74,7 +74,7 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier invalidDateErrorMessage="Invalid date" withYearPicker={{ screenReaderLabel: 'Year picker', - startYear: 1999, + startYear: 1900, endYear: 2024 }} /> @@ -103,7 +103,7 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier invalidDateErrorMessage="Invalid date" withYearPicker={{ screenReaderLabel: 'Year picker', - startYear: 1999, + startYear: 1900, endYear: 2024 }} /> @@ -112,3 +112,54 @@ This component is an updated version of [`DateInput`](/#DateInput) that's easier render() ``` + +### With custom validation + +```js +--- +type: example +--- +const Example = () => { + const [value, setValue] = useState('') + const [messages, setMessages] = useState([]) + + const handleDateValidation = (dateString, isValidDate) => { + if (!isValidDate) { + setMessages([{ + type: 'error', + text: 'This is not a valid date' + }]) + } else if (new Date(dateString) < new Date('January 1, 1900')) { + setMessages([{ + type: 'error', + text: 'Use date after January 1, 1900' + }]) + } else { + setMessages([]) + } + } + + return ( + setValue(value)} + withYearPicker={{ + screenReaderLabel: 'Year picker', + startYear: 1900, + endYear: 2024 + }} + /> + ) +} + +render() +``` diff --git a/packages/ui-date-input/src/DateInput2/index.tsx b/packages/ui-date-input/src/DateInput2/index.tsx index a311a28b55..a66ae4e80a 100644 --- a/packages/ui-date-input/src/DateInput2/index.tsx +++ b/packages/ui-date-input/src/DateInput2/index.tsx @@ -56,17 +56,7 @@ function isValidMomentDate( return moment .tz( dateString, - [ - moment.ISO_8601, - 'llll', - 'LLLL', - 'lll', - 'LLL', - 'll', - 'LL', - 'l', - 'L' - ], + [moment.ISO_8601, 'llll', 'LLLL', 'lll', 'LLL', 'll', 'LL', 'l', 'L'], locale, true, timezone @@ -92,6 +82,7 @@ const DateInput2 = ({ onChange, onBlur, withYearPicker, + onRequestValidateDate, invalidDateErrorMessage, locale, timezone, @@ -145,14 +136,16 @@ const DateInput2 = ({ setInputMessages(messages || []) return true } - if (!onlyRemoveError) { - setInputMessages([ + if (!onlyRemoveError && typeof invalidDateErrorMessage === 'string') { + setInputMessages((messages) => [ { type: 'error', - text: invalidDateErrorMessage || '', - } + text: invalidDateErrorMessage + }, + ...messages ]) } + return false } @@ -176,17 +169,18 @@ const DateInput2 = ({ } const handleBlur = (e: SyntheticEvent) => { - onBlur?.(e) const isInputValid = validateInput(false) if (isInputValid && value) { - const formattedDate = new Date(value).toLocaleDateString(getLocale(), { - month: 'long', - year: 'numeric', - day: 'numeric', - timeZone: getTimezone() - }) - handleInputChange(e, formattedDate) + const formattedDate = new Date(value).toLocaleDateString(getLocale(), { + month: 'long', + year: 'numeric', + day: 'numeric', + timeZone: getTimezone() + }) + handleInputChange(e, formattedDate) } + onRequestValidateDate?.(value, isInputValid) + onBlur?.(e) } return ( diff --git a/packages/ui-date-input/src/DateInput2/props.ts b/packages/ui-date-input/src/DateInput2/props.ts index 543eec034b..186fd5466d 100644 --- a/packages/ui-date-input/src/DateInput2/props.ts +++ b/packages/ui-date-input/src/DateInput2/props.ts @@ -99,7 +99,18 @@ type DateInput2Props = { */ onRequestHideCalendar?: (event: SyntheticEvent) => void /** - * The message shown to the user when the date is invalid. + * 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. + */ + onRequestValidateDate?: ( + value?: string, + internalValidationPassed?: boolean + ) => void | FormMessage[] + /** + * The message shown to the user when the date is invalid. If this prop is not set, validation is bypassed. + * If it's set to an empty string, validation happens and the input border changes to red if validation hasn't passed. **/ invalidDateErrorMessage?: string /** @@ -165,6 +176,7 @@ const propTypes: PropValidators = { messages: PropTypes.arrayOf(FormPropTypes.message), onRequestShowCalendar: PropTypes.func, onRequestHideCalendar: PropTypes.func, + onRequestValidateDate: PropTypes.func, invalidDateErrorMessage: PropTypes.oneOfType([ PropTypes.func, PropTypes.string diff --git a/packages/ui-text-input/src/TextInput/styles.ts b/packages/ui-text-input/src/TextInput/styles.ts index bb84c43e09..3bd79c1f5c 100644 --- a/packages/ui-text-input/src/TextInput/styles.ts +++ b/packages/ui-text-input/src/TextInput/styles.ts @@ -220,6 +220,11 @@ const generateStyle = ( ...flexBase }, afterElement: { + // TODO this is added for the case when there is an IconButton inside a TextInput (like in the DateInput2 component) + // and the button size makes the whole input 2px larger (because of the borders) + // this is not the best solution and in the long term we should work with the design team to figure out how to handle such cases + marginTop: '-1px', + marginBottom: '-1px', label: 'textInput__afterElement', ...flexItemBase, paddingInlineEnd: componentTheme.padding,