Merge pull request #2106 from umbraco/v14/feature/property-validation-mandatory-indicator

Feature: Property label mandatory indicator
This commit is contained in:
Lee Kelleher
2024-07-08 16:45:36 +01:00
committed by GitHub
4 changed files with 102 additions and 34 deletions

View File

@@ -1,19 +1,17 @@
import type { UmbPropertyEditorConfig } from '../../../property-editor/index.js';
import type { UmbPropertyTypeModel } from '../../types.js';
import { css, customElement, html, ifDefined, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbContentPropertyContext } from '@umbraco-cms/backoffice/content';
import type { UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type';
import { UmbDataTypeDetailRepository } from '@umbraco-cms/backoffice/data-type';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, ifDefined, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type { UmbDataTypeDetailModel } from '@umbraco-cms/backoffice/data-type';
import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
@customElement('umb-property-type-based-property')
export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement {
@property({ type: Object, attribute: false })
public get property(): UmbPropertyTypeModel | undefined {
return this._property;
}
public set property(value: UmbPropertyTypeModel | undefined) {
const oldProperty = this._property;
this._property = value;
@@ -21,6 +19,9 @@ export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement {
this._observeDataType(this._property?.dataType.unique);
}
}
public get property(): UmbPropertyTypeModel | undefined {
return this._property;
}
private _property?: UmbPropertyTypeModel;
@property({ type: String, attribute: 'data-path' })
@@ -73,16 +74,19 @@ export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement {
}
override render() {
return this._propertyEditorUiAlias && this._property?.alias
? html`<umb-property
.dataPath=${this.dataPath}
.alias=${this._property.alias}
.label=${this._property.name}
.description=${this._property.description ?? undefined}
.appearance=${this._property.appearance}
property-editor-ui-alias=${ifDefined(this._propertyEditorUiAlias)}
.config=${this._dataTypeData}></umb-property>`
: '';
if (!this._propertyEditorUiAlias || !this._property?.alias) return;
return html`
<umb-property
.dataPath=${this.dataPath}
.alias=${this._property.alias}
.label=${this._property.name}
.description=${this._property.description ?? undefined}
.appearance=${this._property.appearance}
property-editor-ui-alias=${ifDefined(this._propertyEditorUiAlias)}
.config=${this._dataTypeData}
.validation=${this._property.validation}>
</umb-property>
`;
}
static override styles = [

View File

@@ -51,7 +51,7 @@ export class UmbPropertyLayoutElement extends UmbLitElement {
public description = '';
/**
* @description Make the property appear invalid
* @description Make the property appear invalid.
* @type {boolean}
* @attr
* @default undefined
@@ -59,11 +59,20 @@ export class UmbPropertyLayoutElement extends UmbLitElement {
@property({ type: Boolean, reflect: true })
public invalid?: boolean;
/**
* @description Display a mandatory indicator.
* @type {boolean}
* @attr
* @default false
*/
@property({ type: Boolean, reflect: true })
public mandatory?: boolean;
override render() {
// TODO: Only show alias on label if user has access to DocumentType within settings:
return html`
<div id="headerColumn">
<uui-label id="label" title=${this.alias}>
<uui-label id="label" title=${this.alias} ?required=${this.mandatory}>
${this.localize.string(this.label)}
${when(this.invalid, () => html`<uui-badge color="danger" attention>!</uui-badge>`)}
</uui-label>

View File

@@ -14,27 +14,39 @@ import type { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import type { UmbPropertyEditorConfigProperty } from '@umbraco-cms/backoffice/property-editor';
import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
import type { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
import type { UmbPropertyTypeAppearanceModel } from '@umbraco-cms/backoffice/content-type';
import type {
UmbPropertyTypeAppearanceModel,
UmbPropertyTypeValidationModel,
} from '@umbraco-cms/backoffice/content-type';
export class UmbPropertyContext<ValueType = any> extends UmbContextBase<UmbPropertyContext<ValueType>> {
#alias = new UmbStringState(undefined);
public readonly alias = this.#alias.asObservable();
#label = new UmbStringState(undefined);
public readonly label = this.#label.asObservable();
#description = new UmbStringState(undefined);
public readonly description = this.#description.asObservable();
#appearance = new UmbObjectState<UmbPropertyTypeAppearanceModel | undefined>(undefined);
public readonly appearance = this.#appearance.asObservable();
#value = new UmbDeepState<ValueType | undefined>(undefined);
public readonly value = this.#value.asObservable();
#configValues = new UmbArrayState<UmbPropertyEditorConfigProperty>([], (x) => x.alias);
public readonly configValues = this.#configValues.asObservable();
#config = new UmbClassState<UmbPropertyEditorConfigCollection | undefined>(undefined);
public readonly config = this.#config.asObservable();
#validation = new UmbObjectState<UmbPropertyTypeValidationModel | undefined>(undefined);
public readonly validation = this.#validation.asObservable();
private _editor = new UmbBasicState<UmbPropertyEditorUiElement | undefined>(undefined);
public readonly editor = this._editor.asObservable();
setEditor(editor: UmbPropertyEditorUiElement | undefined) {
this._editor.setValue(editor ?? undefined);
}
@@ -108,24 +120,28 @@ export class UmbPropertyContext<ValueType = any> extends UmbContextBase<UmbPrope
public getAlias(): string | undefined {
return this.#alias.getValue();
}
public setLabel(label: string | undefined): void {
this.#label.setValue(label);
}
public getLabel(): string | undefined {
return this.#label.getValue();
}
public setDescription(description: string | undefined): void {
this.#description.setValue(description);
}
public getDescription(): string | undefined {
return this.#description.getValue();
}
public setAppearance(appearance: UmbPropertyTypeAppearanceModel | undefined): void {
this.#appearance.setValue(appearance);
}
public getAppearance(): UmbPropertyTypeAppearanceModel | undefined {
return this.#appearance.getValue();
}
/**
* Set the value of this property.
* @param value {ValueType} the whole value to be set
@@ -143,12 +159,14 @@ export class UmbPropertyContext<ValueType = any> extends UmbContextBase<UmbPrope
public getValue(): ValueType | undefined {
return this.#value.getValue();
}
public setConfig(config: Array<UmbPropertyEditorConfigProperty> | undefined): void {
this.#configValues.setValue(config ?? []);
}
public getConfig(): Array<UmbPropertyEditorConfigProperty> | undefined {
return this.#configValues.getValue();
}
public setVariantId(variantId: UmbVariantId | undefined): void {
this.#variantId.setValue(variantId);
}
@@ -156,6 +174,13 @@ export class UmbPropertyContext<ValueType = any> extends UmbContextBase<UmbPrope
return this.#variantId.getValue();
}
public setValidation(validation: UmbPropertyTypeValidationModel | undefined): void {
this.#validation.setValue(validation);
}
public getValidation(): UmbPropertyTypeValidationModel | undefined {
return this.#validation.getValue();
}
public resetValue(): void {
this.setValue(undefined); // TODO: We should get the value from the server aka. the value from the persisted data. (Most workspaces holds this data, via dataset) [NL]
}

View File

@@ -1,21 +1,24 @@
import { UmbPropertyContext } from './property.context.js';
import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, customElement, property, state, nothing } from '@umbraco-cms/backoffice/external/lit';
import { css, customElement, html, property, state, nothing } from '@umbraco-cms/backoffice/external/lit';
import { createExtensionElement } from '@umbraco-cms/backoffice/extension-api';
import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type {
UmbPropertyEditorConfigCollection,
UmbPropertyEditorConfig,
} from '@umbraco-cms/backoffice/property-editor';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import {
UmbBindValidationMessageToFormControl,
UmbFormControlValidator,
UmbObserveValidationStateController,
} from '@umbraco-cms/backoffice/validation';
import type { UmbPropertyTypeAppearanceModel } from '@umbraco-cms/backoffice/content-type';
import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry';
import type {
UmbPropertyEditorConfigCollection,
UmbPropertyEditorConfig,
} from '@umbraco-cms/backoffice/property-editor';
import type {
UmbPropertyTypeAppearanceModel,
UmbPropertyTypeValidationModel,
} from '@umbraco-cms/backoffice/content-type';
import type { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
/**
* @element umb-property
@@ -112,6 +115,17 @@ export class UmbPropertyElement extends UmbLitElement {
return this.#propertyContext.getConfig();
}
/**
* Validation: Validation settings for the property.
*/
@property({ type: Object, attribute: false })
public set validation(validation: UmbPropertyTypeValidationModel | undefined) {
this.#propertyContext.setValidation(validation);
}
public get validation() {
return this.#propertyContext.getValidation();
}
/**
* DataPath, declare the path to the value of the data that this property represents.
* @public
@@ -152,6 +166,9 @@ export class UmbPropertyElement extends UmbLitElement {
@state()
private _orientation: 'horizontal' | 'vertical' = 'horizontal';
@state()
private _mandatory?: boolean;
#propertyContext = new UmbPropertyContext(this);
#controlValidator?: UmbFormControlValidator;
@@ -169,6 +186,7 @@ export class UmbPropertyElement extends UmbLitElement {
},
null,
);
this.observe(
this.#propertyContext.label,
(label) => {
@@ -176,6 +194,7 @@ export class UmbPropertyElement extends UmbLitElement {
},
null,
);
this.observe(
this.#propertyContext.description,
(description) => {
@@ -183,6 +202,7 @@ export class UmbPropertyElement extends UmbLitElement {
},
null,
);
this.observe(
this.#propertyContext.variantDifference,
(variantDifference) => {
@@ -190,6 +210,7 @@ export class UmbPropertyElement extends UmbLitElement {
},
null,
);
this.observe(
this.#propertyContext.appearance,
(appearance) => {
@@ -197,6 +218,14 @@ export class UmbPropertyElement extends UmbLitElement {
},
null,
);
this.observe(
this.#propertyContext.validation,
(validation) => {
this._mandatory = validation?.mandatory;
},
null,
);
}
private _onPropertyEditorChange = (e: CustomEvent): void => {
@@ -297,10 +326,11 @@ export class UmbPropertyElement extends UmbLitElement {
return html`
<umb-property-layout
id="layout"
.alias=${this._alias}
.label=${this._label}
.description=${this._description}
.alias=${this._alias ?? ''}
.label=${this._label ?? ''}
.description=${this._description ?? ''}
.orientation=${this._orientation ?? 'horizontal'}
?mandatory=${this._mandatory}
?invalid=${this._invalid}>
${this.#renderPropertyActionMenu()}
${this._variantDifference