@wordpress/components
Version:
UI components for WordPress.
137 lines (128 loc) • 4.89 kB
JavaScript
;
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