Adds validation to Dropdown List property editor

Fixes #17271
This commit is contained in:
leekelleher
2025-03-10 18:52:05 +00:00
committed by Jacob Overgaard
parent be4d4bef39
commit c1671f4e9b
2 changed files with 106 additions and 30 deletions

View File

@@ -1,13 +1,29 @@
import { css, html, customElement, property, query } from '@umbraco-cms/backoffice/external/lit';
import { UUIFormControlMixin } from '@umbraco-cms/backoffice/external/uui';
import { css, customElement, html, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
import type { UUISelectEvent } from '@umbraco-cms/backoffice/external/uui';
@customElement('umb-input-dropdown-list')
export class UmbInputDropdownListElement extends UUIFormControlMixin(UmbLitElement, undefined) {
export class UmbInputDropdownListElement extends UmbFormControlMixin<
string | undefined,
typeof UmbLitElement,
undefined
>(UmbLitElement, undefined) {
@property({ type: Array })
public options?: Array<Option>;
public set options(value: Array<Option> | undefined) {
this.#options = value;
this.value =
value
?.filter((option) => option.selected)
.map((option) => option.value)
.join(', ') ?? undefined;
}
public get options(): Array<Option> | undefined {
return this.#options;
}
#options?: Array<Option> | undefined;
@property({ type: String })
public placeholder?: string;
@@ -16,6 +32,19 @@ export class UmbInputDropdownListElement extends UUIFormControlMixin(UmbLitEleme
@property({ type: Boolean })
public multiple?: boolean;
@property({ type: String })
name?: string = 'Dropdown';
/**
* Sets the input to required, meaning validation will fail if the value is empty.
* @type {boolean}
*/
@property({ type: Boolean })
required?: boolean;
@property({ type: String })
requiredMessage?: string;
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
@@ -25,26 +54,36 @@ export class UmbInputDropdownListElement extends UUIFormControlMixin(UmbLitEleme
@property({ type: Boolean, reflect: true })
readonly = false;
@query('uui-select')
private selectEle!: HTMLInputElement;
constructor() {
super();
protected override getFormElement() {
return this.selectEle;
this.addValidator(
'valueMissing',
() => this.requiredMessage ?? UMB_VALIDATION_EMPTY_LOCALIZATION_KEY,
() => !this.readonly && !!this.required && (this.value === undefined || this.value === null || this.value === ''),
);
}
protected override firstUpdated() {
this.addFormControlElement(this.shadowRoot!.querySelector('uui-select')!);
}
#onChange(e: UUISelectEvent) {
e.stopPropagation();
if (e.target.value) this.value = e.target.value;
this.value = e.target.value?.toString() ?? undefined;
this.dispatchEvent(new UmbChangeEvent());
}
override render() {
return html`<uui-select
label=${this.localize.term('formProviderFieldTypes_dropdownName')}
.placeholder=${this.placeholder ?? ''}
.options=${this.options ?? []}
@change=${this.#onChange}
?readonly=${this.readonly}></uui-select>`;
return html`
<uui-select
label=${this.localize.term(this.localize.term('general_fieldFor', [this.name]))}
.placeholder=${this.placeholder ?? ''}
.options=${this.options ?? []}
@change=${this.#onChange}
?readonly=${this.readonly}>
</uui-select>
`;
}
static override styles = [

View File

@@ -1,6 +1,7 @@
import { css, customElement, html, map, nothing, property, state } from '@umbraco-cms/backoffice/external/lit';
import { css, customElement, html, map, nothing, property, state, when } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbPropertyValueChangeEvent } from '@umbraco-cms/backoffice/property-editor';
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
import { UUISelectElement } from '@umbraco-cms/backoffice/external/uui';
import type {
UmbPropertyEditorConfigCollection,
@@ -12,14 +13,26 @@ import type { UUISelectEvent } from '@umbraco-cms/backoffice/external/uui';
* @element umb-property-editor-ui-dropdown
*/
@customElement('umb-property-editor-ui-dropdown')
export class UmbPropertyEditorUIDropdownElement extends UmbLitElement implements UmbPropertyEditorUiElement {
export class UmbPropertyEditorUIDropdownElement
extends UmbFormControlMixin<Array<string> | string | undefined, typeof UmbLitElement, undefined>(
UmbLitElement,
undefined,
)
implements UmbPropertyEditorUiElement
{
#selection: Array<string> = [];
@state()
private _multiple: boolean = false;
@state()
private _options: Array<Option & { invalid?: boolean }> = [];
@property({ type: Array })
public set value(value: Array<string> | string | undefined) {
public override set value(value: Array<string> | string | undefined) {
this.#selection = Array.isArray(value) ? value : value ? [value] : [];
}
public get value(): Array<string> | undefined {
public override get value(): Array<string> | undefined {
return this.#selection;
}
@@ -32,6 +45,19 @@ export class UmbPropertyEditorUIDropdownElement extends UmbLitElement implements
@property({ type: Boolean, reflect: true })
readonly = false;
/**
* Sets the input to mandatory, meaning validation will fail if the value is empty.
* @type {boolean}
*/
@property({ type: Boolean })
mandatory?: boolean;
@property({ type: String })
mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
@property({ type: String })
name?: string;
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
if (!config) return;
@@ -63,11 +89,13 @@ export class UmbPropertyEditorUIDropdownElement extends UmbLitElement implements
this._multiple = config.getValueByAlias<boolean>('multiple') ?? false;
}
@state()
private _multiple: boolean = false;
@state()
private _options: Array<Option & { invalid?: boolean }> = [];
protected override firstUpdated() {
if (this._multiple) {
this.addFormControlElement(this.shadowRoot!.querySelector('select')!);
} else {
this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-dropdown-list')!);
}
}
#onChange(event: UUISelectEvent) {
const value = event.target.value as string;
@@ -87,7 +115,14 @@ export class UmbPropertyEditorUIDropdownElement extends UmbLitElement implements
}
override render() {
return this._multiple ? this.#renderDropdownMultiple() : this.#renderDropdownSingle();
return html`
${when(
this._multiple,
() => this.#renderDropdownMultiple(),
() => this.#renderDropdownSingle(),
)}
${this.#renderDropdownValidation()}
`;
}
#renderDropdownMultiple() {
@@ -96,23 +131,25 @@ export class UmbPropertyEditorUIDropdownElement extends UmbLitElement implements
}
return html`
<select id="native" multiple @change=${this.#onChangeMulitple}>
<select id="native" multiple ?required=${this.mandatory} @change=${this.#onChangeMulitple}>
${map(
this._options,
(item) => html`<option value=${item.value} ?selected=${item.selected}>${item.name}</option>`,
)}
</select>
${this.#renderDropdownValidation()}
`;
}
#renderDropdownSingle() {
return html`
<umb-input-dropdown-list
.name=${this.name}
.options=${this._options}
@change=${this.#onChange}
?readonly=${this.readonly}></umb-input-dropdown-list>
${this.#renderDropdownValidation()}
.required=${this.mandatory}
.requiredMessage=${this.mandatoryMessage}
?readonly=${this.readonly}
@change=${this.#onChange}>
</umb-input-dropdown-list>
`;
}