Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: Localization of validation messages #2237

Merged
merged 5 commits into from
Aug 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -284,7 +284,7 @@ export class UmbBlockGridEntriesElement extends UmbFormControlMixin(UmbLitElemen
</umb-block-grid-entry>`,
)}
</div>
<uui-form-validation-message .for=${this}></uui-form-validation-message>
<umb-form-validation-message .for=${this}></umb-form-validation-message>
${this._canCreate ? this.#renderCreateButton() : nothing}
`;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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`
<uui-form-validation-message id="validation-message" @invalid=${this.#onInvalid} @valid=${this.#onValid}>
<umb-form-validation-message id="validation-message" @invalid=${this.#onInvalid} @valid=${this.#onValid}>
<div id="item">
${this.disabled || this.readonly ? nothing : html`<uui-icon name="icon-navigation"></uui-icon>`}
<div class="color-wrapper">
Expand Down Expand Up @@ -183,7 +183,7 @@ export class UmbMultipleColorPickerItemInputElement extends UUIFormControlMixin(
`,
)}
</div>
</uui-form-validation-message>
</umb-form-validation-message>
`;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export class UmbInputMultipleTextStringItemElement extends UUIFormControlMixin(U
return html`
${this.disabled || this.readonly ? nothing : html`<uui-icon name="icon-navigation" class="handle"></uui-icon>`}

<uui-form-validation-message id="validation-message" @invalid=${this.#onInvalid} @valid=${this.#onValid}>
<umb-form-validation-message id="validation-message" @invalid=${this.#onInvalid} @valid=${this.#onValid}>
<uui-input
id="input"
label="Value"
Expand All @@ -90,7 +90,7 @@ export class UmbInputMultipleTextStringItemElement extends UUIFormControlMixin(U
?readonly=${this.readonly}
required=${this.required}
required-message="Value is missing"></uui-input>
</uui-form-validation-message>
</umb-form-validation-message>

${when(
!this.readonly,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement i
return html`
<uui-box class="uui-text">
<div class="container">
<uui-form-validation-message>
<umb-form-validation-message>
<uui-input
id="name-input"
name="name"
Expand All @@ -208,8 +208,8 @@ export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement i
${umbFocus()}>
<!-- TODO: validation for bad characters -->
</uui-input>
</uui-form-validation-message>
<uui-form-validation-message>
</umb-form-validation-message>
<umb-form-validation-message>
<uui-input-lock
id="alias-input"
name="alias"
Expand All @@ -222,7 +222,7 @@ export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement i
@input=${this.#onAliasChange}
@lock-change=${this.#onToggleAliasLock}>
</uui-input-lock>
</uui-form-validation-message>
</umb-form-validation-message>
<uui-textarea
id="description-input"
name="description"
Expand All @@ -231,13 +231,13 @@ export class UmbPropertyTypeWorkspaceViewSettingsElement extends UmbLitElement i
placeholder=${this.localize.term('placeholders_enterDescription')}
.value=${this._data?.description}></uui-textarea>
</div>
<uui-form-validation-message>
<umb-form-validation-message>
<umb-data-type-flow-input
.value=${this._data?.dataType?.unique ?? ''}
@change=${this.#onDataTypeIdChange}
required
${umbBindToValidation(this, '$.dataType.unique')}></umb-data-type-flow-input>
</uui-form-validation-message>
</umb-form-validation-message>
<hr />
<div class="container">
<b><umb-localize key="validation_validation">Validation</umb-localize></b>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,9 @@ export class UmbPropertyLayoutElement extends UmbLitElement {
<slot name="description"></slot>
</div>
<div id="editorColumn">
<uui-form-validation-message>
<umb-form-validation-message>
<slot name="editor"></slot>
</uui-form-validation-message>
</umb-form-validation-message>
</div>
`;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<UmbFormControlMixinInterface<unknown>, 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`
<slot></slot>
<div id="messages">
${repeat(this._messages, (item) => html`<div>${unsafeHTML(item[1])}</div>`)}
<slot name="message"></slot>
</div>
`;
}

static override styles = [
css`
#messages {
color: var(--uui-color-danger-standalone);
}
`,
];
}
declare global {
interface HTMLElementTagNameMap {
'umb-form-validation-message': UmbFormValidationMessageElement;
}
}
3 changes: 2 additions & 1 deletion src/packages/core/validation/index.ts
Original file line number Diff line number Diff line change
@@ -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';
10 changes: 6 additions & 4 deletions src/packages/core/validation/mixins/form-control.mixin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ export declare abstract class UmbFormControlMixinElement<ValueType>
* 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<
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -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) {
Expand Down
Loading