Property Editors: Added form control and mandatory support to editors in rich content group(Code editor, Markdown, Block grid) (#20693)

* Added mandatory support for block grid property editor.

* Added form control and mandatory support to code editor.

* Added form control and mandatory support to markdown editor.

---------

Co-authored-by: Niels Lyngsø <niels.lyngso@gmail.com>
This commit is contained in:
Engiber Lozada
2025-11-13 21:57:27 +01:00
committed by GitHub
parent e549217e66
commit 73847d1eff
4 changed files with 72 additions and 10 deletions

View File

@@ -19,7 +19,11 @@ import type {
} from '@umbraco-cms/backoffice/property-editor';
import { jsonStringComparison, observeMultiple } from '@umbraco-cms/backoffice/observable-api';
import { UMB_PROPERTY_CONTEXT, UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
import { UmbFormControlMixin, UmbValidationContext } from '@umbraco-cms/backoffice/validation';
import {
UMB_VALIDATION_EMPTY_LOCALIZATION_KEY,
UmbFormControlMixin,
UmbValidationContext,
} from '@umbraco-cms/backoffice/validation';
import type { UmbBlockTypeGroup } from '@umbraco-cms/backoffice/block-type';
import { debounceTime } from '@umbraco-cms/backoffice/external/rxjs';
@@ -77,6 +81,10 @@ export class UmbPropertyEditorUIBlockGridElement
return this.#readonly;
}
#readonly = false;
@property({ type: Boolean })
mandatory?: boolean;
@property({ type: String })
mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
@state()
private _layoutColumns?: number;
@@ -112,6 +120,16 @@ export class UmbPropertyEditorUIBlockGridElement
constructor() {
super();
this.addValidator(
'valueMissing',
() => this.mandatoryMessage,
() => {
if (!this.mandatory || this.readonly) return false;
const count = this.value?.layout?.[UMB_BLOCK_GRID_PROPERTY_EDITOR_SCHEMA_ALIAS]?.length ?? 0;
return count === 0;
},
);
this.consumeContext(UMB_CONTENT_WORKSPACE_CONTEXT, (context) => {
if (context) {
this.observe(

View File

@@ -9,11 +9,15 @@ import type {
} from '@umbraco-cms/backoffice/property-editor';
import '../components/code-editor.element.js';
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
const elementName = 'umb-property-editor-ui-code-editor';
@customElement(elementName)
export class UmbPropertyEditorUICodeEditorElement extends UmbLitElement implements UmbPropertyEditorUiElement {
export class UmbPropertyEditorUICodeEditorElement
extends UmbFormControlMixin<string, typeof UmbLitElement, undefined>(UmbLitElement)
implements UmbPropertyEditorUiElement
{
#defaultLanguage: CodeEditorLanguage = 'javascript';
@state()
@@ -31,8 +35,9 @@ export class UmbPropertyEditorUICodeEditorElement extends UmbLitElement implemen
@state()
private _wordWrap = false;
@property()
value = '';
mandatory?: boolean;
@property({ type: String })
mandatoryMessage = UMB_VALIDATION_EMPTY_LOCALIZATION_KEY;
@property({ attribute: false })
public set config(config: UmbPropertyEditorConfigCollection | undefined) {
@@ -53,6 +58,16 @@ export class UmbPropertyEditorUICodeEditorElement extends UmbLitElement implemen
this.dispatchEvent(new UmbChangeEvent());
}
constructor() {
super();
this.addValidator(
'valueMissing',
() => this.mandatoryMessage,
() => !!this.mandatory && (!this.value || this.value.length === 0),
);
}
override render() {
return html`
<umb-code-editor

View File

@@ -21,7 +21,7 @@ import { UMB_MEDIA_PICKER_MODAL, UmbMediaUrlRepository } from '@umbraco-cms/back
import { UmbCodeEditorLoadedEvent } from '@umbraco-cms/backoffice/code-editor';
import type { UmbCodeEditorController, UmbCodeEditorElement } from '@umbraco-cms/backoffice/code-editor';
import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
import { UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
import { sanitizeHTML } from '@umbraco-cms/backoffice/utils';
interface UmbMarkdownEditorAction extends monaco.editor.IActionDescriptor {
@@ -63,6 +63,10 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin<string, typeof
this.#editor?.monacoEditor?.updateOptions({ readOnly: this.#readonly });
}
#readonly = false;
@property({ type: Boolean })
required?: boolean;
@property({ type: String })
requiredMessage?: string;
#editor?: UmbCodeEditorController;
@@ -74,6 +78,16 @@ export class UmbInputMarkdownElement extends UmbFormControlMixin<string, typeof
#mediaUrlRepository = new UmbMediaUrlRepository(this);
constructor() {
super();
this.addValidator(
'valueMissing',
() => this.requiredMessage ?? UMB_VALIDATION_EMPTY_LOCALIZATION_KEY,
() => !this.readonly && !!this.required && (this.value === undefined || this.value === null || this.value === ''),
);
}
#onCodeEditorLoaded(event: UmbCodeEditorLoadedEvent) {
if (event.type !== UmbCodeEditorLoadedEvent.TYPE) return;

View File

@@ -9,15 +9,16 @@ import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
import '../../components/input-markdown-editor/index.js';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UMB_VALIDATION_EMPTY_LOCALIZATION_KEY, UmbFormControlMixin } from '@umbraco-cms/backoffice/validation';
/**
* @element umb-property-editor-ui-markdown-editor
*/
@customElement('umb-property-editor-ui-markdown-editor')
export class UmbPropertyEditorUIMarkdownEditorElement extends UmbLitElement implements UmbPropertyEditorUiElement {
@property()
value?: string;
export class UmbPropertyEditorUIMarkdownEditorElement
extends UmbFormControlMixin<string, typeof UmbLitElement>(UmbLitElement)
implements UmbPropertyEditorUiElement
{
/**
* Sets the input to readonly mode, meaning value cannot be changed but still able to read and select its content.
* @type {boolean}
@@ -26,6 +27,14 @@ export class UmbPropertyEditorUIMarkdownEditorElement extends UmbLitElement impl
*/
@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;
@state()
private _preview?: boolean;
@@ -45,6 +54,10 @@ export class UmbPropertyEditorUIMarkdownEditorElement extends UmbLitElement impl
this.dispatchEvent(new UmbChangeEvent());
}
protected override firstUpdated() {
this.addFormControlElement(this.shadowRoot!.querySelector('umb-input-markdown')!);
}
override render() {
return html`
<umb-input-markdown
@@ -52,7 +65,9 @@ export class UmbPropertyEditorUIMarkdownEditorElement extends UmbLitElement impl
.overlaySize=${this._overlaySize}
?preview=${this._preview}
@change=${this.#onChange}
?readonly=${this.readonly}></umb-input-markdown>
?readonly=${this.readonly}
?required=${this.mandatory}
.requiredMessage=${this.mandatoryMessage}></umb-input-markdown>
`;
}
}