From 23aa2a2b4642c11cf31316e6fbb40e09e80168a3 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Tue, 23 Jul 2024 11:48:45 -0700 Subject: [PATCH 1/9] [tech debt] Convert EuiRelativeTab from class to function component - which will allow us to use a padding style hook in the future + remove unused `position` prop + reorder imports --- .../date_popover/date_popover_content.tsx | 1 - .../date_popover/relative_tab.tsx | 321 ++++++++---------- 2 files changed, 145 insertions(+), 177 deletions(-) diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx index 27f5df2eae8..29b0fd40780 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx @@ -115,7 +115,6 @@ export const EuiDatePopoverContent: FunctionComponent< } onChange={onChange} roundUp={roundUp} - position={position} labelPrefix={labelPrefix} timeOptions={timeOptions} /> diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx index 08c19ee0085..f8de82fe979 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/relative_tab.tsx @@ -6,9 +6,20 @@ * Side Public License, v 1. */ -import React, { Component, ChangeEventHandler } from 'react'; +import React, { + FunctionComponent, + ChangeEvent, + useState, + useCallback, + useMemo, + useRef, +} from 'react'; import dateMath from '@elastic/datemath'; -import { htmlIdGenerator } from '../../../../services'; +import { LocaleSpecifier } from 'moment'; + +import { useUpdateEffect, useGeneratedHtmlId } from '../../../../services'; +import { useEuiI18n, EuiI18n } from '../../../i18n'; +import { EuiScreenReaderOnly } from '../../../accessibility'; import { EuiFlexGroup, EuiFlexItem } from '../../../flex'; import { EuiForm, @@ -20,20 +31,17 @@ import { EuiFormLabel, EuiSwitchEvent, } from '../../../form'; +import { EuiPopoverFooter } from '../../../popover'; import { EuiSpacer } from '../../../spacer'; +import { RelativeParts, TimeUnitId } from '../../types'; import { TimeOptions } from '../time_options'; +import { INVALID_DATE } from '../date_modes'; import { parseRelativeParts, toRelativeStringFromParts, } from '../relative_utils'; -import { EuiScreenReaderOnly } from '../../../accessibility'; -import { EuiI18n } from '../../../i18n'; -import { RelativeParts, TimeUnitId } from '../../types'; -import { LocaleSpecifier } from 'moment'; // eslint-disable-line import/named import { EuiDatePopoverContentProps } from './date_popover_content'; -import { INVALID_DATE } from '../date_modes'; -import { EuiPopoverFooter } from '../../../popover'; export interface EuiRelativeTabProps { dateFormat: string; @@ -41,189 +49,150 @@ export interface EuiRelativeTabProps { value: string; onChange: EuiDatePopoverContentProps['onChange']; roundUp?: boolean; - position: 'start' | 'end'; labelPrefix: string; timeOptions: TimeOptions; } -interface EuiRelativeTabState - extends Pick { - count: number | undefined; -} +export const EuiRelativeTab: FunctionComponent = ({ + timeOptions: { relativeOptions, relativeRoundingLabels }, + dateFormat, + locale, + value, + onChange, + roundUp, + labelPrefix, +}) => { + const initialRelativeParts = useRef(parseRelativeParts(value)); + const { roundUnit } = initialRelativeParts.current; -export class EuiRelativeTab extends Component< - EuiRelativeTabProps, - EuiRelativeTabState -> { - state: EuiRelativeTabState = { - ...parseRelativeParts(this.props.value), - }; + const [unit, setUnit] = useState( + initialRelativeParts.current.unit + ); + const onUnitChange = useCallback((event: ChangeEvent) => { + setUnit(event.target.value); + }, []); - relativeDateInputNumberDescriptionId = htmlIdGenerator()(); + const [round, setRound] = useState( + initialRelativeParts.current.round + ); + const onRoundChange = useCallback((event: EuiSwitchEvent) => { + setRound(event.target.checked); + }, []); - onCountChange: ChangeEventHandler = (event) => { + const [count, setCount] = useState( + initialRelativeParts.current.count + ); + const onCountChange = useCallback((event: ChangeEvent) => { const sanitizedValue = parseInt(event.target.value, 10); - this.setState( - { - count: isNaN(sanitizedValue) ? undefined : sanitizedValue, - }, - this.handleChange - ); - }; - - onUnitChange: ChangeEventHandler = (event) => { - this.setState( - { - unit: event.target.value, - }, - this.handleChange - ); - }; - - onRoundChange = (event: EuiSwitchEvent) => { - this.setState( - { - round: event.target.checked, - }, - this.handleChange - ); - }; - - handleChange = () => { - const { count, round, roundUnit, unit } = this.state; - const { onChange } = this.props; - if (count === undefined || count < 0) { - return; - } + const count = isNaN(sanitizedValue) ? undefined : sanitizedValue; + setCount(count); + }, []); + + useUpdateEffect(() => { + if (count === undefined || count < 0) return; + const date = toRelativeStringFromParts({ count, - round, + round: !!round, roundUnit, unit, }); onChange(date); - }; + }, [onChange, count, round, roundUnit, unit]); - render() { - const { relativeOptions, relativeRoundingLabels } = this.props.timeOptions; - const { count, unit } = this.state; - const invalidDate = this.props.value === INVALID_DATE; - const invalidValue = count === undefined || count < 0; - const isInvalid = invalidValue || invalidDate; + const invalidDate = value === INVALID_DATE; + const invalidValue = count === undefined || count < 0; + const isInvalid = invalidValue || invalidDate; - const parsedValue = dateMath.parse(this.props.value, { - roundUp: this.props.roundUp, - }); + const formattedValue = useMemo(() => { + if (isInvalid) return ''; - const formattedValue = - isInvalid || !parsedValue || !parsedValue.isValid() - ? '' - : parsedValue - .locale(this.props.locale || 'en') - .format(this.props.dateFormat); - - const getErrorMessage = ({ - numberInputError, - dateInputError, - }: { - numberInputError: string; - dateInputError: string; - }) => { - if (invalidValue) return numberInputError; - if (invalidDate) return dateInputError; - return null; - }; - - return ( - <> - - - - = 0', - 'Time span amount', - 'Must be a valid range', - ]} - > - {([ - numberInputError, - numberInputLabel, - dateInputError, - ]: string[]) => ( - - - - )} - - - - - {(unitInputLabel: string) => ( - - )} - - - - - {this.props.labelPrefix}} - /> - -

- = 0' + ); + const dateInputError = useEuiI18n( + 'euiRelativeTab.dateInputError', + 'Must be a valid range' + ); + const unitSelectAriaLabel = useEuiI18n( + 'euiRelativeTab.unitInputLabel', + 'Relative time span' + ); + + return ( + <> + + + + + -

-
-
- - - - - ); - } -} + + + + + + + + {labelPrefix}} + /> + +

+ +

+
+ + + + + + ); +}; From 9b0df9394007bf653b81bda0d677b3ae25525106 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Tue, 23 Jul 2024 11:50:39 -0700 Subject: [PATCH 2/9] Convert popover padding CSS to Emotion hook usages --- .../date_popover/_date_popover_content.scss | 8 -------- .../date_popover/date_popover_content.tsx | 7 ++----- .../super_date_picker/date_popover/relative_tab.tsx | 3 ++- 3 files changed, 4 insertions(+), 14 deletions(-) diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss b/packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss index 0182e258066..b99ac082389 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss @@ -7,11 +7,3 @@ width: $euiDatePickerCalendarWidth; } } - -.euiDatePopoverContent__padded { - padding: $euiSizeS; -} - -.euiDatePopoverContent__padded--large { - padding: $euiSize; -} diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx index 29b0fd40780..365e3b93bca 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx @@ -8,6 +8,7 @@ import React, { FunctionComponent } from 'react'; +import { useEuiPaddingCSS } from '../../../../global_styling'; import { EuiI18n, useEuiI18n } from '../../../i18n'; import { EuiTabbedContent, EuiTabbedContentProps } from '../../../tabs'; import { EuiText } from '../../../text'; @@ -126,11 +127,7 @@ export const EuiDatePopoverContent: FunctionComponent< id: DATE_MODES.NOW, name: nowLabel, content: ( - +

= ({ return ( <> - + Date: Tue, 23 Jul 2024 12:19:22 -0700 Subject: [PATCH 3/9] Convert `.euiDatePopoverContent` to Emotion --- .../date_popover/_date_popover_content.scss | 9 ------ .../date_popover_content.styles.ts | 31 +++++++++++++++++++ .../date_popover/date_popover_content.tsx | 15 +++++---- 3 files changed, 40 insertions(+), 15 deletions(-) create mode 100644 packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.styles.ts diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss b/packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss index b99ac082389..e69de29bb2d 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss @@ -1,9 +0,0 @@ -.euiDatePopoverContent, -.euiDatePopoverContent .react-datepicker { - width: $euiFormMaxWidth; - max-width: 100%; - - @include euiBreakpoint('xs') { - width: $euiDatePickerCalendarWidth; - } -} diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.styles.ts b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.styles.ts new file mode 100644 index 00000000000..9a7f4c3f45f --- /dev/null +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.styles.ts @@ -0,0 +1,31 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { css } from '@emotion/react'; + +import { UseEuiTheme } from '../../../../services'; +import { euiMaxBreakpoint, logicalCSS } from '../../../../global_styling'; +import { euiFormVariables } from '../../../form/form.styles'; + +export const euiDatePopoverContentStyles = (euiThemeContext: UseEuiTheme) => { + const { maxWidth } = euiFormVariables(euiThemeContext); + + return { + euiDatePopoverContent: css` + &, + & .react-datepicker { + ${logicalCSS('width', maxWidth)} + ${logicalCSS('max-width', '100%')} + + ${euiMaxBreakpoint(euiThemeContext, 's')} { + ${logicalCSS('width', '284px')}/* TODO: $euiDatePickerCalendarWidth */ + } + } + `, + }; +}; diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx index 365e3b93bca..a7b645076dc 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx @@ -7,16 +7,15 @@ */ import React, { FunctionComponent } from 'react'; +import { LocaleSpecifier } from 'moment'; +import { useEuiMemoizedStyles } from '../../../../services'; import { useEuiPaddingCSS } from '../../../../global_styling'; import { EuiI18n, useEuiI18n } from '../../../i18n'; import { EuiTabbedContent, EuiTabbedContentProps } from '../../../tabs'; import { EuiText } from '../../../text'; import { EuiButton } from '../../../button'; -import { EuiAbsoluteTab } from './absolute_tab'; -import { EuiRelativeTab } from './relative_tab'; - import { TimeOptions } from '../time_options'; import { getDateMode, @@ -24,7 +23,9 @@ import { toAbsoluteString, toRelativeString, } from '../date_modes'; -import { LocaleSpecifier } from 'moment'; // eslint-disable-line import/named +import { EuiAbsoluteTab } from './absolute_tab'; +import { EuiRelativeTab } from './relative_tab'; +import { euiDatePopoverContentStyles } from './date_popover_content.styles'; export interface EuiDatePopoverContentProps { value: string; @@ -53,6 +54,8 @@ export const EuiDatePopoverContent: FunctionComponent< utcOffset, timeOptions, }) => { + const styles = useEuiMemoizedStyles(euiDatePopoverContentStyles); + const onTabClick: EuiTabbedContentProps['onTabClick'] = (selectedTab) => { switch (selectedTab.id) { case DATE_MODES.ABSOLUTE: @@ -131,8 +134,7 @@ export const EuiDatePopoverContent: FunctionComponent<

Date: Tue, 23 Jul 2024 13:26:20 -0700 Subject: [PATCH 4/9] [tech debt] Convert EuiAbsoluteTab from class to function component - will allow us to use hooks for Emotion styles, might as well do this since we did EuiRelativeTab as well - create new `isReadyToParse` state to replace old `isParsing` ref + remove unused `position` prop --- .../date_popover/absolute_tab.tsx | 351 ++++++++---------- .../date_popover/date_popover_content.tsx | 1 - 2 files changed, 160 insertions(+), 192 deletions(-) diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx index 69035c0d22c..4dd47866d9a 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx @@ -6,17 +6,23 @@ * Side Public License, v 1. */ -import React, { Component, ChangeEvent, FormEvent } from 'react'; - -import moment, { Moment, LocaleSpecifier } from 'moment'; // eslint-disable-line import/named - +import React, { + FunctionComponent, + ChangeEvent, + FormEvent, + useState, + useEffect, + useCallback, +} from 'react'; +import moment, { Moment, LocaleSpecifier } from 'moment'; import dateMath from '@elastic/datemath'; +import { useUpdateEffect } from '../../../../services'; +import { useEuiI18n } from '../../../i18n'; import { EuiFormRow, EuiFieldText, EuiFormLabel } from '../../../form'; import { EuiFlexGroup } from '../../../flex'; import { EuiButtonIcon } from '../../../button'; import { EuiCode } from '../../../code'; -import { EuiI18n } from '../../../i18n'; import { EuiDatePicker, EuiDatePickerProps } from '../../date_picker'; import { EuiDatePopoverContentProps } from './date_popover_content'; @@ -36,198 +42,161 @@ export interface EuiAbsoluteTabProps { value: string; onChange: EuiDatePopoverContentProps['onChange']; roundUp: boolean; - position: 'start' | 'end'; labelPrefix: string; utcOffset?: number; } -interface EuiAbsoluteTabState { - hasUnparsedText: boolean; - isTextInvalid: boolean; - textInputValue: string; - valueAsMoment: Moment | null; -} - -export class EuiAbsoluteTab extends Component< - EuiAbsoluteTabProps, - EuiAbsoluteTabState -> { - state: EuiAbsoluteTabState; - isParsing = false; // Store outside of state as a ref for faster/unbatched updates - - constructor(props: EuiAbsoluteTabProps) { - super(props); - - const parsedValue = dateMath.parse(props.value, { roundUp: props.roundUp }); - const valueAsMoment = - parsedValue && parsedValue.isValid() ? parsedValue : moment(); - - const textInputValue = valueAsMoment - .locale(this.props.locale || 'en') - .format(this.props.dateFormat); - - this.state = { - hasUnparsedText: false, - isTextInvalid: false, - textInputValue, - valueAsMoment, - }; - } - - handleChange: EuiDatePickerProps['onChange'] = (date) => { - const { onChange } = this.props; - if (date === null) { - return; - } - onChange(date.toISOString()); - - const valueAsMoment = moment(date); - this.setState({ - valueAsMoment, - textInputValue: valueAsMoment.format(this.props.dateFormat), - hasUnparsedText: false, - isTextInvalid: false, - }); - }; - - handleTextChange = (event: ChangeEvent) => { - if (this.isParsing) return; - - this.setState({ - textInputValue: event.target.value, - hasUnparsedText: true, - isTextInvalid: false, - }); - }; - - parseUserDateInput = (textInputValue: string) => { - this.isParsing = true; - // Wait a tick for state to finish updating (whatever gets returned), - // and then allow `onChange` user input to continue setting state - requestAnimationFrame(() => { - this.isParsing = false; - }); - - const invalidDateState = { - textInputValue, - isTextInvalid: true, - valueAsMoment: null, - }; - if (!textInputValue) { - return this.setState(invalidDateState); +export const EuiAbsoluteTab: FunctionComponent = ({ + value, + onChange, + dateFormat, + timeFormat, + locale, + roundUp, + utcOffset, + labelPrefix, +}) => { + const [valueAsMoment, setValueAsMoment] = useState(() => { + const parsedValue = dateMath.parse(value, { roundUp }); + return parsedValue && parsedValue.isValid() ? parsedValue : moment(); + }); + const handleChange: EuiDatePickerProps['onChange'] = useCallback( + (date: Moment | null) => { + if (date === null) return; + + const valueAsMoment = moment(date); + setValueAsMoment(valueAsMoment); + setTextInputValue(valueAsMoment.format(dateFormat)); + setHasUnparsedText(false); + setIsTextInvalid(false); + }, + [dateFormat] + ); + + const [textInputValue, setTextInputValue] = useState(() => + valueAsMoment!.locale(locale || 'en').format(dateFormat) + ); + const handleTextChange = useCallback( + (event: ChangeEvent) => { + setTextInputValue(event.target.value); + setHasUnparsedText(true); + setIsTextInvalid(false); + }, + [] + ); + + const submitButtonLabel = useEuiI18n( + 'euiAbsoluteTab.dateFormatButtonLabel', + 'Parse date' + ); + const dateFormatError = useEuiI18n( + 'euiAbsoluteTab.dateFormatError', + 'Allowed formats: {dateFormat}, ISO 8601, RFC 2822, or Unix timestamp.', + { dateFormat: {dateFormat} } + ); + const [hasUnparsedText, setHasUnparsedText] = useState(false); + const [isReadyToParse, setIsReadyToParse] = useState(false); + const [isTextInvalid, setIsTextInvalid] = useState(false); + + useEffect(() => { + if (isReadyToParse) { + if (!textInputValue) { + setIsTextInvalid(true); + setValueAsMoment(null); + return; + } + + // Attempt to parse with passed `dateFormat` and `locale` + let valueAsMoment = moment( + textInputValue, + dateFormat, + typeof locale === 'string' ? locale : 'en', // Narrow the union type to string + true + ); + let dateIsValid = valueAsMoment.isValid(); + + // If not valid, try a few other other standardized formats + if (!dateIsValid) { + valueAsMoment = moment(textInputValue, ALLOWED_USER_DATE_FORMATS, true); + dateIsValid = valueAsMoment.isValid(); + } + + if (dateIsValid) { + setTextInputValue(valueAsMoment.format(dateFormat)); + setValueAsMoment(valueAsMoment); + setHasUnparsedText(false); + setIsTextInvalid(false); + } else { + setIsTextInvalid(true); + setValueAsMoment(null); + } + setIsReadyToParse(false); } + }, [isReadyToParse, textInputValue, dateFormat, locale]); - const { onChange, dateFormat, locale } = this.props; - - // Attempt to parse with passed `dateFormat` and `locale` - let valueAsMoment = moment( - textInputValue, - dateFormat, - typeof locale === 'string' ? locale : 'en', // Narrow the union type to string - true - ); - let dateIsValid = valueAsMoment.isValid(); - - // If not valid, try a few other other standardized formats - if (!dateIsValid) { - valueAsMoment = moment(textInputValue, ALLOWED_USER_DATE_FORMATS, true); - dateIsValid = valueAsMoment.isValid(); - } - - if (dateIsValid) { + useUpdateEffect(() => { + if (valueAsMoment) { onChange(valueAsMoment.toISOString()); - this.setState({ - textInputValue: valueAsMoment.format(this.props.dateFormat), - valueAsMoment: valueAsMoment, - hasUnparsedText: false, - isTextInvalid: false, - }); - } else { - this.setState(invalidDateState); } - }; - - render() { - const { dateFormat, timeFormat, locale, utcOffset, labelPrefix } = - this.props; - const { valueAsMoment, isTextInvalid, hasUnparsedText, textInputValue } = - this.state; - - return ( - <> - - {dateFormat} }} + }, [valueAsMoment]); + + return ( + <> + + { + e.preventDefault(); // Prevents a page refresh/reload + setIsReadyToParse(true); + }} + className="euiSuperDatePicker__absoluteDateForm" + gutterSize="s" + responsive={false} + > + - {([dateFormatButtonLabel, dateFormatError]: string[]) => ( - { - e.preventDefault(); // Prevents a page refresh/reload - this.parseUserDateInput(textInputValue); - }} - className="euiSuperDatePicker__absoluteDateForm" - gutterSize="s" - responsive={false} - > - - { - this.parseUserDateInput( - event.clipboardData.getData('text') - ); - }} - data-test-subj="superDatePickerAbsoluteDateInput" - prepend={{labelPrefix}} - /> - - {hasUnparsedText && ( - - )} - - )} - - - ); - } -} + { + setTextInputValue(event.clipboardData.getData('text')); + setIsReadyToParse(true); + }} + data-test-subj="superDatePickerAbsoluteDateInput" + prepend={{labelPrefix}} + /> + + {hasUnparsedText && ( + + )} + + + ); +}; diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx index a7b645076dc..cf62ac294ae 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/date_popover_content.tsx @@ -99,7 +99,6 @@ export const EuiDatePopoverContent: FunctionComponent< value={value} onChange={onChange} roundUp={roundUp} - position={position} labelPrefix={labelPrefix} utcOffset={utcOffset} /> From 25e57201401627ce3ffd848d96c6ab24d8883b27 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Tue, 23 Jul 2024 13:30:21 -0700 Subject: [PATCH 5/9] Convert EuiAbsoluteTab form to Emotion --- .../date_popover/_absolute_tab.scss | 19 -------- .../date_popover/absolute_tab.styles.ts | 44 +++++++++++++++++++ .../date_popover/absolute_tab.tsx | 11 +++-- 3 files changed, 51 insertions(+), 23 deletions(-) create mode 100644 packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.styles.ts diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/_absolute_tab.scss b/packages/eui/src/components/date_picker/super_date_picker/date_popover/_absolute_tab.scss index 9e86650d7e2..e69de29bb2d 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/_absolute_tab.scss +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/_absolute_tab.scss @@ -1,19 +0,0 @@ -.euiSuperDatePicker__absoluteDateForm { - padding: 0 $euiSizeS $euiSizeS; -} - -.euiSuperDatePicker__absoluteDateFormSubmit { - flex-shrink: 0; -} - -.euiSuperDatePicker__absoluteDateFormRow { - flex-grow: 1; - - // CSS hack to make the help/error text extend to the submit button. - // We can't actually put the submit button within an EuiFormRow due to - // cloneElement limitations (https://github.com/elastic/eui/issues/2493#issuecomment-561278494) - // TODO: Remove this and clean up DOM rendering once we can - .euiFormRow__text { - margin-inline-end: -1 * ($euiSizeXL + $euiSizeS); // XL - size of the button, S - size of the flex gap - } -} diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.styles.ts b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.styles.ts new file mode 100644 index 00000000000..a0530f4c23e --- /dev/null +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.styles.ts @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { css } from '@emotion/react'; + +import { UseEuiTheme } from '../../../../services'; +import { logicalCSS, mathWithUnits } from '../../../../global_styling'; + +export const euiAbsoluteTabDateFormStyles = (euiThemeContext: UseEuiTheme) => { + const { euiTheme } = euiThemeContext; + + return { + euiAbsoluteTabDateForm: css` + ${logicalCSS('padding-horizontal', euiTheme.size.s)} + ${logicalCSS('padding-bottom', euiTheme.size.s)} + `, + euiAbsoluteTabDateForm__submit: css` + flex-shrink: 0; + `, + euiAbsoluteTabDateForm__row: css` + flex-grow: 1; + + /* CSS hack to make the help/error text extend to the submit button. + * We can't actually put the submit button within an EuiFormRow due to + * cloneElement limitations (https://github.com/elastic/eui/issues/2493#issuecomment-561278494) + * TODO: Remove this and clean up DOM rendering once we can + */ + .euiFormRow__text { + ${logicalCSS( + 'margin-right', + mathWithUnits( + [euiTheme.size.xl, euiTheme.size.s], + (submitButtonSize, gapSize) => -1 * (submitButtonSize + gapSize) + ) + )} + } + `, + }; +}; diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx index 4dd47866d9a..f0e288e987d 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx @@ -17,7 +17,7 @@ import React, { import moment, { Moment, LocaleSpecifier } from 'moment'; import dateMath from '@elastic/datemath'; -import { useUpdateEffect } from '../../../../services'; +import { useUpdateEffect, useEuiMemoizedStyles } from '../../../../services'; import { useEuiI18n } from '../../../i18n'; import { EuiFormRow, EuiFieldText, EuiFormLabel } from '../../../form'; import { EuiFlexGroup } from '../../../flex'; @@ -26,6 +26,7 @@ import { EuiCode } from '../../../code'; import { EuiDatePicker, EuiDatePickerProps } from '../../date_picker'; import { EuiDatePopoverContentProps } from './date_popover_content'; +import { euiAbsoluteTabDateFormStyles } from './absolute_tab.styles'; // Allow users to paste in and have the datepicker parse multiple common date formats, // in addition to the configured displayed `dateFormat` prop @@ -56,6 +57,8 @@ export const EuiAbsoluteTab: FunctionComponent = ({ utcOffset, labelPrefix, }) => { + const styles = useEuiMemoizedStyles(euiAbsoluteTabDateFormStyles); + const [valueAsMoment, setValueAsMoment] = useState(() => { const parsedValue = dateMath.parse(value, { roundUp }); return parsedValue && parsedValue.isValid() ? parsedValue : moment(); @@ -159,12 +162,12 @@ export const EuiAbsoluteTab: FunctionComponent = ({ e.preventDefault(); // Prevents a page refresh/reload setIsReadyToParse(true); }} - className="euiSuperDatePicker__absoluteDateForm" + css={styles.euiAbsoluteTabDateForm} gutterSize="s" responsive={false} > = ({ {hasUnparsedText && ( Date: Tue, 23 Jul 2024 13:31:25 -0700 Subject: [PATCH 6/9] Delete Sass files --- .../src/components/date_picker/super_date_picker/_index.scss | 1 - .../super_date_picker/date_popover/_absolute_tab.scss | 0 .../super_date_picker/date_popover/_date_popover_content.scss | 0 .../date_picker/super_date_picker/date_popover/_index.scss | 2 -- 4 files changed, 3 deletions(-) delete mode 100644 packages/eui/src/components/date_picker/super_date_picker/date_popover/_absolute_tab.scss delete mode 100644 packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss delete mode 100644 packages/eui/src/components/date_picker/super_date_picker/date_popover/_index.scss diff --git a/packages/eui/src/components/date_picker/super_date_picker/_index.scss b/packages/eui/src/components/date_picker/super_date_picker/_index.scss index 6ba70ece893..06a7ba3c66d 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/_index.scss +++ b/packages/eui/src/components/date_picker/super_date_picker/_index.scss @@ -1,2 +1 @@ -@import 'date_popover/index'; @import 'quick_select_popover/index'; diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/_absolute_tab.scss b/packages/eui/src/components/date_picker/super_date_picker/date_popover/_absolute_tab.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss b/packages/eui/src/components/date_picker/super_date_picker/date_popover/_date_popover_content.scss deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/_index.scss b/packages/eui/src/components/date_picker/super_date_picker/date_popover/_index.scss deleted file mode 100644 index b6086ab8484..00000000000 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/_index.scss +++ /dev/null @@ -1,2 +0,0 @@ -@import 'absolute_tab'; -@import 'date_popover_content'; From 8add8cc1db3c54a4e2f1e6112cccc73c2adebb3e Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Tue, 23 Jul 2024 14:02:04 -0700 Subject: [PATCH 7/9] changelog --- packages/eui/changelogs/upcoming/7908.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 packages/eui/changelogs/upcoming/7908.md diff --git a/packages/eui/changelogs/upcoming/7908.md b/packages/eui/changelogs/upcoming/7908.md new file mode 100644 index 00000000000..1582043ba5a --- /dev/null +++ b/packages/eui/changelogs/upcoming/7908.md @@ -0,0 +1,3 @@ +**CSS-in-JS conversions** + +- Converted `EuiSuperDatePicker`'s date popover content to Emotion From 8a2ab6bbc1fc6663bdfdacaff05dee782f87e473 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Wed, 24 Jul 2024 09:50:11 -0700 Subject: [PATCH 8/9] Fix pasting duplicate text :facepalm: It worked in Firefox but not in webkit, derp --- .../super_date_picker/date_popover/absolute_tab.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx index f0e288e987d..8fe9d378b14 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx @@ -81,11 +81,13 @@ export const EuiAbsoluteTab: FunctionComponent = ({ ); const handleTextChange = useCallback( (event: ChangeEvent) => { + if (isReadyToParse) return; // Text paste event, don't continue + setTextInputValue(event.target.value); setHasUnparsedText(true); setIsTextInvalid(false); }, - [] + [isReadyToParse] ); const submitButtonLabel = useEuiI18n( From aa45cbc32d57b032c8f8f81d5fa6c2adc80a7c70 Mon Sep 17 00:00:00 2001 From: Cee Chen Date: Wed, 24 Jul 2024 09:50:38 -0700 Subject: [PATCH 9/9] Reorder absolute tab vars so js doesn't complain about the hoisted variable + fix comment typo --- .../date_popover/absolute_tab.tsx | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx index 8fe9d378b14..76b4b2717e2 100644 --- a/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx +++ b/packages/eui/src/components/date_picker/super_date_picker/date_popover/absolute_tab.tsx @@ -76,9 +76,22 @@ export const EuiAbsoluteTab: FunctionComponent = ({ [dateFormat] ); + const submitButtonLabel = useEuiI18n( + 'euiAbsoluteTab.dateFormatButtonLabel', + 'Parse date' + ); + const dateFormatError = useEuiI18n( + 'euiAbsoluteTab.dateFormatError', + 'Allowed formats: {dateFormat}, ISO 8601, RFC 2822, or Unix timestamp.', + { dateFormat: {dateFormat} } + ); const [textInputValue, setTextInputValue] = useState(() => valueAsMoment!.locale(locale || 'en').format(dateFormat) ); + const [hasUnparsedText, setHasUnparsedText] = useState(false); + const [isReadyToParse, setIsReadyToParse] = useState(false); + const [isTextInvalid, setIsTextInvalid] = useState(false); + const handleTextChange = useCallback( (event: ChangeEvent) => { if (isReadyToParse) return; // Text paste event, don't continue @@ -90,19 +103,6 @@ export const EuiAbsoluteTab: FunctionComponent = ({ [isReadyToParse] ); - const submitButtonLabel = useEuiI18n( - 'euiAbsoluteTab.dateFormatButtonLabel', - 'Parse date' - ); - const dateFormatError = useEuiI18n( - 'euiAbsoluteTab.dateFormatError', - 'Allowed formats: {dateFormat}, ISO 8601, RFC 2822, or Unix timestamp.', - { dateFormat: {dateFormat} } - ); - const [hasUnparsedText, setHasUnparsedText] = useState(false); - const [isReadyToParse, setIsReadyToParse] = useState(false); - const [isTextInvalid, setIsTextInvalid] = useState(false); - useEffect(() => { if (isReadyToParse) { if (!textInputValue) { @@ -120,7 +120,7 @@ export const EuiAbsoluteTab: FunctionComponent = ({ ); let dateIsValid = valueAsMoment.isValid(); - // If not valid, try a few other other standardized formats + // If not valid, try a few other standardized formats if (!dateIsValid) { valueAsMoment = moment(textInputValue, ALLOWED_USER_DATE_FORMATS, true); dateIsValid = valueAsMoment.isValid();