From 96945bb1f6802d3ec9234e35efc2e10b9444b128 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Sat, 24 Aug 2024 21:32:23 +0200 Subject: [PATCH] implement localization of validation messages --- .../block-grid-entries.element.ts | 2 +- .../property-editor-ui-block-list.element.ts | 4 +- ...ultiple-color-picker-item-input.element.ts | 4 +- ...input-multiple-text-string-item.element.ts | 4 +- ...roperty-workspace-view-settings.element.ts | 12 +-- .../property-layout.element.ts | 4 +- .../form-validation-message.element.ts | 97 +++++++++++++++++++ .../src/packages/core/validation/index.ts | 3 +- .../validation/mixins/form-control.mixin.ts | 10 +- 9 files changed, 120 insertions(+), 20 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/core/validation/components/form-validation-message.element.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts index 8ecfe7f894..96b4fd3f00 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-grid/components/block-grid-entries/block-grid-entries.element.ts @@ -284,7 +284,7 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen `, )} - + ${this._canCreate ? this.#renderCreateButton() : nothing} `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts index 9e263cf849..30aceb67a7 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-list/property-editors/block-list-editor/property-editor-ui-block-list.element.ts @@ -163,13 +163,13 @@ export class UmbPropertyEditorUIBlockListElement this.addValidator( 'rangeUnderflow', - () => this.localize.term('validation_entriesShort'), + () => '#validation_entriesShort', () => !!this._limitMin && this.#entriesContext.getLength() < this._limitMin, ); this.addValidator( 'rangeOverflow', - () => this.localize.term('validation_entriesExceed'), + () => '#validation_entriesExceed', () => !!this._limitMax && this.#entriesContext.getLength() > this._limitMax, ); diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-item-input.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-item-input.element.ts index 75191cc44e..2513799e64 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-item-input.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-color-picker-input/multiple-color-picker-item-input.element.ts @@ -134,7 +134,7 @@ export class UmbMultipleColorPickerItemInputElement extends UUIFormControlMixin( override render() { //TODO: Using native input=color element instead of uui-color-picker due to its huge size and bad adaptability as a pop up return html` - +
${this.disabled || this.readonly ? nothing : html``}
@@ -183,7 +183,7 @@ export class UmbMultipleColorPickerItemInputElement extends UUIFormControlMixin( `, )}
- + `; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string-item.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string-item.element.ts index 9c3979f30d..9087d92a28 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string-item.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/multiple-text-string-input/input-multiple-text-string-item.element.ts @@ -79,7 +79,7 @@ export class UmbInputMultipleTextStringItemElement extends UUIFormControlMixin(U return html` ${this.disabled || this.readonly ? nothing : html``} - + - + ${when( !this.readonly, diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts index e908214675..21f0d5d93b 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property-type/workspace/views/settings/property-workspace-view-settings.element.ts @@ -195,7 +195,7 @@ export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement i return html`
- + - - + + - +
- + - +
Validation diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts index 1724d318a1..e7e1b41dc5 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/property/property-layout/property-layout.element.ts @@ -81,9 +81,9 @@ export class UmbPropertyLayoutElement extends UmbLitElement {
- + - +
`; } diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/components/form-validation-message.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/components/form-validation-message.element.ts new file mode 100644 index 0000000000..38a4fb1181 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/components/form-validation-message.element.ts @@ -0,0 +1,97 @@ +import { UmbValidationInvalidEvent, UmbValidationValidEvent } from '../events/index.js'; +import type { UmbFormControlMixinInterface } from '../mixins/index.js'; +import { css, customElement, html, property, repeat, unsafeHTML } from '@umbraco-cms/backoffice/external/lit'; +import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; + +/** + * @description - Component for displaying one or more validation messages from UMB/UUI Form Control within the given scope. + * Notice: Only supports components that is build on the UMB / UUI FormControlMixing. + * @slot - for button contents + * @slot message - for extras in the messages container + * @see FormControlMixin + */ +@customElement('umb-form-validation-message') +export class UmbFormValidationMessageElement extends UmbLitElement { + /** + * Set the element containing Form Controls of interest. + * @type {string} + * @default + */ + @property({ reflect: false, attribute: true }) + public get for(): HTMLElement | string | null { + return this._for; + } + public set for(value: HTMLElement | string | null) { + let element = null; + if (typeof value === 'string') { + const scope = this.getRootNode(); + element = (scope as DocumentFragment)?.getElementById(value); + } else if (value instanceof HTMLElement) { + element = value; + } + const newScope = element ?? this; + const oldScope = this._for; + + if (oldScope === newScope) { + return; + } + if (oldScope !== null) { + oldScope.removeEventListener(UmbValidationInvalidEvent.TYPE, this.#onControlInvalid as EventListener); + oldScope.removeEventListener(UmbValidationValidEvent.TYPE, this.#onControlValid as EventListener); + } + this._for = newScope; + this._for.addEventListener(UmbValidationInvalidEvent.TYPE, this.#onControlInvalid as EventListener); + this._for.addEventListener(UmbValidationValidEvent.TYPE, this.#onControlValid as EventListener); + } + private _for: HTMLElement | null = null; + + constructor() { + super(); + if (this.for === null) { + this.for = this; + } + } + + private _messages = new Map, string>(); + + #onControlInvalid = async (e: UmbValidationInvalidEvent) => { + const ctrl = (e as any).composedPath()[0]; + if (ctrl.pristine === false) { + // Currently we only show message from components who does have the pristine property. (we only want to show messages from fields that are NOT pristine aka. that are dirty or in a from that has been submitted) + // Notice we use the localization controller here, this is different frm the UUI component which uses the same name. + this._messages.set(ctrl, this.localize.string(ctrl.validationMessage)); + } else { + this._messages.delete(ctrl); + } + this.requestUpdate(); + }; + + #onControlValid = (e: UmbValidationValidEvent) => { + const ctrl = (e as any).composedPath()[0]; + this._messages.delete(ctrl); + this.requestUpdate(); + }; + + override render() { + return html` + +
+ ${repeat(this._messages, (item) => html`
${unsafeHTML(item[1])}
`)} + +
+ `; + } + + static override styles = [ + css` + #messages { + color: var(--uui-color-danger-standalone); + } + `, + ]; +} +declare global { + interface HTMLElementTagNameMap { + 'umb-form-validation-message': UmbFormValidationMessageElement; + } +} diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts b/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts index ecb51f74ac..4231347296 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/core/validation/index.ts @@ -1,9 +1,10 @@ +export * from './components/form-validation-message.element.js'; export * from './const.js'; export * from './context/index.js'; export * from './controllers/index.js'; +export * from './directives/bind-to-validation.lit-directive.js'; export * from './events/index.js'; export * from './interfaces/index.js'; export * from './mixins/index.js'; export * from './translators/index.js'; export * from './utils/index.js'; -export * from './directives/bind-to-validation.lit-directive.js'; 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 4a634d197c..2732156f80 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 @@ -99,6 +99,7 @@ export declare abstract class UmbFormControlMixinElement * The mixin allows a custom element to participate in HTML forms. * @param {object} superClass - superclass to be extended. * @param defaultValue + * @returns {class} - The mixin class. * @mixin */ export function UmbFormControlMixin< @@ -172,7 +173,7 @@ export function UmbFormControlMixin< * Get internal form element. * This has to be implemented to provide a FormControl Element of choice for the given context. The element is used as anchor for validation-messages. * @function getFormElement - * @returns {HTMLElement | undefined | null} + * @returns {HTMLElement | undefined | null} - Returns the form element or undefined if not found. */ protected getFormElement(): HTMLElement | undefined | null { return this.#formCtrlElements.find((el) => el.validity.valid === false); @@ -181,7 +182,7 @@ export function UmbFormControlMixin< /** * Focus first element that is invalid. * @function focusFirstInvalidElement - * @returns {HTMLElement | undefined} + * @returns {HTMLElement | undefined} - Returns the first invalid element or undefined if no invalid elements are found. */ focusFirstInvalidElement() { const firstInvalid = this.#formCtrlElements.find((el) => el.validity.valid === false); @@ -219,6 +220,7 @@ export function UmbFormControlMixin< * @param {FlagTypes} flagKey the type of validation. * @param {method} getMessageMethod method to retrieve relevant message. Is executed every time the validator is re-executed. * @param {method} checkMethod method to determine if this validator should invalidate this form control. Return true if this should prevent submission. + * @returns {UmbFormControlValidatorConfig} - The added validator configuration. */ addValidator( flagKey: FlagTypes, @@ -252,7 +254,7 @@ export function UmbFormControlMixin< /** * @function addFormControlElement * @description Important notice if adding a native form control then ensure that its value and thereby validity is updated when value is changed from the outside. - * @param element {UmbNativeFormControlElement} - element to validate and include as part of this form association. + * @param {UmbNativeFormControlElement} element - element to validate and include as part of this form association. */ protected addFormControlElement(element: UmbNativeFormControlElement) { this.#formCtrlElements.push(element); @@ -275,7 +277,7 @@ export function UmbFormControlMixin< /** * @function setCustomValidity * @description Set custom validity state, set to empty string to remove the custom message. - * @param message {string} - The message to be shown + * @param {string} message - The message to be shown * @see {@link https://developer.mozilla.org/en-US/docs/Web/API/HTMLObjectElement/setCustomValidity|HTMLObjectElement:setCustomValidity} */ protected setCustomValidity(message: string | null) {