From dd36791492851c923167f5751f767e75355c3c92 Mon Sep 17 00:00:00 2001 From: Eric Olkowski Date: Mon, 14 Aug 2023 09:32:34 -0400 Subject: [PATCH 1/2] fix(DatePicker): updated v4 onBlur logic --- .../src/components/DatePicker/DatePicker.tsx | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/packages/react-core/src/components/DatePicker/DatePicker.tsx b/packages/react-core/src/components/DatePicker/DatePicker.tsx index abbd7a2502a..e0bca30e62b 100644 --- a/packages/react-core/src/components/DatePicker/DatePicker.tsx +++ b/packages/react-core/src/components/DatePicker/DatePicker.tsx @@ -11,6 +11,14 @@ import { useImperativeHandle } from 'react'; import { KeyTypes } from '../../helpers'; import { isValidDate } from '../../helpers/datetimeUtils'; +/** Props that customize the requirement of a date */ +export interface DatePickerRequiredObject { + /** Flag indicating the date is required. */ + isRequired?: boolean; + /** Error message to display when the text input is empty and the isRequired prop is also passed in. */ + emptyDateText?: string; +} + /** The main date picker component. */ export interface DatePickerProps @@ -31,7 +39,7 @@ export interface DatePickerProps className?: string; /** How to format the date in the text input. */ dateFormat?: (date: Date) => string; - /** How to format the date in the text input. */ + /** How to parse the date in the text input. */ dateParse?: (value: string) => Date; /** Helper text to display alongside the date picker. */ helperText?: React.ReactNode; @@ -39,7 +47,7 @@ export interface DatePickerProps inputProps?: TextInputProps; /** Flag indicating the date picker is disabled. */ isDisabled?: boolean; - /** Error message to display when the text input cannot be parsed. */ + /** Error message to display when the text input contains a non-empty value in an invalid format. */ invalidFormatText?: string; /** Callback called every time the text input loses focus. */ onBlur?: (event: any, value: string, date?: Date) => void; @@ -49,6 +57,8 @@ export interface DatePickerProps placeholder?: string; /** Props to pass to the popover that contains the calendar month component. */ popoverProps?: Partial>; + /** Options to customize the requirement of a date */ + requiredDateOptions?: DatePickerRequiredObject; /** Functions that returns an error message if a date is invalid. */ validators?: ((date: Date) => string)[]; /** Value of the text input. */ @@ -94,6 +104,7 @@ const DatePickerBase = ( onChange = (): any => undefined, onBlur = (): any => undefined, invalidFormatText = 'Invalid date', + requiredDateOptions, helperText, appendTo = 'parent', popoverProps, @@ -120,6 +131,7 @@ const DatePickerBase = ( const style = { '--pf-c-date-picker__input--c-form-control--width-chars': widthChars, ...styleProps }; const buttonRef = React.useRef(); const datePickerWrapperRef = React.useRef(); + const emptyDateText = requiredDateOptions?.emptyDateText || 'Date cannot be blank'; React.useEffect(() => { setValue(valueProp); @@ -151,17 +163,22 @@ const DatePickerBase = ( }; const onInputBlur = (event: any) => { - if (pristine) { - return; - } const newValueDate = dateParse(value); - if (isValidDate(newValueDate)) { - onBlur(event, value, new Date(newValueDate)); + const dateIsValid = isValidDate(newValueDate); + const onBlurDateArg = dateIsValid ? new Date(newValueDate) : undefined; + onBlur(event, value, onBlurDateArg); + + if (dateIsValid) { setError(newValueDate); - } else { - onBlur(event, value); + } + + if (!dateIsValid && !pristine) { setErrorText(invalidFormatText); } + + if (!dateIsValid && pristine && requiredDateOptions?.isRequired) { + setErrorText(emptyDateText); + } }; const onDateClick = (newValueDate: Date) => { @@ -235,6 +252,10 @@ const DatePickerBase = ( return false; } setPopoverOpen(false); + // If datepicker is required and the popover is opened without the text input + // first receiving focus, we want to validate that the text input is not blank upon + // closing the popover + requiredDateOptions?.isRequired && !value && setErrorText(emptyDateText); if (event.key === KeyTypes.Escape && popoverOpen) { event.stopPropagation(); } @@ -250,6 +271,7 @@ const DatePickerBase = ( Date: Mon, 14 Aug 2023 09:56:35 -0400 Subject: [PATCH 2/2] Added example --- .../components/DatePicker/examples/DatePicker.md | 13 ++++++++++++- .../DatePicker/examples/DatePickerRequired.tsx | 6 ++++++ 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 packages/react-core/src/components/DatePicker/examples/DatePickerRequired.tsx diff --git a/packages/react-core/src/components/DatePicker/examples/DatePicker.md b/packages/react-core/src/components/DatePicker/examples/DatePicker.md index 41b05a1b386..990218674d1 100644 --- a/packages/react-core/src/components/DatePicker/examples/DatePicker.md +++ b/packages/react-core/src/components/DatePicker/examples/DatePicker.md @@ -2,7 +2,7 @@ id: Date picker section: components cssPrefix: pf-c-date-picker -propComponents: ['DatePicker', 'CalendarFormat', 'DatePickerRef'] +propComponents: ['DatePicker', 'CalendarFormat', 'DatePickerRef', 'DatePickerRequiredObject'] beta: true --- @@ -13,6 +13,17 @@ beta: true ```ts file="./DatePickerBasic.tsx" ``` +### Required + +To require users to select a date before continuing, use the `requiredDateOptions.isRequired` property. + +A required date picker will be invalid when the text input is empty and either the text input loses focus or the date picker popover is closed. + +The error message can be customized via the `requiredDateOptions.emptyDateText` property. + +```ts file="./DatePickerRequired.tsx" +``` + ### American format ```ts file="./DatePickerAmerican.tsx" diff --git a/packages/react-core/src/components/DatePicker/examples/DatePickerRequired.tsx b/packages/react-core/src/components/DatePicker/examples/DatePickerRequired.tsx new file mode 100644 index 00000000000..fbeb0f1febb --- /dev/null +++ b/packages/react-core/src/components/DatePicker/examples/DatePickerRequired.tsx @@ -0,0 +1,6 @@ +import React from 'react'; +import { DatePicker } from '@patternfly/react-core'; + +export const DatePickerRequired: React.FunctionComponent = () => ( + +);