Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EuiFieldNumber] Clean up native :invalid detection #6741

Merged
merged 3 commits into from
Apr 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,6 @@ exports[`EuiQuickSelectPanels customQuickSelectPanels should render custom panel
>
<input
aria-describedby="generated-id generated-id"
aria-invalid="false"
aria-label="Time value"
class="euiFieldNumber euiFieldNumber--compressed"
type="number"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ exports[`EuiFieldNumber is rendered 1`] = `
fullwidth="false"
icon="warning"
inputid="1"
isinvalid="false"
isloading="false"
>
<eui-validatable-control>
<input
aria-invalid="false"
aria-label="aria-label"
class="euiFieldNumber testClass1 testClass2 euiFieldNumber--withIcon"
data-test-subj="test subject string"
Expand All @@ -30,7 +28,6 @@ exports[`EuiFieldNumber is rendered 1`] = `
exports[`EuiFieldNumber props controlOnly is rendered 1`] = `
<eui-validatable-control>
<input
aria-invalid="false"
class="euiFieldNumber"
type="number"
value=""
Expand All @@ -42,12 +39,10 @@ exports[`EuiFieldNumber props fullWidth is rendered 1`] = `
<eui-form-control-layout
compressed="false"
fullwidth="true"
isinvalid="false"
isloading="false"
>
<eui-validatable-control>
<input
aria-invalid="false"
class="euiFieldNumber euiFieldNumber--fullWidth"
type="number"
value=""
Expand Down Expand Up @@ -80,12 +75,10 @@ exports[`EuiFieldNumber props isLoading is rendered 1`] = `
<eui-form-control-layout
compressed="false"
fullwidth="false"
isinvalid="false"
isloading="true"
>
<eui-validatable-control>
<input
aria-invalid="false"
class="euiFieldNumber euiFormControlLayout--1icons euiFieldNumber-isLoading"
type="number"
value=""
Expand All @@ -98,13 +91,11 @@ exports[`EuiFieldNumber props readOnly is rendered 1`] = `
<eui-form-control-layout
compressed="false"
fullwidth="false"
isinvalid="false"
isloading="false"
readonly="true"
>
<eui-validatable-control>
<input
aria-invalid="false"
class="euiFieldNumber"
readonly=""
type="number"
Expand All @@ -118,12 +109,10 @@ exports[`EuiFieldNumber props value no initial value 1`] = `
<eui-form-control-layout
compressed="false"
fullwidth="false"
isinvalid="false"
isloading="false"
>
<eui-validatable-control>
<input
aria-invalid="false"
class="euiFieldNumber"
type="number"
value=""
Expand All @@ -136,12 +125,10 @@ exports[`EuiFieldNumber props value value is number 1`] = `
<eui-form-control-layout
compressed="false"
fullwidth="false"
isinvalid="false"
isloading="false"
>
<eui-validatable-control>
<input
aria-invalid="false"
class="euiFieldNumber"
type="number"
value="0"
Expand Down
31 changes: 15 additions & 16 deletions src/components/form/field_number/field_number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -103,29 +101,30 @@ export const EuiFieldNumber: FunctionComponent<EuiFieldNumberProps> = (
inputRef,
readOnly,
controlOnly,
onKeyDown: _onKeyDown,
onKeyUp: _onKeyUp,
...rest
} = props;

// Attempt to determine additional invalid state. The native number input
// 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 validityRef = useRef<HTMLInputElement | null>(null);
const setRefs = useCombinedRefs([validityRef, inputRef]);
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
const onKeyDown = useCallback(
const onKeyUp = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
_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;
// Prefer `undefined` over `false` so that the `aria-invalid` prop unsets completely
const isInvalid = !validity.valid || undefined;
setIsNativelyInvalid(isInvalid);
},
[_onKeyDown]
[_onKeyUp]
);

const numIconsClass = controlOnly
Expand Down Expand Up @@ -155,9 +154,9 @@ export const EuiFieldNumber: FunctionComponent<EuiFieldNumberProps> = (
placeholder={placeholder}
readOnly={readOnly}
className={classes}
ref={setRefs}
onKeyDown={onKeyDown}
aria-invalid={isInvalid || isNativelyInvalid}
ref={inputRef}
onKeyUp={onKeyUp}
aria-invalid={isInvalid == null ? isNativelyInvalid : isInvalid}
{...rest}
/>
</EuiValidatableControl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ exports[`EuiDualRange input props can be applied to min and max inputs 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-invalid="false"
aria-label="Min value"
class="euiFieldNumber euiRangeInput euiRangeInput--min emotion-euiRangeInput"
max="8"
Expand Down Expand Up @@ -113,7 +112,6 @@ exports[`EuiDualRange input props can be applied to min and max inputs 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-invalid="false"
aria-label="Max value"
class="euiFieldNumber euiRangeInput euiRangeInput--max emotion-euiRangeInput"
max="10"
Expand Down Expand Up @@ -323,7 +321,6 @@ exports[`EuiDualRange props inputs should render 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-invalid="false"
aria-label="aria-label"
class="euiFieldNumber euiRangeInput euiRangeInput--min emotion-euiRangeInput"
max="8"
Expand Down Expand Up @@ -375,7 +372,6 @@ exports[`EuiDualRange props inputs should render 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-invalid="false"
aria-label="aria-label"
class="euiFieldNumber euiRangeInput euiRangeInput--max emotion-euiRangeInput"
max="100"
Expand Down Expand Up @@ -532,7 +528,6 @@ exports[`EuiDualRange props loading should display when showInput="inputWithPopo
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-invalid="false"
class="euiFieldNumber euiFormControlLayoutDelimited__input emotion-euiRangeInput"
max="8"
min="0"
Expand All @@ -551,7 +546,6 @@ exports[`EuiDualRange props loading should display when showInput="inputWithPopo
</span>
</div>
<input
aria-invalid="false"
class="euiFieldNumber euiFormControlLayoutDelimited__input emotion-euiRangeInput"
max="100"
min="1"
Expand Down Expand Up @@ -621,7 +615,6 @@ exports[`EuiDualRange props slider should display in popover 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-invalid="false"
class="euiFieldNumber euiFormControlLayoutDelimited__input emotion-euiRangeInput"
max="8"
min="0"
Expand All @@ -640,7 +633,6 @@ exports[`EuiDualRange props slider should display in popover 1`] = `
</span>
</div>
<input
aria-invalid="false"
class="euiFieldNumber euiFormControlLayoutDelimited__input emotion-euiRangeInput"
max="100"
min="1"
Expand Down
3 changes: 0 additions & 3 deletions src/components/form/range/__snapshots__/range.test.tsx.snap
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,6 @@ exports[`EuiRange props input should render 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-invalid="false"
aria-label="aria-label"
class="euiFieldNumber euiRangeInput euiRangeInput--max emotion-euiRangeInput"
data-test-subj="test subject string"
Expand Down Expand Up @@ -355,7 +354,6 @@ exports[`EuiRange props loading should display when showInput="inputWithPopover"
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-invalid="false"
aria-label="aria-label"
class="euiFieldNumber euiRangeInput euiRangeInput--max emotion-euiRangeInput euiFormControlLayout--1icons euiFieldNumber-isLoading"
data-test-subj="test subject string"
Expand Down Expand Up @@ -428,7 +426,6 @@ exports[`EuiRange props slider should display in popover 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-invalid="false"
aria-label="aria-label"
class="euiFieldNumber euiRangeInput euiRangeInput--max emotion-euiRangeInput"
data-test-subj="test subject string"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ exports[`EuiRangeInput renders 1`] = `
class="euiFormControlLayout__childrenWrapper"
>
<input
aria-invalid="false"
class="euiFieldNumber euiRangeInput euiRangeInput--max emotion-euiRangeInput"
max="100"
min="0"
Expand Down
3 changes: 3 additions & 0 deletions upcoming_changelogs/6741.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**Bug fixes**

- Fixed `EuiFieldNumber`'s native browser validity detection causing extra unnecessary rerenders