From 78a0bf1d53680f05a8fedbf6d5cd1c10775241bc Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Fri, 3 Nov 2023 12:09:35 -0700 Subject: [PATCH] [ft request] Add extra UX affordance for pasting to not require the enter key requires an early return on `onChange` - onPaste fires before onChange, and onChange will bogart the state updates otherwise --- .../date_popover/absolute_tab.test.tsx | 24 ++++++++++ .../date_popover/absolute_tab.tsx | 46 +++++++++++++------ 2 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/components/date_picker/super_date_picker/date_popover/absolute_tab.test.tsx b/src/components/date_picker/super_date_picker/date_popover/absolute_tab.test.tsx index d9113b5181a4..6bdfd69cbca9 100644 --- a/src/components/date_picker/super_date_picker/date_popover/absolute_tab.test.tsx +++ b/src/components/date_picker/super_date_picker/date_popover/absolute_tab.test.tsx @@ -56,6 +56,30 @@ describe('EuiAbsoluteTab', () => { fireEvent.keyDown(input, { key: 'Enter' }); expect(queryByText(formatHelpText)).toHaveClass('euiFormErrorText'); }); + + it('immediately parses pasted text without needing an extra enter keypress', () => { + const { getByTestSubject, queryByText } = render( + + ); + const input = getByTestSubject( + 'superDatePickerAbsoluteDateInput' + ) as HTMLInputElement; + + fireEvent.paste(input, { + clipboardData: { getData: () => '1970-01-01' }, + }); + expect(input).not.toBeInvalid(); + expect(input.value).toContain('Jan 1, 1970'); + + input.value = ''; + fireEvent.paste(input, { + clipboardData: { getData: () => 'not a date' }, + }); + expect(input).toBeInvalid(); + + expect(queryByText(/Allowed formats: /)).toBeInTheDocument(); + expect(queryByText(/Press the Enter key /)).not.toBeInTheDocument(); + }); }); describe('date parsing', () => { diff --git a/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx b/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx index d223dd33026d..672ea85841f1 100644 --- a/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx +++ b/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import React, { Component, ChangeEvent, KeyboardEvent } from 'react'; +import React, { Component, ChangeEvent } from 'react'; import moment, { Moment, LocaleSpecifier } from 'moment'; // eslint-disable-line import/named @@ -52,6 +52,7 @@ export class EuiAbsoluteTab extends Component< EuiAbsoluteTabState > { state: EuiAbsoluteTabState; + isParsing = false; // Store outside of state as a ref for faster/unbatched updates constructor(props: EuiAbsoluteTabProps) { super(props); @@ -89,6 +90,8 @@ export class EuiAbsoluteTab extends Component< }; handleTextChange = (event: ChangeEvent) => { + if (this.isParsing) return; + this.setState({ textInputValue: event.target.value, hasUnparsedText: true, @@ -96,20 +99,23 @@ export class EuiAbsoluteTab extends Component< }); }; - parseUserDateInput = (event: KeyboardEvent) => { - if (event.key !== keys.ENTER) return; - - const { onChange, dateFormat } = this.props; - const { textInputValue } = this.state; + parseUserDateInput = (textInputValue: string) => { + this.isParsing = true; + const finishParsing = () => { + this.isParsing = false; + }; const invalidDateState = { + textInputValue, isTextInvalid: true, valueAsMoment: null, }; if (!textInputValue) { - return this.setState(invalidDateState); + return this.setState(invalidDateState, finishParsing); } + const { onChange, dateFormat } = this.props; + // Attempt to parse with passed `dateFormat` let valueAsMoment = moment(textInputValue, dateFormat, true); let dateIsValid = valueAsMoment.isValid(); @@ -122,14 +128,17 @@ export class EuiAbsoluteTab extends Component< if (dateIsValid) { onChange(valueAsMoment.toISOString()); - this.setState({ - textInputValue: valueAsMoment.format(this.props.dateFormat), - valueAsMoment: valueAsMoment, - hasUnparsedText: false, - isTextInvalid: false, - }); + this.setState( + { + textInputValue: valueAsMoment.format(this.props.dateFormat), + valueAsMoment: valueAsMoment, + hasUnparsedText: false, + isTextInvalid: false, + }, + finishParsing + ); } else { - this.setState(invalidDateState); + this.setState(invalidDateState, finishParsing); } }; @@ -181,7 +190,14 @@ export class EuiAbsoluteTab extends Component< isInvalid={isTextInvalid} value={textInputValue} onChange={this.handleTextChange} - onKeyDown={this.parseUserDateInput} + onPaste={(event) => { + this.parseUserDateInput(event.clipboardData.getData('text')); + }} + onKeyDown={(event) => { + if (event.key === keys.ENTER) { + this.parseUserDateInput(textInputValue); + } + }} data-test-subj="superDatePickerAbsoluteDateInput" prepend={{labelPrefix}} />