UNPKG

@wordpress/components

Version:
137 lines (128 loc) 4.89 kB
"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault"); Object.defineProperty(exports, "__esModule", { value: true }); exports.ControlWithError = void 0; var _i18n = require("@wordpress/i18n"); var _icons = require("@wordpress/icons"); var _element = require("@wordpress/element"); var _withIgnoreImeEvents = require("../utils/with-ignore-ime-events"); var _icon = _interopRequireDefault(require("../icon")); var _jsxRuntime = require("react/jsx-runtime"); /** * WordPress dependencies */ /** * External dependencies */ /** * Internal dependencies */ function appendRequiredIndicator(label, required, markWhenOptional) { if (required && !markWhenOptional) { return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [label, " ", `(${(0, _i18n.__)('Required')})`] }); } if (!required && markWhenOptional) { return /*#__PURE__*/(0, _jsxRuntime.jsxs)(_jsxRuntime.Fragment, { children: [label, " ", `(${(0, _i18n.__)('Optional')})`] }); } return label; } /** * HTML elements that support the Constraint Validation API. * * Here, we exclude HTMLButtonElement because although it does technically support the API, * normal buttons are actually exempted from any validation. * @see https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Form_validation * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLButtonElement/willValidate */ function UnforwardedControlWithError({ required, markWhenOptional, customValidator, getValidityTarget, children }, forwardedRef) { const [errorMessage, setErrorMessage] = (0, _element.useState)(); const [isTouched, setIsTouched] = (0, _element.useState)(false); // Ensure that error messages are visible after user attemps to submit a form // with multiple invalid fields. (0, _element.useEffect)(() => { const validityTarget = getValidityTarget(); const showValidationMessage = () => setErrorMessage(validityTarget?.validationMessage); validityTarget?.addEventListener('invalid', showValidationMessage); return () => { validityTarget?.removeEventListener('invalid', showValidationMessage); }; }); const validate = () => { const message = customValidator?.(); const validityTarget = getValidityTarget(); validityTarget?.setCustomValidity(message !== null && message !== void 0 ? message : ''); setErrorMessage(validityTarget?.validationMessage); }; const onBlur = event => { // Only consider "blurred from the component" if focus has fully left the wrapping div. // This prevents unnecessary blurs from components with multiple focusable elements. if (!event.relatedTarget || !event.currentTarget.contains(event.relatedTarget)) { setIsTouched(true); const validityTarget = getValidityTarget(); // Prevents a double flash of the native error tooltip when the control is already showing one. if (!validityTarget?.validity.valid) { if (!errorMessage) { setErrorMessage(validityTarget?.validationMessage); } return; } validate(); } }; const onChange = (...args) => { children.props.onChange?.(...args); // Only validate incrementally if the field has blurred at least once, // or currently has an error message. if (isTouched || errorMessage) { validate(); } }; const onKeyDown = event => { // Ensures that custom validators are triggered when the user submits by pressing Enter, // without ever blurring the control. if (event.key === 'Enter') { validate(); } }; return ( /*#__PURE__*/ // Disable reason: Just listening to a bubbled event, not for interaction. // eslint-disable-next-line jsx-a11y/no-static-element-interactions (0, _jsxRuntime.jsxs)("div", { className: "components-validated-control", ref: forwardedRef, onBlur: onBlur, onKeyDown: (0, _withIgnoreImeEvents.withIgnoreIMEEvents)(onKeyDown), children: [(0, _element.cloneElement)(children, { label: appendRequiredIndicator(children.props.label, required, markWhenOptional), onChange, required }), /*#__PURE__*/(0, _jsxRuntime.jsx)("div", { "aria-live": "polite", children: errorMessage && /*#__PURE__*/(0, _jsxRuntime.jsxs)("p", { className: "components-validated-control__error", children: [/*#__PURE__*/(0, _jsxRuntime.jsx)(_icon.default, { className: "components-validated-control__error-icon", icon: _icons.error, size: 16, fill: "currentColor" }), errorMessage] }) })] }) ); } const ControlWithError = exports.ControlWithError = (0, _element.forwardRef)(UnforwardedControlWithError); //# sourceMappingURL=control-with-error.js.map