From e27e3839df0f60517ebab713273cece156020a95 Mon Sep 17 00:00:00 2001 From: James Loveridge Date: Thu, 27 Nov 2025 10:05:32 +0000 Subject: [PATCH 01/22] feat: add functionality for Constraint Validation API & types --- components/o3-form/src/js/validation.js | 368 +++++++++++++++++++ components/o3-form/src/types/validation.d.ts | 53 +++ 2 files changed, 421 insertions(+) create mode 100644 components/o3-form/src/js/validation.js create mode 100644 components/o3-form/src/types/validation.d.ts diff --git a/components/o3-form/src/js/validation.js b/components/o3-form/src/js/validation.js new file mode 100644 index 0000000000..88b48116df --- /dev/null +++ b/components/o3-form/src/js/validation.js @@ -0,0 +1,368 @@ +/** + * Options to configure validation behaviour. + * + * @typedef {Object} O3ValidationOptions + * @property {boolean} [useBrowserReport=false] When true, call `form.reportValidity()` after an invalid submit (do not combine with `preventSubmit=true`). + * @property {boolean} [errorSummary=true] Whether to create/update an error summary element. + * @property {Object.} [customMessages] Map of field id or name to a custom error message override. + * @property {boolean} [preventSubmit=true] Prevent form submission while invalid. + * @property {boolean} [focusFirstInvalid=true] Focus the first invalid control (or summary heading) after validation. + * @property {(formEl: HTMLFormElement) => Promise} [asyncValidate] Optional future hook for asynchronous validation. (Reserved; currently ignored.) + */ + +/** + * FieldError represents a single invalid field. + * Mirrors exported TS FieldError type. + * + * @typedef {Object} FieldError + * @property {string} id Element id (may be blank if missing; summary links omitted if so). + * @property {string} fieldName Human friendly label text. + * @property {string} message Validation message. + * @property {('native'|'custom')} [source] Origin of message (native browser vs custom override). + */ + +const SUMMARY_SELECTOR = '.o3-form__error-summary'; +const SUMMARY_CLASS = 'o3-form__error-summary'; +const ERROR_INPUT_CLASS = 'o3-form-text-input--error'; +const ERROR_CHECK_CLASS = 'o3-form-input-error'; + +/** Maintain internal state per form element */ +const formState = new WeakMap(); + +/** + * Derive a human readable field name from its associated `