From 7140a4e083b95178fcc88a69d8ebbda27590099d Mon Sep 17 00:00:00 2001 From: Cee Chen <549407+cee-chen@users.noreply.github.com> Date: Thu, 23 May 2024 08:31:28 -0700 Subject: [PATCH] [EuiDatePicker] Handle invalid `selected` moment formats (#7784) --- packages/eui/changelogs/upcoming/7784.md | 3 +++ .../date_picker/date_picker.test.tsx | 27 +++++++++++++++++-- .../components/date_picker/date_picker.tsx | 6 ++++- .../date_picker/react-datepicker/src/index.js | 7 +++-- 4 files changed, 38 insertions(+), 5 deletions(-) create mode 100644 packages/eui/changelogs/upcoming/7784.md diff --git a/packages/eui/changelogs/upcoming/7784.md b/packages/eui/changelogs/upcoming/7784.md new file mode 100644 index 00000000000..df3e5f69b77 --- /dev/null +++ b/packages/eui/changelogs/upcoming/7784.md @@ -0,0 +1,3 @@ +**Bug fixes** + +- Fixed `EuiDatePicker` to more gracefully handle incorrectly formatted `selected` Moment dates, instead of simply crashing diff --git a/packages/eui/src/components/date_picker/date_picker.test.tsx b/packages/eui/src/components/date_picker/date_picker.test.tsx index d0f2208f559..0928286c511 100644 --- a/packages/eui/src/components/date_picker/date_picker.test.tsx +++ b/packages/eui/src/components/date_picker/date_picker.test.tsx @@ -7,9 +7,10 @@ */ import React from 'react'; -import { render } from '../../test/rtl'; -import { requiredProps } from '../../test'; import moment from 'moment'; +import { fireEvent } from '@testing-library/react'; +import { render, waitForEuiPopoverOpen } from '../../test/rtl'; +import { requiredProps } from '../../test'; import { EuiDatePicker } from './date_picker'; import { EuiContext } from '../context'; @@ -21,6 +22,28 @@ describe('EuiDatePicker', () => { expect(container.firstChild).toMatchSnapshot(); }); + it('handles invalid `selected` dates', async () => { + // Silence the console warning from the invalid moment date + jest.spyOn(console, 'warn').mockImplementation(() => {}); + const { container } = render( + + ); + const datepickerInput = container.querySelector('input.euiDatePicker'); + + expect(datepickerInput).toHaveValue('Invalid date'); + expect(datepickerInput).toBeInvalid(); + + // The calendar date picker should load in a popover, but no date should be selected + fireEvent.focus(datepickerInput!); + await waitForEuiPopoverOpen(); + const calendar = document.querySelector('.react-datepicker__month'); + expect(calendar).toBeInTheDocument(); + const selected = document.querySelector('.react-datepicker__day--selected'); + expect(selected).not.toBeInTheDocument(); + + jest.restoreAllMocks(); + }); + test('compressed', () => { const { container } = render(); // TODO: Should probably be a visual snapshot test diff --git a/packages/eui/src/components/date_picker/date_picker.tsx b/packages/eui/src/components/date_picker/date_picker.tsx index 5867a4224dc..69c2582b17d 100644 --- a/packages/eui/src/components/date_picker/date_picker.tsx +++ b/packages/eui/src/components/date_picker/date_picker.tsx @@ -164,7 +164,7 @@ export const EuiDatePicker: FunctionComponent = ({ injectTimes, inline, inputRef, - isInvalid, + isInvalid: _isInvalid, isLoading, locale, maxDate, @@ -193,6 +193,10 @@ export const EuiDatePicker: FunctionComponent = ({ 'euiDatePicker--shadow': inline && shadow, }); + // Check for whether the passed `selected` moment date is valid + const isInvalid = + _isInvalid || (selected?.isValid() === false ? true : undefined); + const numIconsClass = controlOnly ? false : getFormControlClassNameForIconCount({ diff --git a/packages/eui/src/components/date_picker/react-datepicker/src/index.js b/packages/eui/src/components/date_picker/react-datepicker/src/index.js index a8048cfcb3e..cc30a496103 100644 --- a/packages/eui/src/components/date_picker/react-datepicker/src/index.js +++ b/packages/eui/src/components/date_picker/react-datepicker/src/index.js @@ -83,6 +83,9 @@ function hasPreSelectionChanged(date1, date2) { function hasSelectionChanged(date1, date2) { if (date1 && date2) { + if (date1._isValid === false && date2._isValid === false) { + return false; + } return !equals(date1, date2); } @@ -288,7 +291,7 @@ export default class DatePicker extends React.Component { return { open: this.props.startOpen || false, preventFocus: false, - preSelection: this.props.selected + preSelection: this.props.selected?._isValid ? newDate(this.props.selected) : boundedPreSelection, // transforming highlighted days (perhaps nested array) @@ -693,7 +696,7 @@ export default class DatePicker extends React.Component { useWeekdaysShort={this.props.useWeekdaysShort} formatWeekDay={this.props.formatWeekDay} dropdownMode={this.props.dropdownMode} - selected={this.props.selected} + selected={this.props.selected?._isValid ? this.props.selected : undefined} preSelection={this.state.preSelection} onSelect={this.handleSelect} onWeekSelect={this.props.onWeekSelect}