Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(DatePicker): updated v4 onBlur logic #9491

Merged
merged 2 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 31 additions & 9 deletions packages/react-core/src/components/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@
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
Expand All @@ -31,15 +39,15 @@
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;
/** Additional props for the text input. */
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;
Expand All @@ -49,6 +57,8 @@
placeholder?: string;
/** Props to pass to the popover that contains the calendar month component. */
popoverProps?: Partial<Omit<PopoverProps, 'appendTo'>>;
/** 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. */
Expand Down Expand Up @@ -94,6 +104,7 @@
onChange = (): any => undefined,
onBlur = (): any => undefined,
invalidFormatText = 'Invalid date',
requiredDateOptions,
helperText,
appendTo = 'parent',
popoverProps,
Expand All @@ -116,15 +127,16 @@
const [popoverOpen, setPopoverOpen] = React.useState(false);
const [selectOpen, setSelectOpen] = React.useState(false);
const [pristine, setPristine] = React.useState(true);
const widthChars = React.useMemo(() => Math.max(dateFormat(new Date()).length, placeholder.length), [dateFormat]);

Check warning on line 130 in packages/react-core/src/components/DatePicker/DatePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

React Hook React.useMemo has a missing dependency: 'placeholder.length'. Either include it or remove the dependency array
const style = { '--pf-c-date-picker__input--c-form-control--width-chars': widthChars, ...styleProps };
const buttonRef = React.useRef<HTMLButtonElement>();
const datePickerWrapperRef = React.useRef<HTMLDivElement>();
const emptyDateText = requiredDateOptions?.emptyDateText || 'Date cannot be blank';

React.useEffect(() => {
setValue(valueProp);
setValueDate(dateParse(valueProp));
}, [valueProp]);

Check warning on line 139 in packages/react-core/src/components/DatePicker/DatePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

React Hook React.useEffect has a missing dependency: 'dateParse'. Either include it or remove the dependency array. If 'dateParse' changes too often, find the parent component that defines it and wrap that definition in useCallback

React.useEffect(() => {
setPristine(!value);
Expand All @@ -132,7 +144,7 @@
if (errorText && isValidDate(newValueDate)) {
setError(newValueDate);
}
}, [value]);

Check warning on line 147 in packages/react-core/src/components/DatePicker/DatePicker.tsx

View workflow job for this annotation

GitHub Actions / lint

React Hook React.useEffect has missing dependencies: 'dateParse', 'errorText', and 'setError'. Either include them or remove the dependency array. If 'dateParse' changes too often, find the parent component that defines it and wrap that definition in useCallback

const setError = (date: Date) => {
setErrorText(validators.map(validator => validator(date)).join('\n') || '');
Expand All @@ -151,17 +163,22 @@
};

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) => {
Expand Down Expand Up @@ -235,6 +252,10 @@
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();
}
Expand All @@ -250,6 +271,7 @@
<InputGroup>
<TextInput
isDisabled={isDisabled}
isRequired={requiredDateOptions?.isRequired}
aria-label={ariaLabel}
placeholder={placeholder}
validated={errorText.trim() ? 'error' : 'default'}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
---

Expand All @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import React from 'react';
import { DatePicker } from '@patternfly/react-core';

export const DatePickerRequired: React.FunctionComponent = () => (
<DatePicker requiredDateOptions={{ isRequired: true, emptyDateText: 'Date is required' }} />
);
Loading