From cd84ff9b6d3562529b6bcf3044badb673018aa0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Thu, 8 Aug 2024 20:33:12 +0200 Subject: [PATCH] POC for single message based validation --- .../validation/mixins/form-control.mixin.ts | 63 +++++++++++++------ .../property-editor-ui-text-box.element.ts | 14 +++++ 2 files changed, 59 insertions(+), 18 deletions(-) diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts index d50342ea8c..a94eca0182 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/mixins/form-control.mixin.ts @@ -13,8 +13,8 @@ type UmbNativeFormControlElement = Pick< * https://developer.mozilla.org/en-US/docs/Web/API/ValidityState * */ type FlagTypes = - | 'badInput' | 'customError' + | 'badInput' | 'patternMismatch' | 'rangeOverflow' | 'rangeUnderflow' @@ -23,9 +23,21 @@ type FlagTypes = | 'tooShort' | 'typeMismatch' | 'valueMissing' - | 'badInput' | 'valid'; +const WeightedErrorFlagTypes = [ + 'customError', + 'badInput', + 'patternMismatch', + 'rangeOverflow', + 'rangeUnderflow', + 'stepMismatch', + 'tooLong', + 'tooShort', + 'typeMismatch', + 'valueMissing', +]; + // Acceptable as an internal interface/type, BUT if exposed externally this should be turned into a public interface in a separate file. export interface UmbFormControlValidatorConfig { flagKey: FlagTypes; @@ -222,6 +234,11 @@ export function UmbFormControlMixin< checkMethod: checkMethod, } satisfies UmbFormControlValidatorConfig; this.#validators.push(validator); + // Sort validators based on the WeightedErrorFlagTypes order. [NL] + this.#validators.sort((a, b) => { + // This could easily be extended with a weight set on the validator object itself. [NL] + return WeightedErrorFlagTypes.indexOf(a.flagKey) - WeightedErrorFlagTypes.indexOf(b.flagKey); + }); return validator; } @@ -291,29 +308,38 @@ export function UmbFormControlMixin< */ protected _runValidators() { this.#validity = {}; - const messages: Set = new Set(); + //const messages: Set = new Set(); + let message: string | undefined = undefined; let innerFormControlEl: UmbNativeFormControlElement | undefined = undefined; - // Loop through inner native form controls to adapt their validityState. [NL] - this.#formCtrlElements.forEach((formCtrlEl) => { - let key: keyof ValidityState; - for (key in formCtrlEl.validity) { - if (key !== 'valid' && formCtrlEl.validity[key]) { - this.#validity[key] = true; - messages.add(formCtrlEl.validationMessage); - innerFormControlEl ??= formCtrlEl; - } - } - }); - // Loop through custom validators, currently its intentional to have them overwritten native validity. but might need to be reconsidered (This current way enables to overwrite with custom messages) [NL] - this.#validators.forEach((validator) => { + this.#validators.some((validator) => { if (validator.checkMethod()) { this.#validity[validator.flagKey] = true; - messages.add(validator.getMessageMethod()); + //messages.add(validator.getMessageMethod()); + message = validator.getMessageMethod(); + return true; } + return false; }); + if (message) { + // Loop through inner native form controls to adapt their validityState. [NL] + this.#formCtrlElements.some((formCtrlEl) => { + let key: keyof ValidityState; + for (key in formCtrlEl.validity) { + if (key !== 'valid' && formCtrlEl.validity[key]) { + this.#validity[key] = true; + //messages.add(formCtrlEl.validationMessage); + message = formCtrlEl.validationMessage; + innerFormControlEl ??= formCtrlEl; + return true; + } + } + return false; + }); + } + const hasError = Object.values(this.#validity).includes(true); // https://developer.mozilla.org/en-US/docs/Web/API/ValidityState#valid @@ -323,7 +349,8 @@ export function UmbFormControlMixin< this._internals.setValidity( this.#validity, // Turn messages into an array and join them with a comma. [NL]: - [...messages].join(', '), + //[...messages].join(', '), + message, innerFormControlEl ?? this.getFormElement() ?? undefined, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts index 4cac623483..0edcbdc497 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/property-editors/text-box/property-editor-ui-text-box.element.ts @@ -55,6 +55,20 @@ export class UmbPropertyEditorUITextBoxElement this._placeholder = config?.getValueByAlias('placeholder'); } + constructor() { + super(); + this.addValidator( + 'tooShort', + () => 'custom too short msg', + () => String(this.value).length < 10, + ); + this.addValidator( + 'valueMissing', + () => 'custom valueMissing msg', + () => this.value === undefined && this.value === '', + ); + } + protected override firstUpdated(): void { this.addFormControlElement(this.shadowRoot!.querySelector('uui-input')!); }