From f5b01183e2c722b977231ae52a1c383e6b869931 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 23 Sep 2024 14:17:52 +0200 Subject: [PATCH 1/2] Implement validation in insert field modal --- .../template-field-dropdown-list.element.ts | 40 ++++++++++++++--- ...lating-page-field-builder-modal.element.ts | 45 +++++++++++++------ 2 files changed, 66 insertions(+), 19 deletions(-) diff --git a/src/packages/templating/modals/templating-page-field-builder/components/template-field-dropdown-list/template-field-dropdown-list.element.ts b/src/packages/templating/modals/templating-page-field-builder/components/template-field-dropdown-list/template-field-dropdown-list.element.ts index ef744bf99c..d199e00ea3 100644 --- a/src/packages/templating/modals/templating-page-field-builder/components/template-field-dropdown-list/template-field-dropdown-list.element.ts +++ b/src/packages/templating/modals/templating-page-field-builder/components/template-field-dropdown-list/template-field-dropdown-list.element.ts @@ -16,6 +16,11 @@ import type { UUIComboboxEvent, UUIComboboxElement } from '@umbraco-cms/backoffi import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbMediaTypeDetailRepository, UMB_MEDIA_TYPE_PICKER_MODAL } from '@umbraco-cms/backoffice/media-type'; import { UMB_MODAL_MANAGER_CONTEXT, type UmbModalManagerContext } from '@umbraco-cms/backoffice/modal'; +import { + UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, + UmbFormControlMixin, + type UmbFormControlValidatorConfig, +} from '@umbraco-cms/backoffice/validation'; interface FieldPickerValue { alias: string; @@ -29,19 +34,44 @@ enum FieldType { } @customElement('umb-template-field-dropdown-list') -export class UmbTemplateFieldDropdownListElement extends UmbLitElement { +export class UmbTemplateFieldDropdownListElement extends UmbFormControlMixin< + FieldPickerValue | undefined, + typeof UmbLitElement, + undefined +>(UmbLitElement) { + // + #requiredValidation?: UmbFormControlValidatorConfig; + @property({ type: Boolean }) + public get required(): boolean | undefined { + return undefined; + } + public set required(value: boolean | undefined) { + if (value === true) { + this.#requiredValidation ??= this.addValidator( + 'valueMissing', + () => this.requiredMessage, + () => !this._value, + ); + } else if (this.#requiredValidation) { + this.removeValidator(this.#requiredValidation); + } + } + + @property({ type: String }) + requiredMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY; + @property({ type: Boolean, attribute: 'exclude-media-type', reflect: true }) public excludeMediaType = false; private _value: FieldPickerValue | undefined; @property({ type: Object }) - public set value(val: FieldPickerValue | undefined) { + public override set value(val: FieldPickerValue | undefined) { const oldVal = this._value; this._value = val; this.requestUpdate('value', oldVal); this.dispatchEvent(new UmbChangeEvent()); } - public get value(): FieldPickerValue | undefined { + public override get value(): FieldPickerValue | undefined { return this._value; } @@ -176,8 +206,8 @@ export class UmbTemplateFieldDropdownListElement extends UmbLitElement { if (this._type !== FieldType.SYSTEM && !this._unique) return; return html` ${this.localize.string(this._uniqueName ?? '')} - - + + ${repeat( this._customFields, (field) => field.alias, diff --git a/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts b/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts index 9f8ea3fbba..9d1eec32e3 100644 --- a/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts +++ b/src/packages/templating/modals/templating-page-field-builder/templating-page-field-builder-modal.element.ts @@ -7,24 +7,35 @@ import type { UmbTemplateFieldDropdownListElement } from './components/template- import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { css, html, customElement, state } from '@umbraco-cms/backoffice/external/lit'; import { UmbModalBaseElement } from '@umbraco-cms/backoffice/modal'; -import type { UUIBooleanInputEvent, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; +import type { UUIBooleanInputEvent, UUIButtonState, UUIInputEvent } from '@umbraco-cms/backoffice/external/uui'; // import of local components import './components/template-field-dropdown-list/index.js'; +import { UmbValidationContext, umbBindToValidation } from '@umbraco-cms/backoffice/validation'; @customElement('umb-templating-page-field-builder-modal') export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseElement< UmbTemplatingPageFieldBuilderModalData, UmbTemplatingPageFieldBuilderModalValue > { - private _close() { + #validation = new UmbValidationContext(this); + + #close() { this.modalContext?.reject(); } - private _submit() { - if (!this._field) return; - this.value = { output: getUmbracoFieldSnippet(this._field, this._default, this._recursive) }; - this.modalContext?.submit(); + async #submit() { + this._submitButtonState = 'waiting'; + + try { + await this.#validation.validate(); + this._submitButtonState = 'success'; + + this.value = { output: getUmbracoFieldSnippet(this._field!, this._default, this._recursive) }; + this.modalContext?.submit(); + } catch { + this._submitButtonState = 'failed'; + } } @state() @@ -39,6 +50,9 @@ export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseEleme @state() private _recursive: boolean = false; + @state() + private _submitButtonState: UUIButtonState; + /** TODO: Implement "Choose field" */ #onChangeFieldValue(e: Event) { @@ -50,12 +64,14 @@ export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseEleme
- - Choose field - - + + + Default value @@ -85,14 +101,15 @@ export class UmbTemplatingPageFieldBuilderModalElement extends UmbModalBaseEleme `; From bae0c270fc0512c7cfc39e688668d9c913dd4422 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niels=20Lyngs=C3=B8?= Date: Mon, 23 Sep 2024 14:21:18 +0200 Subject: [PATCH 2/2] user workspace validation context --- .../core/validation/controllers/validation.controller.ts | 2 +- .../user-workspace-profile-settings.element.ts | 2 ++ .../user/user/workspace/user/user-workspace.context.ts | 3 +++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/packages/core/validation/controllers/validation.controller.ts b/src/packages/core/validation/controllers/validation.controller.ts index eac15a1308..80a819b598 100644 --- a/src/packages/core/validation/controllers/validation.controller.ts +++ b/src/packages/core/validation/controllers/validation.controller.ts @@ -219,7 +219,7 @@ export class UmbValidationController extends UmbControllerBase implements UmbVal /** * Validate this context, all the validators of this context will be validated. * Notice its a recursive check meaning sub validation contexts also validates their validators. - * @returns succeed {Promise} - Returns a promise that resolves to true if the validator succeeded, this depends on the validators and wether forceSucceed is set. + * @returns succeed {Promise} - Returns a promise that resolves to true if the validation succeeded. */ async validate(): Promise { // TODO: clear server messages here?, well maybe only if we know we will get new server messages? Do the server messages hook into the system like another validator? diff --git a/src/packages/user/user/workspace/user/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts b/src/packages/user/user/workspace/user/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts index 0d313eb130..c3a6a54501 100644 --- a/src/packages/user/user/workspace/user/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts +++ b/src/packages/user/user/workspace/user/components/user-workspace-profile-settings/user-workspace-profile-settings.element.ts @@ -6,6 +6,7 @@ import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import type { UmbChangeEvent } from '@umbraco-cms/backoffice/event'; import type { UmbUiCultureInputElement } from '@umbraco-cms/backoffice/localization'; +import { umbBindToValidation } from '@umbraco-cms/backoffice/validation'; @customElement('umb-user-workspace-profile-settings') export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement { @@ -79,6 +80,7 @@ export class UmbUserWorkspaceProfileSettingsElement extends UmbLitElement { @change="${this.#onEmailChange}" required required-message=${this.localize.term('user_emailRequired')} + ${umbBindToValidation(this)} value=${ifDefined(this._user?.email)}> `; diff --git a/src/packages/user/user/workspace/user/user-workspace.context.ts b/src/packages/user/user/workspace/user/user-workspace.context.ts index 18963318ec..5108384550 100644 --- a/src/packages/user/user/workspace/user/user-workspace.context.ts +++ b/src/packages/user/user/workspace/user/user-workspace.context.ts @@ -9,6 +9,7 @@ import type { UmbSubmittableWorkspaceContext } from '@umbraco-cms/backoffice/wor import { UmbSubmittableWorkspaceContextBase } from '@umbraco-cms/backoffice/workspace'; import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; import { UmbObjectState } from '@umbraco-cms/backoffice/observable-api'; +import { UmbValidationContext } from '@umbraco-cms/backoffice/validation'; type EntityType = UmbUserDetailModel; @@ -40,6 +41,8 @@ export class UmbUserWorkspaceContext constructor(host: UmbControllerHost) { super(host, UMB_USER_WORKSPACE_ALIAS); + this.addValidationContext(new UmbValidationContext(this)); + this.routes.setRoutes([ { path: 'edit/:id',