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] Update native validity state on blur & report/surface native validation messages #6758

Closed
wants to merge 2 commits into from
Closed
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
11 changes: 11 additions & 0 deletions src/components/form/field_number/field_number.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,16 @@ describe('EuiFieldNumber', () => {
cy.get('input').click().type('2');
checkIsInvalid();
});

it('shows invalid state on blur', () => {
cy.mount(<EuiFieldNumber max={1} value={2} />);
checkIsValid();
cy.get('input').click();
cy.get('body').click('bottomRight');
checkIsInvalid();
});

// TODO: Consider adding a Cypress visual snapshot/diff plugin here
// to confirm that the native browser validity report displays as expected
});
});
44 changes: 33 additions & 11 deletions src/components/form/field_number/field_number.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ export type EuiFieldNumberProps = Omit<
* @default false
*/
compressed?: boolean;

/**
* By default, native browser invalidity messages will be reported on user input
* (e.g., if consumers exceed the set `min` or `max` range, or do not match `step` increments).
* These messages are displayed via the browser `reportValidity` API.
*
* If you would prefer to use your own error messaging, set this flag to `false`.
* @default true
*/
reportNativeInvalidity?: boolean;
};

export const EuiFieldNumber: FunctionComponent<EuiFieldNumberProps> = (
Expand All @@ -101,7 +111,9 @@ export const EuiFieldNumber: FunctionComponent<EuiFieldNumberProps> = (
inputRef,
readOnly,
controlOnly,
onKeyUp: _onKeyUp,
onKeyUp,
onBlur,
reportNativeInvalidity = true,
...rest
} = props;

Expand All @@ -113,18 +125,19 @@ export const EuiFieldNumber: FunctionComponent<EuiFieldNumberProps> = (
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 onKeyUp = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
_onKeyUp?.(e);

const { validity } = e.target as HTMLInputElement;
const checkNativeValidity = useCallback(
(inputEl: HTMLInputElement) => {
// Prefer `undefined` over `false` so that the `aria-invalid` prop unsets completely
const isInvalid = !validity.valid || undefined;
const isInvalid = !inputEl.validity.valid || undefined;
setIsNativelyInvalid(isInvalid);

// Some browser (i.e. Safari) don't make their native error messages visible -
// the `reportValidity` API more clearly surfaces these messages
if (isInvalid && reportNativeInvalidity) {
inputEl.reportValidity();
}
},
[_onKeyUp]
[reportNativeInvalidity]
);

const numIconsClass = controlOnly
Expand Down Expand Up @@ -155,8 +168,17 @@ export const EuiFieldNumber: FunctionComponent<EuiFieldNumberProps> = (
readOnly={readOnly}
className={classes}
ref={inputRef}
onKeyUp={onKeyUp}
aria-invalid={isInvalid == null ? isNativelyInvalid : isInvalid}
onKeyUp={(e) => {
// Note that we can't use `onChange` because browsers don't emit change events
// for invalid text - see https://github.com/facebook/react/issues/16554
onKeyUp?.(e);
checkNativeValidity(e.currentTarget);
}}
onBlur={(e) => {
onBlur?.(e);
checkNativeValidity(e.currentTarget);
}}
{...rest}
/>
</EuiValidatableControl>
Expand Down