Skip to content
This repository was archived by the owner on Nov 6, 2025. It is now read-only.

Commit e201800

Browse files
authored
Merge pull request #2148 from umbraco/v14/bugfix/use-property-editor-ui-alias-for-config-options
Use the right property editor UI alias for config options
2 parents e28baec + b26b7ad commit e201800

File tree

6 files changed

+128
-34
lines changed

6 files changed

+128
-34
lines changed

examples/property-editor/README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Property Dataset Dashboard Example
2+
3+
This example is a work in progress example of how to write a property editor.
4+
5+
This example covers a few points:
6+
7+
- Using an existing Property Editor Schema

examples/property-editor/index.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
import type { ManifestPropertyEditorUi } from '@umbraco-cms/backoffice/extension-registry';
2+
3+
export const manifests: Array<ManifestPropertyEditorUi> = [
4+
{
5+
type: 'propertyEditorUi',
6+
alias: 'example.propertyEditorUi.propertyEditor',
7+
name: 'Example Property Editor UI',
8+
element: () => import('./property-editor.js'),
9+
meta: {
10+
label: 'Example Editor',
11+
propertyEditorSchemaAlias: 'Umbraco.ListView',
12+
icon: 'icon-code',
13+
group: 'common',
14+
settings: {
15+
properties: [
16+
{
17+
alias: 'customText',
18+
label: 'Custom text',
19+
propertyEditorUiAlias: 'Umb.PropertyEditorUi.TextBox',
20+
},
21+
],
22+
defaultData: [
23+
{
24+
alias: 'customText',
25+
value: 'Default value',
26+
},
27+
],
28+
},
29+
},
30+
},
31+
];
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
2+
import { html, customElement, LitElement } from '@umbraco-cms/backoffice/external/lit';
3+
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
4+
5+
@customElement('example-property-editor')
6+
export class ExamplePropertyEditor extends UmbElementMixin(LitElement) {
7+
override render() {
8+
return html` <h1 class="uui-h2">Property Editor Example</h1> `;
9+
}
10+
11+
static override styles = [UmbTextStyles];
12+
}
13+
14+
export default ExamplePropertyEditor;
15+
16+
declare global {
17+
interface HTMLElementTagNameMap {
18+
'example-property-editor': ExamplePropertyEditor;
19+
}
20+
}

src/assets/lang/en.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -695,6 +695,7 @@ export default {
695695
hasReferencesDeleteConsequence:
696696
'Deleting <strong>%0%</strong> will delete the properties and their data from the following items',
697697
acceptDeleteConsequence: 'I understand this action will delete the properties and data based on this Data Type',
698+
noConfiguration: 'There is no configuration for this property editor.',
698699
},
699700
errorHandling: {
700701
errorButDataWasSaved:

src/packages/data-type/components/property-editor-config/property-editor-config.element.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import { UmbDataPathPropertyValueFilter } from '@umbraco-cms/backoffice/validati
1212
*/
1313
@customElement('umb-property-editor-config')
1414
export class UmbPropertyEditorConfigElement extends UmbLitElement {
15-
// TODO: Make this element generic, so its not bound to DATA-TYPEs. This will require moving some functionality of Data-Type-Context to this. and this might need to self provide a variant Context for its inner property editor UIs.
15+
// TODO: Make this element generic, so its not bound to DATA-TYPEs. This will require moving some functionality of Data-Type-Context to this. and this might need to self provide a variant Context for its inner property editor UIs. [NL]
1616
#workspaceContext?: typeof UMB_DATA_TYPE_WORKSPACE_CONTEXT.TYPE;
1717

1818
@state()
@@ -53,7 +53,9 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement {
5353
property-editor-ui-alias=${property.propertyEditorUiAlias}
5454
.config=${property.config}></umb-property>`,
5555
)
56-
: html`<div>No configuration</div>`;
56+
: html`<umb-localize key="editdatatype_noConfiguration"
57+
>There is no configuration for this property editor.</umb-localize
58+
>`;
5759
}
5860

5961
static override styles = [UmbTextStyles];

src/packages/data-type/workspace/data-type-workspace.context.ts

Lines changed: 65 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,24 @@ import {
3030
} from '@umbraco-cms/backoffice/entity-action';
3131

3232
type EntityType = UmbDataTypeDetailModel;
33+
34+
/**
35+
* @class UmbDataTypeWorkspaceContext
36+
* @description - Context for handling data type workspace
37+
* There is two overall code flows to be aware about:
38+
*
39+
* propertyEditorUiAlias is observed
40+
* loads propertyEditorUi manifest
41+
* then the propertyEditorSchemaAlias is set to what the UI is configured for.
42+
*
43+
* propertyEditorSchemaAlias is observed
44+
* loads the propertyEditorSchema manifest
45+
* if no UI is defined then the propertyEditorSchema manifest default ui is set for the propertyEditorUiAlias.
46+
*
47+
* This supports two cases:
48+
* - when editing an existing data type that only has a schema alias set, then it gets the UI set.
49+
* - a new property editor ui is picked for a data-type, uses the data-type configuration to set the schema, if such is configured for the Property Editor UI. (The user picks the UI via the UI, the schema comes from the UI that the user picked, we store both on the data-type)
50+
*/
3351
export class UmbDataTypeWorkspaceContext
3452
extends UmbSubmittableWorkspaceContextBase<EntityType>
3553
implements UmbInvariantDatasetWorkspaceContext, UmbRoutableWorkspaceContext
@@ -72,8 +90,6 @@ export class UmbDataTypeWorkspaceContext
7290

7391
#settingsDefaultData?: Array<PropertyEditorSettingsDefaultData>;
7492

75-
#propertyEditorUISettingsSchemaAlias?: string;
76-
7793
#propertyEditorUiIcon = new UmbStringState<string | null>(null);
7894
readonly propertyEditorUiIcon = this.#propertyEditorUiIcon.asObservable();
7995

@@ -82,6 +98,8 @@ export class UmbDataTypeWorkspaceContext
8298

8399
constructor(host: UmbControllerHost) {
84100
super(host, 'Umb.Workspace.DataType');
101+
102+
this.#observePropertyEditorSchemaAlias();
85103
this.#observePropertyEditorUIAlias();
86104

87105
this.routes.setRoutes([
@@ -121,7 +139,7 @@ export class UmbDataTypeWorkspaceContext
121139
this.#propertyEditorUISettingsDefaultData = [];
122140
this.#settingsDefaultData = undefined;
123141

124-
this._mergeConfigProperties();
142+
this.#mergeConfigProperties();
125143
}
126144

127145
// Hold the last set property editor ui alias, so we know when it changes, so we can reset values. [NL]
@@ -131,30 +149,13 @@ export class UmbDataTypeWorkspaceContext
131149
this.observe(
132150
this.propertyEditorUiAlias,
133151
async (propertyEditorUiAlias) => {
134-
const previousPropertyEditorUIAlias = this.#lastPropertyEditorUIAlias;
135-
this.#lastPropertyEditorUIAlias = propertyEditorUiAlias;
152+
this.#propertyEditorUISettingsProperties = [];
153+
this.#propertyEditorUISettingsDefaultData = [];
154+
136155
// we only want to react on the change if the alias is set or null. When it is undefined something is still loading
137156
if (propertyEditorUiAlias === undefined) return;
138157

139-
// if the property editor ui alias is not set, we use the default alias from the schema
140-
if (propertyEditorUiAlias === null) {
141-
await this.#observePropertyEditorSchemaAlias();
142-
if (this.#propertyEditorSchemaConfigDefaultUIAlias !== null) {
143-
this.setPropertyEditorUiAlias(this.#propertyEditorSchemaConfigDefaultUIAlias);
144-
}
145-
} else {
146-
await this.#setPropertyEditorUIConfig(propertyEditorUiAlias);
147-
this.setPropertyEditorSchemaAlias(this.#propertyEditorUISettingsSchemaAlias!);
148-
await this.#observePropertyEditorSchemaAlias();
149-
}
150-
151-
if (
152-
this.getIsNew() ||
153-
(previousPropertyEditorUIAlias && previousPropertyEditorUIAlias !== propertyEditorUiAlias)
154-
) {
155-
this.#transferConfigDefaultData();
156-
}
157-
this._mergeConfigProperties();
158+
this.#observePropertyEditorUIManifest(propertyEditorUiAlias);
158159
},
159160
'editorUiAlias',
160161
);
@@ -164,13 +165,19 @@ export class UmbDataTypeWorkspaceContext
164165
return this.observe(
165166
this.propertyEditorSchemaAlias,
166167
(propertyEditorSchemaAlias) => {
167-
this.#setPropertyEditorSchemaConfig(propertyEditorSchemaAlias);
168+
this.#propertyEditorSchemaSettingsProperties = [];
169+
this.#propertyEditorSchemaSettingsDefaultData = [];
170+
this.#observePropertyEditorSchemaManifest(propertyEditorSchemaAlias);
168171
},
169172
'schemaAlias',
170-
).asPromise();
173+
);
171174
}
172175

173-
#setPropertyEditorSchemaConfig(propertyEditorSchemaAlias?: string) {
176+
#observePropertyEditorSchemaManifest(propertyEditorSchemaAlias?: string) {
177+
if (!propertyEditorSchemaAlias) {
178+
this.removeUmbControllerByAlias('schema');
179+
return;
180+
}
174181
this.observe(
175182
propertyEditorSchemaAlias
176183
? umbExtensionsRegistry.byTypeAndAlias('propertyEditorSchema', propertyEditorSchemaAlias)
@@ -183,36 +190,56 @@ export class UmbDataTypeWorkspaceContext
183190
}));
184191
this.#propertyEditorSchemaSettingsDefaultData = manifest?.meta.settings?.defaultData || [];
185192
this.#propertyEditorSchemaConfigDefaultUIAlias = manifest?.meta.defaultPropertyEditorUiAlias || null;
193+
if (this.#propertyEditorSchemaConfigDefaultUIAlias && this.getPropertyEditorUiAlias() === null) {
194+
// Fallback to the default property editor ui for this property editor schema.
195+
this.setPropertyEditorUiAlias(this.#propertyEditorSchemaConfigDefaultUIAlias);
196+
}
197+
this.#mergeConfigProperties();
186198
},
187199
'schema',
188200
);
189201
}
190202

191-
#setPropertyEditorUIConfig(propertyEditorUIAlias: string) {
192-
return this.observe(
203+
#observePropertyEditorUIManifest(propertyEditorUIAlias: string | null) {
204+
if (!propertyEditorUIAlias) {
205+
this.removeUmbControllerByAlias('editorUi');
206+
return;
207+
}
208+
this.observe(
193209
umbExtensionsRegistry.byTypeAndAlias('propertyEditorUi', propertyEditorUIAlias),
194210
(manifest) => {
195211
this.#propertyEditorUiIcon.setValue(manifest?.meta.icon || null);
196212
this.#propertyEditorUiName.setValue(manifest?.name || null);
197213

198-
this.#propertyEditorUISettingsSchemaAlias = manifest?.meta.propertyEditorSchemaAlias;
199214
// Maps properties to have a weight, so they can be sorted, notice UI properties have a +1000 weight compared to schema properties.
200215
this.#propertyEditorUISettingsProperties = (manifest?.meta.settings?.properties ?? []).map((x, i) => ({
201216
...x,
202217
weight: x.weight ?? 1000 + i,
203218
}));
204219
this.#propertyEditorUISettingsDefaultData = manifest?.meta.settings?.defaultData || [];
220+
this.setPropertyEditorSchemaAlias(manifest?.meta.propertyEditorSchemaAlias);
221+
this.#mergeConfigProperties();
205222
},
206223
'editorUi',
207-
).asPromise();
224+
);
208225
}
209226

210-
private _mergeConfigProperties() {
227+
#mergeConfigProperties() {
211228
if (this.#propertyEditorSchemaSettingsProperties && this.#propertyEditorUISettingsProperties) {
212229
// Reset the value to this array, and then afterwards append:
213230
this.#properties.setValue(this.#propertyEditorSchemaSettingsProperties);
214231
// Append the UI settings properties to the schema properties, so they can override the schema properties:
215232
this.#properties.append(this.#propertyEditorUISettingsProperties);
233+
234+
// If new or if the alias was changed then set default values. This 'complexity' to prevent setting default data when initialized [NL]
235+
const previousPropertyEditorUIAlias = this.#lastPropertyEditorUIAlias;
236+
this.#lastPropertyEditorUIAlias = this.getPropertyEditorUiAlias();
237+
if (
238+
this.getIsNew() ||
239+
(previousPropertyEditorUIAlias && previousPropertyEditorUIAlias !== this.#lastPropertyEditorUIAlias)
240+
) {
241+
this.#transferConfigDefaultData();
242+
}
216243
}
217244
}
218245

@@ -301,9 +328,15 @@ export class UmbDataTypeWorkspaceContext
301328
this.#currentData.update({ name });
302329
}
303330

331+
getPropertyEditorSchemaAlias() {
332+
return this.#currentData.getValue()?.editorAlias;
333+
}
304334
setPropertyEditorSchemaAlias(alias?: string) {
305335
this.#currentData.update({ editorAlias: alias });
306336
}
337+
getPropertyEditorUiAlias() {
338+
return this.#currentData.getValue()?.editorUiAlias;
339+
}
307340
setPropertyEditorUiAlias(alias?: string) {
308341
this.#currentData.update({ editorUiAlias: alias });
309342
}

0 commit comments

Comments
 (0)