From 29f1cf2fa75aa991166bd44081c92ef4bd0fadcf Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Thu, 27 Apr 2023 13:41:50 -0700 Subject: [PATCH 1/3] Fix EuiFieldNumber to no longer need a combined ref - switching to onKeyUp instead of onKeyDown allows us to use `e.target` without it being stale, & also removes the need for a rAF --- .../form/field_number/field_number.tsx | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/src/components/form/field_number/field_number.tsx b/src/components/form/field_number/field_number.tsx index 24f9c21a28f..647201c9e39 100644 --- a/src/components/form/field_number/field_number.tsx +++ b/src/components/form/field_number/field_number.tsx @@ -11,13 +11,11 @@ import React, { Ref, FunctionComponent, useState, - useRef, useCallback, } from 'react'; import { CommonProps } from '../../common'; import classNames from 'classnames'; -import { useCombinedRefs } from '../../../services'; import { IconType } from '../../icon'; import { EuiValidatableControl } from '../validatable_control'; @@ -103,7 +101,7 @@ export const EuiFieldNumber: FunctionComponent = ( inputRef, readOnly, controlOnly, - onKeyDown: _onKeyDown, + onKeyUp: _onKeyUp, ...rest } = props; @@ -112,20 +110,17 @@ export const EuiFieldNumber: FunctionComponent = ( // `aria-invalid` as well as display an icon. We also want to *not* set this on // EuiValidatableControl, in order to not override custom validity messages const [isNativelyInvalid, setIsNativelyInvalid] = useState(false); - const validityRef = useRef(null); - const setRefs = useCombinedRefs([validityRef, inputRef]); // Note that we can't use hook into `onChange` because browsers don't emit change events // for invalid values - see https://github.com/facebook/react/issues/16554 - const onKeyDown = useCallback( + const onKeyUp = useCallback( (e: React.KeyboardEvent) => { - _onKeyDown?.(e); - // Wait a beat before checking validity - we can't use `e.target` as it's stale - requestAnimationFrame(() => { - setIsNativelyInvalid(!validityRef.current!.validity.valid); - }); + _onKeyUp?.(e); + + const { validity } = e.target as HTMLInputElement; + setIsNativelyInvalid(!validity.valid); }, - [_onKeyDown] + [_onKeyUp] ); const numIconsClass = controlOnly @@ -155,8 +150,8 @@ export const EuiFieldNumber: FunctionComponent = ( placeholder={placeholder} readOnly={readOnly} className={classes} - ref={setRefs} - onKeyDown={onKeyDown} + ref={inputRef} + onKeyUp={onKeyUp} aria-invalid={isInvalid || isNativelyInvalid} {...rest} /> From 2dffd5fadffb53173bdd47a3e29b47bf22cbaa18 Mon Sep 17 00:00:00 2001 From: Constance Chen Date: Thu, 27 Apr 2023 13:43:01 -0700 Subject: [PATCH 2/3] [misc] Prefer `undefined` for `isNativeInvalid` - so that the `aria-invalid` prop only appears when invalid - consumers passing `isInvalid={false}` should still see `aria-invalid="false"` --- .../quick_select_popover.test.tsx.snap | 1 - .../__snapshots__/field_number.test.tsx.snap | 13 ------------- src/components/form/field_number/field_number.tsx | 10 +++++++--- .../range/__snapshots__/dual_range.test.tsx.snap | 8 -------- .../form/range/__snapshots__/range.test.tsx.snap | 3 --- .../range/__snapshots__/range_input.test.tsx.snap | 1 - 6 files changed, 7 insertions(+), 29 deletions(-) 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 ee65360e55b..5ab6de9a433 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 @@ -134,7 +134,6 @@ exports[`EuiQuickSelectPanels customQuickSelectPanels should render custom panel > = ( // will set :invalid state automatically, but we need to also set // `aria-invalid` as well as display an icon. We also want to *not* set this on // EuiValidatableControl, in order to not override custom validity messages - const [isNativelyInvalid, setIsNativelyInvalid] = useState(false); + const [isNativelyInvalid, setIsNativelyInvalid] = useState< + true | undefined + >(); // Note that we can't use hook into `onChange` because browsers don't emit change events // for invalid values - see https://github.com/facebook/react/issues/16554 @@ -118,7 +120,9 @@ export const EuiFieldNumber: FunctionComponent = ( _onKeyUp?.(e); const { validity } = e.target as HTMLInputElement; - setIsNativelyInvalid(!validity.valid); + // Prefer `undefined` over `false` so that the `aria-invalid` prop unsets completely + const isInvalid = !validity.valid || undefined; + setIsNativelyInvalid(isInvalid); }, [_onKeyUp] ); @@ -152,7 +156,7 @@ export const EuiFieldNumber: FunctionComponent = ( className={classes} ref={inputRef} onKeyUp={onKeyUp} - aria-invalid={isInvalid || isNativelyInvalid} + aria-invalid={isInvalid == null ? isNativelyInvalid : isInvalid} {...rest} /> diff --git a/src/components/form/range/__snapshots__/dual_range.test.tsx.snap b/src/components/form/range/__snapshots__/dual_range.test.tsx.snap index db1152e1a6b..5515fd4f6f7 100644 --- a/src/components/form/range/__snapshots__/dual_range.test.tsx.snap +++ b/src/components/form/range/__snapshots__/dual_range.test.tsx.snap @@ -63,7 +63,6 @@ exports[`EuiDualRange input props can be applied to min and max inputs 1`] = ` class="euiFormControlLayout__childrenWrapper" > Date: Thu, 27 Apr 2023 14:05:58 -0700 Subject: [PATCH 3/3] changelog --- upcoming_changelogs/6741.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 upcoming_changelogs/6741.md diff --git a/upcoming_changelogs/6741.md b/upcoming_changelogs/6741.md new file mode 100644 index 00000000000..29313a4baaf --- /dev/null +++ b/upcoming_changelogs/6741.md @@ -0,0 +1,3 @@ +**Bug fixes** + +- Fixed `EuiFieldNumber`'s native browser validity detection causing extra unnecessary rerenders