Skip to content

Commit

Permalink
Grouped isRequired and emptyDateText props as one
Browse files Browse the repository at this point in the history
  • Loading branch information
thatblindgeye committed Aug 8, 2023
1 parent c54b673 commit c62c19c
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 25 deletions.
24 changes: 15 additions & 9 deletions packages/react-core/src/components/DatePicker/DatePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ import { KeyTypes } from '../../helpers';
import { isValidDate } from '../../helpers/datetimeUtils';
import { HelperText, HelperTextItem } from '../HelperText';

/** 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 Down Expand Up @@ -40,12 +48,8 @@ export interface DatePickerProps
inputProps?: TextInputProps;
/** Flag indicating the date picker is disabled. */
isDisabled?: boolean;
/** Flag indicating the date picker is required. */
isRequired?: boolean;
/** Error message to display when the text input contains a non-empty value in an invalid format. */
invalidFormatText?: string;
/** Error message to display when the text input is empty and the isRequired prop is also passed in. */
emptyDateText?: string;
/** Callback called every time the text input loses focus. */
onBlur?: (event: any, value: string, date?: Date) => void;
/** Callback called every time the text input value changes. */
Expand All @@ -54,6 +58,8 @@ export interface DatePickerProps
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 @@ -92,15 +98,14 @@ const DatePickerBase = (
dateFormat = yyyyMMddFormat,
dateParse = (val: string) => val.split('-').length === 3 && new Date(`${val}T00:00:00`),
isDisabled = false,
isRequired = false,
placeholder = 'YYYY-MM-DD',
value: valueProp = '',
'aria-label': ariaLabel = 'Date picker',
buttonAriaLabel = 'Toggle date picker',
onChange = (): any => undefined,
onBlur = (): any => undefined,
invalidFormatText = 'Invalid date',
emptyDateText = 'Date cannot be blank',
requiredDateOptions,
helperText,
appendTo = 'inline',
popoverProps,
Expand Down Expand Up @@ -128,6 +133,7 @@ const DatePickerBase = (
const buttonRef = React.useRef<HTMLButtonElement>();
const datePickerWrapperRef = React.useRef<HTMLDivElement>();
const triggerRef = React.useRef<HTMLDivElement>();
const emptyDateText = requiredDateOptions?.emptyDateText || 'Date cannot be blank';

React.useEffect(() => {
setValue(valueProp);
Expand Down Expand Up @@ -172,7 +178,7 @@ const DatePickerBase = (
setErrorText(invalidFormatText);
}

if (!dateIsValid && pristine && isRequired) {
if (!dateIsValid && pristine && requiredDateOptions?.isRequired) {
setErrorText(emptyDateText);
}
};
Expand Down Expand Up @@ -250,7 +256,7 @@ const DatePickerBase = (
// 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
isRequired && !value && setErrorText(emptyDateText);
requiredDateOptions?.isRequired && !value && setErrorText(emptyDateText);
}
if (event.key === KeyTypes.Escape && popoverOpen) {
event.stopPropagation();
Expand All @@ -269,7 +275,7 @@ const DatePickerBase = (
<InputGroupItem isFill>
<TextInput
isDisabled={isDisabled}
isRequired={isRequired}
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 @@ -100,7 +100,7 @@ test('Shows "Invalid date" instead of helperText when text input contains invali
expect(screen.getByText('Invalid date')).toBeVisible();
});

test('Does not render text input as invalid when isRequired is false', async () => {
test('Does not render text input as invalid when requiredDateOptions.isRequired is false', async () => {
const user = userEvent.setup();

render(<DatePicker />);
Expand All @@ -111,7 +111,7 @@ test('Does not render text input as invalid when isRequired is false', async ()
expect(screen.getByRole('textbox')).not.toHaveAttribute('aria-invalid', 'true');
});

test('Does not render emptyDateText when isRequired is false', async () => {
test('Does not render emptyDateText when requiredDateOptions.isRequired is false', async () => {
const user = userEvent.setup();

render(<DatePicker />);
Expand All @@ -122,45 +122,45 @@ test('Does not render emptyDateText when isRequired is false', async () => {
expect(screen.queryByText('Date cannot be blank')).not.toBeInTheDocument;
});

test('Renders text input as invalid on blur when isRequired is false', async () => {
test('Renders text input as invalid on blur when requiredDateOptions.isRequired is true', async () => {
const user = userEvent.setup();

render(<DatePicker isRequired />);
render(<DatePicker requiredDateOptions={{ isRequired: true }} />);

await user.click(screen.getByRole('textbox'));
await user.click(document.body);

expect(screen.getByRole('textbox')).toHaveAttribute('aria-invalid', 'true');
});

test('Renders default emptyDateText on blur when isRequired is passed', async () => {
test('Renders default emptyDateText on blur when requiredDateOptions.isRequired is true', async () => {
const user = userEvent.setup();

render(<DatePicker isRequired />);
render(<DatePicker requiredDateOptions={{ isRequired: true }} />);

await user.click(screen.getByRole('textbox'));
await user.click(document.body);

expect(screen.getByText('Date cannot be blank')).toBeInTheDocument();
});

test('Renders custom emptyDateText when isRequired is true', async () => {
test('Renders custom emptyDateText when requiredDateOptions.isRequired is true', async () => {
const user = userEvent.setup();

render(<DatePicker emptyDateText="Required in test" isRequired />);
render(<DatePicker requiredDateOptions={{ isRequired: true, emptyDateText: 'Required in test' }} />);

await user.click(screen.getByRole('textbox'));
await user.click(document.body);

expect(screen.getByText('Required in test')).toBeInTheDocument();
});

test('Shows emptyDateText instead of helperText when text input is empty and isRequired is true', async () => {
test('Shows emptyDateText instead of helperText when text input is empty and requiredDateOptions.isRequired is true', async () => {
const user = userEvent.setup();

render(
<DatePicker
isRequired
requiredDateOptions={{ isRequired: true }}
helperText={
<HelperText>
<HelperTextItem>Help me</HelperTextItem>
Expand All @@ -176,10 +176,10 @@ test('Shows emptyDateText instead of helperText when text input is empty and isR
expect(screen.getByText('Date cannot be blank')).toBeVisible();
});

test('Renders text input as invalid when isRequired is false and popover is closed without selection', async () => {
test('Renders text input as invalid when requiredDateOptions.isRequired is true and popover is closed without selection', async () => {
const user = userEvent.setup();

render(<DatePicker isRequired />);
render(<DatePicker requiredDateOptions={{ isRequired: true }} />);

await user.click(screen.getByRole('button', { name: 'Toggle date picker' }));
await user.click(document.body);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ id: Date picker
section: components
subsection: date-and-time
cssPrefix: pf-v5-c-date-picker
propComponents: ['DatePicker', 'CalendarFormat', 'DatePickerRef']
propComponents: ['DatePicker', 'CalendarFormat', 'DatePickerRef', 'DatePickerRequiredObject']
---

## Examples
Expand All @@ -16,11 +16,11 @@ propComponents: ['DatePicker', 'CalendarFormat', 'DatePickerRef']

### Required

To require users to select a date before continuing, use the `isRequired` property.
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 `emptyDateText` property.
The error message can be customized via the `requiredDateOptions.emptyDateText` property.

```ts file="./DatePickerRequired.tsx"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ import React from 'react';
import { DatePicker } from '@patternfly/react-core';

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

0 comments on commit c62c19c

Please sign in to comment.