diff --git a/src-docs/src/views/date_picker/range.tsx b/src-docs/src/views/date_picker/range.tsx index 65a08a8a655..a9ecf02cbc6 100644 --- a/src-docs/src/views/date_picker/range.tsx +++ b/src-docs/src/views/date_picker/range.tsx @@ -10,7 +10,7 @@ export default () => { return ( /* DisplayToggles wrapper for Docs only */ - + endDate} startDateControl={ diff --git a/src-docs/src/views/form_controls/form_control_layout_range.tsx b/src-docs/src/views/form_controls/form_control_layout_range.tsx index 9425c27c43e..ec15ef02c19 100644 --- a/src-docs/src/views/form_controls/form_control_layout_range.tsx +++ b/src-docs/src/views/form_controls/form_control_layout_range.tsx @@ -3,6 +3,7 @@ import React from 'react'; import { EuiFormControlLayoutDelimited, EuiFormLabel, + EuiFieldNumber, EuiIcon, } from '../../../../src/components'; @@ -218,5 +219,49 @@ export default () => ( /> } /> + + Disabled} + startControl={ + + } + endControl={ + + } + /> + + Invalid} + startControl={ + + } + endControl={ + + } + /> ); diff --git a/src-docs/src/views/super_date_picker/playground.js b/src-docs/src/views/super_date_picker/playground.js index fb35eb69d49..b593cdc7924 100644 --- a/src-docs/src/views/super_date_picker/playground.js +++ b/src-docs/src/views/super_date_picker/playground.js @@ -18,6 +18,20 @@ export const superDatePickerConfig = () => { propsToUse.onTimeChange = simulateFunction(propsToUse.onTimeChange, true); propsToUse.onRefreshChange = simulateFunction(propsToUse.onRefreshChange); + propsToUse.isPaused = { + ...propsToUse.isPaused, + type: PropTypes.Boolean, + defaultValue: true, + value: true, + }; + + propsToUse.showUpdateButton = { + ...propsToUse.showUpdateButton, + type: PropTypes.Boolean, + defaultValue: true, + value: true, + }; + propsToUse.locale = { ...propsToUse.locale, type: PropTypes.String, diff --git a/src/components/color_picker/__snapshots__/color_picker.test.tsx.snap b/src/components/color_picker/__snapshots__/color_picker.test.tsx.snap index 537f051646d..b9d48084a46 100644 --- a/src/components/color_picker/__snapshots__/color_picker.test.tsx.snap +++ b/src/components/color_picker/__snapshots__/color_picker.test.tsx.snap @@ -13,16 +13,8 @@ exports[`renders EuiColorPicker 1`] = `
-
+
-
+
diff --git a/src/components/date_picker/__snapshots__/date_picker_range.test.tsx.snap b/src/components/date_picker/__snapshots__/date_picker_range.test.tsx.snap index 8fbf882c855..9ba7ffb0fd8 100644 --- a/src/components/date_picker/__snapshots__/date_picker_range.test.tsx.snap +++ b/src/components/date_picker/__snapshots__/date_picker_range.test.tsx.snap @@ -2,464 +2,361 @@ exports[`EuiDatePickerRange disabled is rendered 1`] = `
-
+ + +
+
-
-
- -
-
-
-
- - +
-
- - - -
+ + to + +
+
-
-
- -
-
+
-
+
`; exports[`EuiDatePickerRange is rendered 1`] = `
-
+ + +
+
-
-
- -
-
-
-
- - +
-
- - - -
+ + to + +
+
-
-
- -
-
+
-
+
`; exports[`EuiDatePickerRange isInvalid is rendered 1`] = `
-
+ + +
+
-
-
- -
-
-
-
- - -
-
-
-
- - - -
+ + to + +
+
-
-
- -
-
-
-
-
-
+
+ +
+
`; exports[`EuiDatePickerRange readOnly is rendered 1`] = `
-
+ + +
+
-
-
- -
-
-
-
- - +
-
- - - -
+ + to + +
+
-
-
- -
-
+
-
+
`; exports[`EuiDatePickerRange uses individual EuiDatePicker props 1`] = `
-
+ + +
+
-
-
- -
-
-
-
- - +
-
- - - -
+ + to + +
+
-
-
- -
-
+
-
+
`; diff --git a/src/components/date_picker/_date_picker_range.scss b/src/components/date_picker/_date_picker_range.scss index a70bb73f80b..5ef1251be83 100644 --- a/src/components/date_picker/_date_picker_range.scss +++ b/src/components/date_picker/_date_picker_range.scss @@ -1,65 +1,5 @@ -/** - * 1. Account for inner box-shadow style border - */ - .euiDatePickerRange { - @include euiFormControlSize(auto, $includeAlternates: true); - // Match just the regular drop shadow of inputs - @include euiFormControlDefaultShadow; - display: flex; - align-items: center; - padding: 1px; /* 1 */ - - // Allow any child to fill entire range container - > * { - flex-grow: 1; - } - .euiFieldText.euiDatePicker { height: $euiFormControlLayoutGroupInputHeight; - // including all states - box-shadow: none !important; // stylelint-disable-line declaration-no-important - text-align: center; - } - - .euiDatePickerRange__start { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } - - .euiDatePickerRange__end { - border-top-left-radius: 0; - border-bottom-left-radius: 0; - } - - .react-datepicker-popper .euiFieldText.euiDatePicker { - // set any subsequent children in popper back to left align - text-align: left; - } - - // Necessary for EuiSuperDatePicker - &--inGroup { - box-shadow: none; - padding: 0; } } - -.euiDatePickerRange--isDisabled { - background: $euiFormBackgroundDisabledColor; -} - -.euiDatePickerRange--readOnly { - background: $euiFormBackgroundReadOnlyColor; -} - -.euiDatePickerRange__delimeter { - align-self: stretch; - height: auto; - flex-grow: 0; - display: flex; - align-items: center; -} - -.euiDatePickerRange--isInvalid:not(.euiDatePickerRange--isDisabled):not(.euiDatePickerRange--readOnly) .euiDatePickerRange__delimeter { - @include euiFormControlInvalidStyle; -} diff --git a/src/components/date_picker/date_picker.tsx b/src/components/date_picker/date_picker.tsx index 05e4db05844..a82cb76af81 100644 --- a/src/components/date_picker/date_picker.tsx +++ b/src/components/date_picker/date_picker.tsx @@ -126,6 +126,12 @@ interface EuiExtendedDatePickerProps * **Use [EuiPopover](/#/layout/popover) values**: 'upCenter', 'upLeft', 'upRight', downCenter', 'downLeft', 'downRight', 'leftCenter', 'leftUp', 'leftDown', 'rightCenter', 'rightUp', 'rightDown'. */ popoverPlacement?: PopoverAnchorPosition; + + /** + * Completely removes form control layout wrapper and ignores + * iconType. Best used inside EuiFormControlLayoutDelimited. + */ + controlOnly?: boolean; } export type EuiDatePickerProps = CommonProps & EuiExtendedDatePickerProps; @@ -134,6 +140,7 @@ export const EuiDatePicker: FunctionComponent = ({ adjustDateOnChange = true, calendarClassName, className, + controlOnly, customInput, dateFormat = euiDatePickerDefaultDateFormat, dayClassName, @@ -173,10 +180,12 @@ export const EuiDatePicker: FunctionComponent = ({ 'euiDatePicker--inline': inline, }); - const numIconsClass = getFormControlClassNameForIconCount({ - isInvalid, - isLoading, - }); + const numIconsClass = controlOnly + ? false + : getFormControlClassNameForIconCount({ + isInvalid, + isLoading, + }); const datePickerClasses = classNames( 'euiDatePicker', @@ -217,6 +226,52 @@ export const EuiDatePicker: FunctionComponent = ({ useEuiValidatableControl({ isInvalid, controlEl: inputValidityRef }); const inputRefs = useCombinedRefs([inputRef, setInputValidityRef]); + const control = ( + + {({ locale: contextLocale }) => { + return ( + + ); + }} + + ); + + if (controlOnly) return control; + return ( = ({ isLoading={isLoading} isInvalid={isInvalid} > - - {({ locale: contextLocale }) => { - return ( - - ); - }} - + {control} ); diff --git a/src/components/date_picker/date_picker_range.tsx b/src/components/date_picker/date_picker_range.tsx index 7c48bf777b8..1b5cc77c05a 100644 --- a/src/components/date_picker/date_picker_range.tsx +++ b/src/components/date_picker/date_picker_range.tsx @@ -9,7 +9,6 @@ import React, { FocusEvent, FocusEventHandler, - Fragment, FunctionComponent, ReactNode, cloneElement, @@ -17,66 +16,59 @@ import React, { } from 'react'; import classNames from 'classnames'; -import { IconType, EuiIcon } from '../icon'; +import { + EuiFormControlLayoutDelimited, + EuiFormControlLayoutDelimitedProps, +} from '../form'; +import { IconType } from '../icon'; import { CommonProps } from '../common'; import { EuiDatePickerProps } from './date_picker'; -export type EuiDatePickerRangeProps = CommonProps & { - /** - * Including any children will replace all innards with the provided children - */ - children?: ReactNode; - - /** - * The end date `EuiDatePicker` element - */ - endDateControl: ReactElement; - - /** - * The start date `EuiDatePicker` element - */ - startDateControl: ReactElement; - - /** - * Pass either an icon type or set to `false` to remove icon entirely - */ - iconType?: boolean | IconType; - - /** - * Won't apply any additional props to start and end date components - */ - isCustom?: boolean; - - /** - * Will color the range delimiter the `danger` color and pass through to each control - */ - isInvalid?: boolean; - - /** - * Passes through to each control - */ - disabled?: boolean; - - /** - * Passes through to each control - */ - readOnly?: boolean; - - /** - * Passes through to each control - */ - fullWidth?: boolean; - - /** - * Triggered whenever the start or end controls are blurred - */ - onBlur?: FocusEventHandler; - - /** - * Triggered whenever the start or end controls are focused - */ - onFocus?: FocusEventHandler; -}; +export type EuiDatePickerRangeProps = CommonProps & + Pick< + EuiFormControlLayoutDelimitedProps, + 'isInvalid' | 'readOnly' | 'fullWidth' | 'prepend' | 'append' + > & { + /** + * Including any children will replace all innards with the provided children + */ + children?: ReactNode; + + /** + * The end date `EuiDatePicker` element + */ + endDateControl: ReactElement; + + /** + * The start date `EuiDatePicker` element + */ + startDateControl: ReactElement; + + /** + * Pass either an icon type or set to `false` to remove icon entirely + */ + iconType?: boolean | IconType; + + /** + * Won't apply any additional props to start and end date components + */ + isCustom?: boolean; + + /** + * Passes through to each control + */ + disabled?: boolean; + + /** + * Triggered whenever the start or end controls are blurred + */ + onBlur?: FocusEventHandler; + + /** + * Triggered whenever the start or end controls are focused + */ + onFocus?: FocusEventHandler; + }; export const EuiDatePickerRange: FunctionComponent = ({ children, @@ -91,6 +83,8 @@ export const EuiDatePickerRange: FunctionComponent = ({ disabled, onFocus, onBlur, + append, + prepend, ...rest }) => { const classes = classNames( @@ -111,8 +105,8 @@ export const EuiDatePickerRange: FunctionComponent = ({ startControl = cloneElement( startDateControl as ReactElement, { - iconType: typeof iconType === 'boolean' ? undefined : iconType, - showIcon: !!iconType, + controlOnly: true, + showIcon: false, fullWidth: fullWidth, readOnly: readOnly, disabled: disabled || startDateControl.props.disabled, @@ -135,6 +129,7 @@ export const EuiDatePickerRange: FunctionComponent = ({ endControl = cloneElement( endDateControl as ReactElement, { + controlOnly: true, showIcon: false, fullWidth: fullWidth, readOnly: readOnly, @@ -157,23 +152,19 @@ export const EuiDatePickerRange: FunctionComponent = ({ ); } - const delimiter = ( - - - - ); - return ( -
- {children ? ( - children - ) : ( - - {startControl} - {delimiter} - {endControl} - - )} -
+ ); }; diff --git a/src/components/date_picker/super_date_picker/__snapshots__/super_date_picker.test.tsx.snap b/src/components/date_picker/super_date_picker/__snapshots__/super_date_picker.test.tsx.snap index d753a10e5c1..c8c4d594cba 100644 --- a/src/components/date_picker/super_date_picker/__snapshots__/super_date_picker.test.tsx.snap +++ b/src/components/date_picker/super_date_picker/__snapshots__/super_date_picker.test.tsx.snap @@ -10,7 +10,8 @@ exports[`EuiSuperDatePicker is rendered 1`] = ` } > - } - iconType={false} - isCustom={true} - startDateControl={
} + - + + } > - } - iconType={false} - isCustom={true} - startDateControl={
} + - + + } > - } - iconType={false} - isCustom={true} - startDateControl={
} + - - - - - - - -`; - -exports[`EuiSuperDatePicker props isAutoRefreshOnly is rendered 1`] = ` - - - + + + + + + + + +`; + +exports[`EuiSuperDatePicker props isAutoRefreshOnly is rendered 1`] = ` + + + `; @@ -1021,7 +1000,9 @@ exports[`EuiSuperDatePicker props isQuickSelectOnly is rendered 1`] = ` } > - } - iconType={false} - isCustom={true} - startDateControl={
} + - + + @@ -1583,7 +1557,8 @@ exports[`EuiSuperDatePicker props showUpdateButton can be iconOnly 1`] = ` } > - } - iconType={false} - isCustom={true} - startDateControl={
} + - + + } > - } - iconType={false} - isCustom={true} - startDateControl={
} + - + + } > - } - iconType={false} - isCustom={true} - startDateControl={
} + - + + .euiFormControlLayout__childrenWrapper { - flex: 1 1 100%; + display: flex; + align-items: center; overflow: hidden; + background-color: $euiFormBackgroundColor; - > .euiDatePickerRange { - max-width: none; - width: auto; + &:last-child { border-radius: 0 $euiFormControlBorderRadius $euiFormControlBorderRadius 0; } - &:not(:last-child) > .euiDatePickerRange { - &, - .euiDatePopoverButton--end, - .euiSuperDatePicker__prettyFormat { - border-top-right-radius: 0; - border-bottom-right-radius: 0; - } + & > .euiFormControlLayoutDelimited__input { + flex-grow: 1; } } } @@ -75,3 +70,20 @@ cursor: not-allowed; } } + +/** +* Make the arrow delimiter match the colors of `.euiDatePopoverButton-needsUpdating` +*/ +.euiSuperDatePicker--needsUpdating.euiFormControlLayoutDelimited { // Extra specificity needed to override default delimited styles + .euiFormControlLayout__childrenWrapper { + background-color: $euiSuperDatePickerNeedsUpdatingBackgroundColor; + } + + .euiFormControlLayoutDelimited__delimiter { + color: $euiSuperDatePickerNeedsUpdatingTextColor; + } +} + +.euiSuperDatePicker .euiFormControlLayout__childrenWrapper { + transition: background $euiAnimSpeedFast ease-in; // Match @mixin euiSuperDatePickerText / .euiDatePopoverButton +} diff --git a/src/components/date_picker/super_date_picker/_variables.scss b/src/components/date_picker/super_date_picker/_variables.scss index 83f209098c2..d881875a56e 100644 --- a/src/components/date_picker/super_date_picker/_variables.scss +++ b/src/components/date_picker/super_date_picker/_variables.scss @@ -1,2 +1,5 @@ $euiSuperDatePickerWidth: $euiSize * 30; $euiSuperDatePickerButtonWidth: $euiButtonMinWidth + ($euiSizeXS * 1.5); + +$euiSuperDatePickerNeedsUpdatingBackgroundColor: tintOrShade($euiColorSuccess, 90%, 70%); +$euiSuperDatePickerNeedsUpdatingTextColor: makeHighContrastColor($euiColorSuccess, $euiSuperDatePickerNeedsUpdatingBackgroundColor); diff --git a/src/components/date_picker/super_date_picker/date_popover/_date_popover_button.scss b/src/components/date_picker/super_date_picker/date_popover/_date_popover_button.scss index 5042743941c..6d782d2ae18 100644 --- a/src/components/date_picker/super_date_picker/date_popover/_date_popover_button.scss +++ b/src/components/date_picker/super_date_picker/date_popover/_date_popover_button.scss @@ -13,11 +13,8 @@ } &-needsUpdating { - $backgroundColor: tintOrShade($euiColorSuccess, 90%, 70%); - $textColor: makeHighContrastColor($euiColorSuccess, $backgroundColor); - - background-color: $backgroundColor; - color: $textColor; + background-color: $euiSuperDatePickerNeedsUpdatingBackgroundColor; + color: $euiSuperDatePickerNeedsUpdatingTextColor; &:focus, &.euiDatePopoverButton-isSelected { @@ -38,15 +35,16 @@ &:disabled { background-color: $euiFormBackgroundDisabledColor; + background-image: none; color: $euiColorDarkShade; cursor: default; } } .euiDatePopoverButton--start { - text-align: right; + text-align: center; } .euiDatePopoverButton--end { - text-align: left; + text-align: center; } diff --git a/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.tsx.snap b/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.tsx.snap index 09b2b778b60..ee65360e55b 100644 --- a/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.tsx.snap +++ b/src/components/date_picker/super_date_picker/quick_select_popover/__snapshots__/quick_select_popover.test.tsx.snap @@ -108,7 +108,7 @@ exports[`EuiQuickSelectPanels customQuickSelectPanels should render custom panel
{ + const { + start, + end, + customQuickSelectPanels, + customQuickSelectRender, + commonlyUsedRanges, + timeOptions, + dateFormat, + onRefreshChange, + recentlyUsedRanges, + refreshInterval, + isPaused, + isDisabled, + } = this.props; + + return ( + + ); + }; + renderDatePickerRange = () => { const { end, @@ -407,30 +444,61 @@ export class EuiSuperDatePickerInternal extends Component< start, } = this.state; const { + isQuickSelectOnly, + showUpdateButton, commonlyUsedRanges, timeOptions, dateFormat, + refreshInterval, + isPaused, isDisabled, + isLoading, locale, timeFormat, utcOffset, compressed, onFocus, + className, + 'data-test-subj': dataTestSubj, } = this.props; + const autoRefreshAppend: EuiFormControlLayoutProps['append'] = !isPaused ? ( + + ) : undefined; + + const formControlLayoutProps = { + className: classNames('euiSuperDatePicker', className), + compressed, + isInvalid, + isLoading: isLoading && !showUpdateButton, + disabled: isDisabled, + prepend: this.renderQuickSelect(), + append: autoRefreshAppend, + 'data-test-subj': dataTestSubj, + }; + + if (isQuickSelectOnly) { + return ( + + ); + } + if ( showPrettyDuration && !isStartDatePopoverOpen && !isEndDatePopoverOpen ) { return ( - } - endDateControl={
} - > + - + ); } @@ -455,11 +523,13 @@ export class EuiSuperDatePickerInternal extends Component< {({ locale: contextLocale }) => ( - ) : undefined; - - const quickSelect = ( - - ); - const flexWrapperClasses = classNames('euiSuperDatePicker__flexWrapper', { 'euiSuperDatePicker__flexWrapper--noUpdateButton': !showUpdateButton, 'euiSuperDatePicker__flexWrapper--isAutoRefreshOnly': isAutoRefreshOnly, @@ -634,18 +665,7 @@ export class EuiSuperDatePickerInternal extends Component< ) : ( <> - - - {!isQuickSelectOnly && this.renderDatePickerRange()} - - + {this.renderDatePickerRange()} {this.renderUpdateButton()} )} diff --git a/src/components/form/field_password/__snapshots__/field_password.test.tsx.snap b/src/components/form/field_password/__snapshots__/field_password.test.tsx.snap index 7ce07f9abfe..57cd8caf400 100644 --- a/src/components/form/field_password/__snapshots__/field_password.test.tsx.snap +++ b/src/components/form/field_password/__snapshots__/field_password.test.tsx.snap @@ -7,20 +7,8 @@ exports[`EuiFieldPassword is rendered 1`] = `
- - -
+ + +
`; @@ -43,14 +43,8 @@ exports[`EuiFieldPassword props compressed is rendered 1`] = `
- - -
+ + +
`; @@ -73,14 +73,8 @@ exports[`EuiFieldPassword props dual dual type also renders append 1`] = `
- - -
+ + +
`; @@ -200,16 +200,8 @@ exports[`EuiFieldPassword props isInvalid is rendered 1`] = `
- - -
+ + +
- - -
+ + +
- - -
+ + +
`; @@ -392,14 +392,8 @@ exports[`EuiFieldPassword props type text is rendered 1`] = `
- - -
+ + +
`; diff --git a/src/components/form/form_control_layout/__snapshots__/form_control_layout.test.tsx.snap b/src/components/form/form_control_layout/__snapshots__/form_control_layout.test.tsx.snap index 3207fcb040a..226a92b3285 100644 --- a/src/components/form/form_control_layout/__snapshots__/form_control_layout.test.tsx.snap +++ b/src/components/form/form_control_layout/__snapshots__/form_control_layout.test.tsx.snap @@ -22,7 +22,7 @@ exports[`EuiFormControlLayout props clear onClick is rendered 1`] = ` class="euiFormControlLayout__childrenWrapper" >
`; diff --git a/src/components/form/validatable_control/validatable_control.test.tsx b/src/components/form/validatable_control/validatable_control.test.tsx index 0c1972d461c..f5057a8d98c 100644 --- a/src/components/form/validatable_control/validatable_control.test.tsx +++ b/src/components/form/validatable_control/validatable_control.test.tsx @@ -7,7 +7,7 @@ */ import React from 'react'; -import { render, mount } from 'enzyme'; +import { render } from '@testing-library/react'; import { renderHook } from '@testing-library/react-hooks'; import { @@ -17,23 +17,33 @@ import { describe('EuiValidatableControl', () => { test('is rendered', () => { - const component = render( - + const { container, rerender } = render( + ); + expect(container.firstChild).toMatchInlineSnapshot(` + + `); - expect(component).toMatchSnapshot(); + rerender( + + + + ); + expect(container.firstChild).toMatchInlineSnapshot(''); }); test('aria-invalid allows falling back to prop set on the child input', () => { - const component = render( + const { container } = render( ); - expect(component).toMatchInlineSnapshot(` + expect(container.firstChild).toMatchInlineSnapshot(` @@ -44,7 +54,7 @@ describe('EuiValidatableControl', () => { it('calls a ref function', () => { const ref = jest.fn(); - mount( + render( @@ -59,7 +69,7 @@ describe('EuiValidatableControl', () => { it('sets a ref object\'s "current" property', () => { const ref = React.createRef(); - mount( + render( @@ -78,13 +88,13 @@ describe('EuiValidatableControl', () => { ); - const wrapper = mount(); + const { rerender } = render(); expect(ref).toHaveBeenCalledTimes(1); expect(ref.mock.calls[0][0].getAttribute('id')).toBe('testInput'); // Force re-render - wrapper.setProps({}); + rerender(); expect(ref).toHaveBeenCalledTimes(1); expect(ref.mock.calls[0][0].getAttribute('id')).toBe('testInput'); @@ -99,13 +109,13 @@ describe('EuiValidatableControl', () => { ); - const wrapper = mount(); + const { rerender } = render(); expect(ref).toHaveBeenCalledTimes(1); expect(ref.mock.calls[0][0].getAttribute('id')).toBe('testInput'); // Force re-render - wrapper.setProps({}); + rerender(); expect(ref).toHaveBeenCalledTimes(3); @@ -126,12 +136,12 @@ describe('EuiValidatableControl', () => { ); - const wrapper = mount(); + const { rerender } = render(); expect(ref).toHaveBeenCalledTimes(1); expect(ref.mock.calls[0][0].getAttribute('id')).toBe('testInput'); - wrapper.setProps({ change: true }); + rerender(); expect(ref).toHaveBeenCalledTimes(3); @@ -155,14 +165,14 @@ describe('EuiValidatableControl', () => { ); - const wrapper = mount(); + const { rerender } = render(); expect(ref.current).not.toBeNull(); expect(ref.current!.getAttribute('id')).toBe('testInput'); const prevRef = ref.current; - wrapper.setProps({ change: true }); + rerender(); expect(ref.current).not.toBeNull(); expect(ref.current!.getAttribute('id')).toBe('testInput2'); diff --git a/src/components/form/validatable_control/validatable_control.tsx b/src/components/form/validatable_control/validatable_control.tsx index 1ae8932911f..748060441c3 100644 --- a/src/components/form/validatable_control/validatable_control.tsx +++ b/src/components/form/validatable_control/validatable_control.tsx @@ -13,7 +13,7 @@ import { ReactElement, Ref, FunctionComponent, - useRef, + useState, useEffect, useCallback, } from 'react'; @@ -45,14 +45,17 @@ export interface EuiValidatableControlProps { export const EuiValidatableControl: FunctionComponent< CommonProps & EuiValidatableControlProps > = ({ isInvalid, children }) => { - const control = useRef(null); + // Note that this must be state and not a ref to cause a rerender/set invalid state on initial mount + const [control, setControl] = useState( + null + ); const child = Children.only(children); const childRef = child.ref; const replacedRef = useCallback( (element: HTMLConstraintValidityElement) => { - control.current = element; + setControl(element); // Call the original ref, if any if (typeof childRef === 'function') { @@ -64,7 +67,7 @@ export const EuiValidatableControl: FunctionComponent< [childRef] ); - useSetControlValidity({ controlEl: control.current, isInvalid }); + useSetControlValidity({ controlEl: control, isInvalid }); return cloneElement(child, { ref: replacedRef, diff --git a/src/components/selectable/__snapshots__/selectable.test.tsx.snap b/src/components/selectable/__snapshots__/selectable.test.tsx.snap index b479fc9dfdd..313ac05e8c1 100644 --- a/src/components/selectable/__snapshots__/selectable.test.tsx.snap +++ b/src/components/selectable/__snapshots__/selectable.test.tsx.snap @@ -2586,35 +2586,13 @@ exports[`EuiSelectable screen reader instructions sets custom accessibility inst
- - -
+
+ + + +
-
+
`; @@ -40,6 +40,19 @@ exports[`EuiSelectableSearch renders aria props if a listId is present 1`] = `
+
+ + +
+
+
+`; + +exports[`EuiSelectableSearch renders placeholder, value, name, and other remaining EuiFieldSearch props 1`] = ` +
+
-
-
-`; - -exports[`EuiSelectableSearch renders placeholder, value, name, and other remaining EuiFieldSearch props 1`] = ` -
-
- - -
-

-

+

-

+

-

+

-

+

-

+

-

+ -
+

-

+

-

+

-

+

+

+ + +
- - -
-

-

+ -
+
-
+
-
+

-

+