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}