Merge remote-tracking branch 'origin/main' into feature/static-file-management

This commit is contained in:
Niels Lyngsø
2023-12-13 14:16:56 +01:00
149 changed files with 1298 additions and 654 deletions

View File

@@ -13,7 +13,9 @@ describe('UmbLogin', () => {
expect(element).to.be.instanceOf(UmbLoginElement); expect(element).to.be.instanceOf(UmbLoginElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -0,0 +1,5 @@
# Property Dataset Dashboard Example
This example demonstrates the essence of the Property Dataset.
This dashboard implements such, to display a few selected Property Editors and bind the data back to the Dashboard.

View File

@@ -0,0 +1,75 @@
import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, customElement, LitElement } from '@umbraco-cms/backoffice/external/lit';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
import { UmbPropertyValueData, type UmbPropertyDatasetElement } from '@umbraco-cms/backoffice/property';
@customElement('example-dataset-dashboard')
export class ExampleDatasetDashboard extends UmbElementMixin(LitElement) {
data: UmbPropertyValueData[] = [
{
alias: 'textProperty',
value: 'Hello',
},
];
#onDataChange(e: Event) {
const oldValue = this.data;
this.data = (e.target as UmbPropertyDatasetElement).value;
this.requestUpdate('data', oldValue);
}
render() {
return html`
<uui-box class="uui-text">
<h1 class="uui-h2" style="margin-top: var(--uui-size-layout-1);">Dataset Example</h1>
<umb-property-dataset .value=${this.data} @change=${this.#onDataChange}>
<umb-property
label="Textual input"
description="Example of text editor"
alias="textProperty"
property-editor-ui-alias="Umb.PropertyEditorUi.TextBox"></umb-property>
<umb-property
label="List of options"
description="Example of dropdown editor"
alias="listProperty"
.config=${[
{
alias: 'multiple',
value: false,
},
{
alias: 'items',
value: {
0: { sortOrder: 1, value: 'First Option' },
1: { sortOrder: 2, value: 'Second Option' },
2: { sortOrder: 3, value: 'Third Option' },
},
},
]}
property-editor-ui-alias="Umb.PropertyEditorUi.Dropdown"></umb-property>
</umb-property-dataset>
<h5 class="uui-h3" style="margin-top: var(--uui-size-layout-1);">Output of dashboard data:</h5>
<code> ${JSON.stringify(this.data, null, 2)} </code>
</uui-box>
`;
}
static styles = [
UmbTextStyles,
css`
:host {
display: block;
padding: var(--uui-size-layout-1);
}
`,
];
}
export default ExampleDatasetDashboard;
declare global {
interface HTMLElementTagNameMap {
'example-dataset-dashboard': ExampleDatasetDashboard;
}
}

View File

@@ -0,0 +1,15 @@
import type { ManifestTypes } from '@umbraco-cms/backoffice/extension-registry';
export const manifests: Array<ManifestTypes> = [
{
type: 'dashboard',
name: 'Example Dataset Workspace View',
alias: 'example.dashboard.dataset',
element: () => import('./dataset-dashboard.js'),
weight: 900,
meta: {
label: 'Dataset example',
pathname: 'dataset-example',
},
},
];

View File

@@ -36,6 +36,7 @@
"./modal": "./dist-cms/packages/core/modal/index.js", "./modal": "./dist-cms/packages/core/modal/index.js",
"./notification": "./dist-cms/packages/core/notification/index.js", "./notification": "./dist-cms/packages/core/notification/index.js",
"./picker-input": "./dist-cms/packages/core/picker-input/index.js", "./picker-input": "./dist-cms/packages/core/picker-input/index.js",
"./property": "./dist-cms/packages/core/property/index.js",
"./property-action": "./dist-cms/packages/core/property-action/index.js", "./property-action": "./dist-cms/packages/core/property-action/index.js",
"./property-editor": "./dist-cms/packages/core/property-editor/index.js", "./property-editor": "./dist-cms/packages/core/property-editor/index.js",
"./section": "./dist-cms/packages/core/section/index.js", "./section": "./dist-cms/packages/core/section/index.js",
@@ -121,6 +122,8 @@
"storybook:build": "npm run wc-analyze && storybook build", "storybook:build": "npm run wc-analyze && storybook build",
"storybook": "npm run wc-analyze && storybook dev -p 6006", "storybook": "npm run wc-analyze && storybook dev -p 6006",
"test:e2e": "npm run auth:test:e2e && npm run backoffice:test:e2e", "test:e2e": "npm run auth:test:e2e && npm run backoffice:test:e2e",
"test:dev": "web-test-runner --config ./web-test-runner.dev.config.mjs",
"test:dev-watch": "web-test-runner --watch --config ./web-test-runner.dev.config.mjs",
"test:watch": "web-test-runner --watch", "test:watch": "web-test-runner --watch",
"test": "web-test-runner --coverage", "test": "web-test-runner --coverage",
"wc-analyze:vscode": "wca **/*.element.ts --format vscode --outFile dist-cms/vscode-html-custom-data.json", "wc-analyze:vscode": "wca **/*.element.ts --format vscode --outFile dist-cms/vscode-html-custom-data.json",

View File

@@ -23,4 +23,4 @@ export class UmbAppContext extends UmbBaseController {
} }
} }
export const UMB_APP_CONTEXT = new UmbContextToken<UmbAppContext>('UMB_APP'); export const UMB_APP_CONTEXT = new UmbContextToken<UmbAppContext>('UmbAppContext');

View File

@@ -15,7 +15,9 @@ describe('UmbInstallerConsentElement', () => {
expect(element).to.be.instanceOf(UmbInstallerConsentElement); expect(element).to.be.instanceOf(UmbInstallerConsentElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbInstallerDatabaseElement', () => {
expect(element).to.be.instanceOf(UmbInstallerDatabaseElement); expect(element).to.be.instanceOf(UmbInstallerDatabaseElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbInstallerErrorElement', () => {
expect(element).to.be.instanceOf(UmbInstallerErrorElement); expect(element).to.be.instanceOf(UmbInstallerErrorElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbInstallerElement', () => {
expect(element).to.be.instanceOf(UmbInstallerElement); expect(element).to.be.instanceOf(UmbInstallerElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbInstallerInstallingElement', () => {
expect(element).to.be.instanceOf(UmbInstallerInstallingElement); expect(element).to.be.instanceOf(UmbInstallerInstallingElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbInstallerLayoutElement', () => {
expect(element).to.be.instanceOf(UmbInstallerLayoutElement); expect(element).to.be.instanceOf(UmbInstallerLayoutElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbInstallerUserElement', () => {
expect(element).to.be.instanceOf(UmbInstallerUserElement); expect(element).to.be.instanceOf(UmbInstallerUserElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -14,7 +14,9 @@ describe('UmbUpgraderView', () => {
expect(element).to.be.instanceOf(UmbUpgraderViewElement); expect(element).to.be.instanceOf(UmbUpgraderViewElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -38,8 +38,11 @@ describe('UmbControllerHostTestElement', () => {
expect(element).to.be.instanceOf(UmbControllerHostProviderElement); expect(element).to.be.instanceOf(UmbControllerHostProviderElement);
}); });
it('provides the context', () => { it('provides the context', async () => {
await Promise.resolve();
// Potentially we need to wait a bit here, cause the value might not be set already? as of the context consumption... // Potentially we need to wait a bit here, cause the value might not be set already? as of the context consumption...
expect(consumer).to.be.instanceOf(UmbTestControllerHostInitializerConsumerElement);
expect(consumer.value).to.not.be.null;
expect(consumer.value).to.equal(contextValue); expect(consumer.value).to.equal(contextValue);
}); });
}); });

View File

@@ -27,9 +27,11 @@ describe('UmbExtensionSlotElement', () => {
/* /*
// This test fails offen on FireFox, there is no real need for this test. So i have chosen to skip it. // This test fails offen on FireFox, there is no real need for this test. So i have chosen to skip it.
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
*/ */
describe('properties', () => { describe('properties', () => {

View File

@@ -12,7 +12,9 @@ describe('UmbInputColorElement', () => {
expect(element).to.be.instanceOf(UmbInputColorElement); expect(element).to.be.instanceOf(UmbInputColorElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -12,7 +12,9 @@ describe('UmbInputDateElement', () => {
expect(element).to.be.instanceOf(UmbInputDateElement); expect(element).to.be.instanceOf(UmbInputDateElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -12,7 +12,9 @@ describe('UmbInputDateElement', () => {
expect(element).to.be.instanceOf(UmbInputDropdownListElement); expect(element).to.be.instanceOf(UmbInputDropdownListElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -12,7 +12,9 @@ describe('UmbInputEyeDropperElement', () => {
expect(element).to.be.instanceOf(UmbInputEyeDropperElement); expect(element).to.be.instanceOf(UmbInputEyeDropperElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -12,7 +12,9 @@ describe('UmbPropertyEditorUINumberRangeElement', () => {
expect(element).to.be.instanceOf(UmbInputNumberRangeElement); expect(element).to.be.instanceOf(UmbInputNumberRangeElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ import { expect, fixture, html } from '@open-wc/testing';
// expect(element).to.be.instanceOf(UmbPickerSectionElement); // expect(element).to.be.instanceOf(UmbPickerSectionElement);
// }); // });
// if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
// it('passes the a11y audit', async () => { // it('passes the a11y audit', async () => {
// await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); // await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
// }); // });
// }
// }); // });

View File

@@ -60,12 +60,12 @@ export class UmbPropertyTypeBasedPropertyElement extends UmbLitElement {
} }
render() { render() {
return html`<umb-workspace-property return html`<umb-property
alias=${ifDefined(this._property?.alias)} alias=${ifDefined(this._property?.alias)}
label=${ifDefined(this._property?.name)} label=${ifDefined(this._property?.name)}
description=${ifDefined(this._property?.description || undefined)} description=${ifDefined(this._property?.description || undefined)}
property-editor-ui-alias=${ifDefined(this._propertyEditorUiAlias)} property-editor-ui-alias=${ifDefined(this._propertyEditorUiAlias)}
.config=${this._dataTypeData}></umb-workspace-property>`; .config=${this._dataTypeData}></umb-property>`;
} }
static styles = [ static styles = [

View File

@@ -1,3 +1,4 @@
import { UMB_PROPERTY_DATASET_CONTEXT, isNameablePropertyDatasetContext } from '@umbraco-cms/backoffice/property';
import { UmbVariantId } from '../../variant/variant-id.class.js'; import { UmbVariantId } from '../../variant/variant-id.class.js';
import { UUIInputElement, UUIInputEvent, UUIPopoverContainerElement } from '@umbraco-cms/backoffice/external/uui'; import { UUIInputElement, UUIInputEvent, UUIPopoverContainerElement } from '@umbraco-cms/backoffice/external/uui';
import { import {
@@ -13,9 +14,7 @@ import {
import { import {
UmbWorkspaceSplitViewContext, UmbWorkspaceSplitViewContext,
UMB_WORKSPACE_SPLIT_VIEW_CONTEXT, UMB_WORKSPACE_SPLIT_VIEW_CONTEXT,
UMB_VARIANT_CONTEXT,
ActiveVariant, ActiveVariant,
isNameablePropertySetContext,
} from '@umbraco-cms/backoffice/workspace'; } from '@umbraco-cms/backoffice/workspace';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { DocumentVariantResponseModel, ContentStateModel } from '@umbraco-cms/backoffice/backend-api'; import { DocumentVariantResponseModel, ContentStateModel } from '@umbraco-cms/backoffice/backend-api';
@@ -38,7 +37,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
} }
#splitViewContext?: UmbWorkspaceSplitViewContext; #splitViewContext?: UmbWorkspaceSplitViewContext;
#variantContext?: typeof UMB_VARIANT_CONTEXT.TYPE; #variantContext?: typeof UMB_PROPERTY_DATASET_CONTEXT.TYPE;
@state() @state()
private _name?: string; private _name?: string;
@@ -66,7 +65,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
this._observeVariants(); this._observeVariants();
this._observeActiveVariants(); this._observeActiveVariants();
}); });
this.consumeContext(UMB_VARIANT_CONTEXT, (instance) => { this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (instance) => {
this.#variantContext = instance; this.#variantContext = instance;
this._observeVariantContext(); this._observeVariantContext();
}); });
@@ -140,7 +139,7 @@ export class UmbVariantSelectorElement extends UmbLitElement {
if ( if (
typeof target?.value === 'string' && typeof target?.value === 'string' &&
this.#variantContext && this.#variantContext &&
isNameablePropertySetContext(this.#variantContext) isNameablePropertyDatasetContext(this.#variantContext)
) { ) {
this.#variantContext.setName(target.value); this.#variantContext.setName(target.value);
} }

View File

@@ -1,9 +1,9 @@
import { html, customElement, state, ifDefined, repeat, nothing } from '@umbraco-cms/backoffice/external/lit'; import { UMB_DATA_TYPE_WORKSPACE_CONTEXT } from '../../workspace/data-type-workspace.context.js';
import { html, customElement, state, ifDefined, repeat } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { PropertyEditorConfigProperty } from '@umbraco-cms/backoffice/extension-registry'; import { PropertyEditorConfigProperty } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UMB_DATA_TYPE_VARIANT_CONTEXT } from '@umbraco-cms/backoffice/data-type';
/** /**
* @element umb-property-editor-config * @element umb-property-editor-config
@@ -13,7 +13,7 @@ import { UMB_DATA_TYPE_VARIANT_CONTEXT } from '@umbraco-cms/backoffice/data-type
@customElement('umb-property-editor-config') @customElement('umb-property-editor-config')
export class UmbPropertyEditorConfigElement extends UmbLitElement { export class UmbPropertyEditorConfigElement extends UmbLitElement {
// 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. // 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.
#variantContext?: typeof UMB_DATA_TYPE_VARIANT_CONTEXT.TYPE; #workspaceContext?: typeof UMB_DATA_TYPE_WORKSPACE_CONTEXT.TYPE;
@state() @state()
private _properties: Array<PropertyEditorConfigProperty> = []; private _properties: Array<PropertyEditorConfigProperty> = [];
@@ -21,16 +21,17 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement {
constructor() { constructor() {
super(); super();
this.consumeContext(UMB_DATA_TYPE_VARIANT_CONTEXT, (instance) => { // This now connects to the workspace, as the variant does not know about the layout details.
this.#variantContext = instance; this.consumeContext(UMB_DATA_TYPE_WORKSPACE_CONTEXT, (instance) => {
this.#workspaceContext = instance;
this.#observeProperties(); this.#observeProperties();
}); });
} }
#observeProperties() { #observeProperties() {
if (!this.#variantContext) return; if (!this.#workspaceContext) return;
this.observe( this.observe(
this.#variantContext.properties, this.#workspaceContext.properties,
(properties) => { (properties) => {
this._properties = properties as Array<PropertyEditorConfigProperty>; this._properties = properties as Array<PropertyEditorConfigProperty>;
}, },
@@ -44,12 +45,12 @@ export class UmbPropertyEditorConfigElement extends UmbLitElement {
this._properties, this._properties,
(property) => property.alias, (property) => property.alias,
(property) => (property) =>
html`<umb-workspace-property html`<umb-property
label="${property.label}" label="${property.label}"
description="${ifDefined(property.description)}" description="${ifDefined(property.description)}"
alias="${property.alias}" alias="${property.alias}"
property-editor-ui-alias="${property.propertyEditorUiAlias}" property-editor-ui-alias="${property.propertyEditorUiAlias}"
.config=${property.config}></umb-workspace-property>`, .config=${property.config}></umb-property>`,
) )
: html`<div>No configuration</div>`; : html`<div>No configuration</div>`;
} }

View File

@@ -2,6 +2,5 @@ import './components/index.js';
export * from './entity.js'; export * from './entity.js';
export * from './repository/index.js'; export * from './repository/index.js';
export * from './variant-context/index.js';
export type { UmbDataTypeDetailModel } from './types.js'; export type { UmbDataTypeDetailModel } from './types.js';

View File

@@ -1,12 +0,0 @@
import type { UmbDataTypeVariantContext } from './data-type-variant-context.js';
import { UmbVariantContext } from '@umbraco-cms/backoffice/workspace';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const isDataTypeVariantContext = (context: UmbVariantContext): context is UmbDataTypeVariantContext =>
'properties' in context && context.getType() === 'data-type';
export const UMB_DATA_TYPE_VARIANT_CONTEXT = new UmbContextToken<UmbVariantContext, UmbDataTypeVariantContext>(
'UmbVariantContext',
undefined,
isDataTypeVariantContext,
);

View File

@@ -1,11 +0,0 @@
import { UmbDataTypeWorkspaceContext } from '../workspace/data-type-workspace.context.js';
import { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbInvariantWorkspaceVariantContext } from '@umbraco-cms/backoffice/workspace';
export class UmbDataTypeVariantContext extends UmbInvariantWorkspaceVariantContext<UmbDataTypeWorkspaceContext> {
properties = this._workspace.properties;
constructor(host: UmbControllerHost, workspace: UmbDataTypeWorkspaceContext) {
super(host, workspace);
}
}

View File

@@ -1,2 +0,0 @@
export * from './data-type-variant-context.token.js';
export * from './data-type-variant-context.js';

View File

@@ -22,7 +22,7 @@ export class UmbDataTypeWorkspaceEditorElement extends UmbLitElement {
this.consumeContext(UMB_DATA_TYPE_WORKSPACE_CONTEXT, (workspaceContext) => { this.consumeContext(UMB_DATA_TYPE_WORKSPACE_CONTEXT, (workspaceContext) => {
this.#workspaceContext = workspaceContext; this.#workspaceContext = workspaceContext;
this.#workspaceContext?.createVariantContext(this); this.#workspaceContext?.createPropertyDatasetContext(this);
this.#observeIsNew(); this.#observeIsNew();
this.#observeName(); this.#observeName();
}); });

View File

@@ -1,10 +1,11 @@
import { UmbPropertyDatasetContext } from '@umbraco-cms/backoffice/property';
import { UmbDataTypeDetailRepository } from '../repository/detail/data-type-detail.repository.js'; import { UmbDataTypeDetailRepository } from '../repository/detail/data-type-detail.repository.js';
import { UmbDataTypeVariantContext } from '../variant-context/data-type-variant-context.js';
import type { UmbDataTypeDetailModel } from '../types.js'; import type { UmbDataTypeDetailModel } from '../types.js';
import { import {
UmbInvariantableWorkspaceContextInterface, UmbInvariantableWorkspaceContextInterface,
UmbEditableWorkspaceContextBase, UmbEditableWorkspaceContextBase,
UmbWorkspaceContextInterface, UmbWorkspaceContextInterface,
UmbInvariantWorkspacePropertyDatasetContext,
} from '@umbraco-cms/backoffice/workspace'; } from '@umbraco-cms/backoffice/workspace';
import { import {
appendToFrozenArray, appendToFrozenArray,
@@ -26,7 +27,6 @@ export class UmbDataTypeWorkspaceContext
extends UmbEditableWorkspaceContextBase<UmbDataTypeDetailRepository, UmbDataTypeDetailModel> extends UmbEditableWorkspaceContextBase<UmbDataTypeDetailRepository, UmbDataTypeDetailModel>
implements UmbInvariantableWorkspaceContextInterface<UmbDataTypeDetailModel | undefined> implements UmbInvariantableWorkspaceContextInterface<UmbDataTypeDetailModel | undefined>
{ {
// TODO: revisit. temp solution because the create and response models are different.
#data = new UmbObjectState<UmbDataTypeDetailModel | undefined>(undefined); #data = new UmbObjectState<UmbDataTypeDetailModel | undefined>(undefined);
readonly data = this.#data.asObservable(); readonly data = this.#data.asObservable();
#getDataPromise?: Promise<any>; #getDataPromise?: Promise<any>;
@@ -146,8 +146,49 @@ export class UmbDataTypeWorkspaceContext
return this._configDefaultData?.find((x) => x.alias === alias)?.value; return this._configDefaultData?.find((x) => x.alias === alias)?.value;
} }
createVariantContext(host: UmbControllerHost): UmbDataTypeVariantContext { createPropertyDatasetContext(host: UmbControllerHost): UmbPropertyDatasetContext {
return new UmbDataTypeVariantContext(host, this); return new UmbInvariantWorkspacePropertyDatasetContext(host, this);
/*
// Example of how this could have been done with the PropertyDatasetBaseContext:
const context = new UmbPropertyDatasetBaseContext(host);
// Observe workspace name:
this.observe(this.name, (name) => {
context.setName(name ?? '');
});
// Observe the variant name:
this.observe(context.name, (name) => {
this.setName(name);
});
this.observe(
this.properties,
(properties) => {
if (properties) {
properties.forEach(async (property) => {
// Observe value of workspace:
this.observe(
await this.propertyValueByAlias(property.alias),
(value) => {
context.setPropertyValue(property.alias, value);
},
'observeWorkspacePropertyOf_' + property.alias,
);
// Observe value of variant:
this.observe(
await context.propertyValueByAlias(property.alias),
(value) => {
this.setPropertyValue(property.alias, value);
},
'observeVariantPropertyOf_' + property.alias,
);
});
}
},
'observePropertyValues',
);
return context;
*/
} }
async load(unique: string) { async load(unique: string) {
@@ -187,7 +228,7 @@ export class UmbDataTypeWorkspaceContext
getName() { getName() {
return this.#data.getValue()?.name; return this.#data.getValue()?.name;
} }
setName(name: string) { setName(name: string | undefined) {
this.#data.update({ name }); this.#data.update({ name });
} }

View File

@@ -91,7 +91,7 @@ export class UmbDataTypeDetailsWorkspaceViewEditElement extends UmbLitElement im
#renderPropertyEditorReference() { #renderPropertyEditorReference() {
return html` return html`
<umb-workspace-property-layout label="Property Editor" description="Select a property editor"> <umb-property-layout label="Property Editor" description="Select a property editor">
${this._propertyEditorUiAlias && this._propertyEditorSchemaAlias ${this._propertyEditorUiAlias && this._propertyEditorSchemaAlias
? html` ? html`
<!-- TODO: border is a bit weird attribute name. Maybe single or standalone would be better? --> <!-- TODO: border is a bit weird attribute name. Maybe single or standalone would be better? -->
@@ -117,7 +117,7 @@ export class UmbDataTypeDetailsWorkspaceViewEditElement extends UmbLitElement im
color="default" color="default"
@click=${this._openPropertyEditorUIPicker}></uui-button> @click=${this._openPropertyEditorUIPicker}></uui-button>
`} `}
</umb-workspace-property-layout> </umb-property-layout>
`; `;
} }

View File

@@ -37,16 +37,16 @@ export class UmbWorkspaceViewDataTypeInfoElement extends UmbLitElement implement
private _renderGeneralInfo() { private _renderGeneralInfo() {
return html` return html`
<uui-box headline="General" style="margin-bottom: 20px;"> <uui-box headline="General" style="margin-bottom: 20px;">
<umb-workspace-property-layout label="Id"> <umb-property-layout label="Id">
<div slot="editor">${this._dataType?.unique}</div> <div slot="editor">${this._dataType?.unique}</div>
</umb-workspace-property-layout> </umb-property-layout>
<umb-workspace-property-layout label="Property Editor Alias"> <umb-property-layout label="Property Editor Alias">
<div slot="editor">${this._dataType?.propertyEditorAlias}</div> <div slot="editor">${this._dataType?.propertyEditorAlias}</div>
</umb-workspace-property-layout> </umb-property-layout>
<umb-workspace-property-layout label="Property Editor UI Alias"> <umb-property-layout label="Property Editor UI Alias">
<div slot="editor">${this._dataType?.propertyEditorUiAlias}</div> <div slot="editor">${this._dataType?.propertyEditorUiAlias}</div>
</umb-workspace-property-layout> </umb-property-layout>
</uui-box> </uui-box>
`; `;
} }

View File

@@ -164,7 +164,7 @@ export class UmbEmbeddedMediaModalElement extends UmbModalBaseElement<
return html` return html`
<umb-body-layout headline="Embed"> <umb-body-layout headline="Embed">
<uui-box> <uui-box>
<umb-workspace-property-layout label="URL" orientation="vertical"> <umb-property-layout label="URL" orientation="vertical">
<div slot="editor"> <div slot="editor">
<uui-input .value=${this._model.url} type="text" @change=${this.#onUrlChange} required="true"> <uui-input .value=${this._model.url} type="text" @change=${this.#onUrlChange} required="true">
<uui-button <uui-button
@@ -176,12 +176,12 @@ export class UmbEmbeddedMediaModalElement extends UmbModalBaseElement<
label="Retrieve"></uui-button> label="Retrieve"></uui-button>
</uui-input> </uui-input>
</div> </div>
</umb-workspace-property-layout> </umb-property-layout>
${when( ${when(
this.#embedResult?.oEmbedStatus === OEmbedStatus.Success || this._model.a11yInfo, this.#embedResult?.oEmbedStatus === OEmbedStatus.Success || this._model.a11yInfo,
() => () =>
html` <umb-workspace-property-layout label="Preview" orientation="vertical"> html` <umb-property-layout label="Preview" orientation="vertical">
<div slot="editor"> <div slot="editor">
${when(this.#loading, () => html`<uui-loader-circle></uui-loader-circle>`)} ${when(this.#loading, () => html`<uui-loader-circle></uui-loader-circle>`)}
${when(this.#embedResult?.markup, () => html`${unsafeHTML(this.#embedResult.markup)}`)} ${when(this.#embedResult?.markup, () => html`${unsafeHTML(this.#embedResult.markup)}`)}
@@ -191,34 +191,34 @@ export class UmbEmbeddedMediaModalElement extends UmbModalBaseElement<
() => html` <p class="sr-only" role="alert">${this._model.a11yInfo}</p>`, () => html` <p class="sr-only" role="alert">${this._model.a11yInfo}</p>`,
)} )}
</div> </div>
</umb-workspace-property-layout>`, </umb-property-layout>`,
)} )}
<umb-workspace-property-layout label="Width" orientation="vertical"> <umb-property-layout label="Width" orientation="vertical">
<uui-input <uui-input
slot="editor" slot="editor"
.value=${this._model.width} .value=${this._model.width}
type="number" type="number"
?disabled=${this.#dimensionControlsDisabled()} ?disabled=${this.#dimensionControlsDisabled()}
@change=${this.#onWidthChange}></uui-input> @change=${this.#onWidthChange}></uui-input>
</umb-workspace-property-layout> </umb-property-layout>
<umb-workspace-property-layout label="Height" orientation="vertical"> <umb-property-layout label="Height" orientation="vertical">
<uui-input <uui-input
slot="editor" slot="editor"
.value=${this._model.height} .value=${this._model.height}
type="number" type="number"
?disabled=${this.#dimensionControlsDisabled()} ?disabled=${this.#dimensionControlsDisabled()}
@change=${this.#onHeightChange}></uui-input> @change=${this.#onHeightChange}></uui-input>
</umb-workspace-property-layout> </umb-property-layout>
<umb-workspace-property-layout label="Constrain" orientation="vertical"> <umb-property-layout label="Constrain" orientation="vertical">
<uui-toggle <uui-toggle
slot="editor" slot="editor"
@change=${this.#onConstrainChange} @change=${this.#onConstrainChange}
?disabled=${this.#dimensionControlsDisabled()} ?disabled=${this.#dimensionControlsDisabled()}
.checked=${this._model.constrain}></uui-toggle> .checked=${this._model.constrain}></uui-toggle>
</umb-workspace-property-layout> </umb-property-layout>
</uui-box> </uui-box>
<uui-button slot="actions" id="cancel" label="Cancel" @click=${this.#handleCancel}>Cancel</uui-button> <uui-button slot="actions" id="cancel" label="Cancel" @click=${this.#handleCancel}>Cancel</uui-button>
@@ -257,11 +257,11 @@ export class UmbEmbeddedMediaModalElement extends UmbModalBaseElement<
width: 1px; width: 1px;
} }
umb-workspace-property-layout:first-child { umb-property-layout:first-child {
padding-top: 0; padding-top: 0;
} }
umb-workspace-property-layout:last-child { umb-property-layout:last-child {
padding-bottom: 0; padding-bottom: 0;
} }

View File

@@ -13,8 +13,10 @@
// expect(element).to.be.instanceOf(UmbIconPickerModalElement); // expect(element).to.be.instanceOf(UmbIconPickerModalElement);
// }); // });
// if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
// // TODO: Reinstate this test when the a11y audit is fixed on uui-color-picker // // TODO: Reinstate this test when the a11y audit is fixed on uui-color-picker
// // it('passes the a11y audit', async () => { // // it('passes the a11y audit', async () => {
// // await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); // // await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
// // }); // // });
// }
// }); // });

View File

@@ -31,9 +31,11 @@ describe('UmbNotificationLayoutDefault', () => {
expect(element).to.be.instanceOf(UmbNotificationLayoutDefaultElement); expect(element).to.be.instanceOf(UmbNotificationLayoutDefaultElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).to.be.accessible(); it('passes the a11y audit', async () => {
}); await expect(element).to.be.accessible();
});
}
describe('Public API', () => { describe('Public API', () => {
describe('properties', () => { describe('properties', () => {

View File

@@ -1,6 +1,6 @@
import { UmbPropertyContext, UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property';
import type { UmbPropertyAction } from '../../shared/property-action/property-action.interface.js'; import type { UmbPropertyAction } from '../../shared/property-action/property-action.interface.js';
import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit'; import { html, customElement, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbWorkspacePropertyContext, UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/workspace';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
@customElement('umb-property-action-clear') @customElement('umb-property-action-clear')
@@ -10,7 +10,7 @@ export class UmbPropertyActionClearElement extends UmbLitElement implements UmbP
// THESE OUT COMMENTED CODE IS USED FOR THE EXAMPLE BELOW, TODO: Should be transferred to some documentation. // THESE OUT COMMENTED CODE IS USED FOR THE EXAMPLE BELOW, TODO: Should be transferred to some documentation.
//private _propertyActionMenuContext?: UmbPropertyActionMenuContext; //private _propertyActionMenuContext?: UmbPropertyActionMenuContext;
private _propertyContext?: UmbWorkspacePropertyContext; private _propertyContext?: UmbPropertyContext;
constructor() { constructor() {
super(); super();
@@ -20,7 +20,7 @@ export class UmbPropertyActionClearElement extends UmbLitElement implements UmbP
this._propertyActionMenuContext = propertyActionsContext; this._propertyActionMenuContext = propertyActionsContext;
}); });
*/ */
this.consumeContext(UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, (propertyContext: UmbWorkspacePropertyContext) => { this.consumeContext(UMB_PROPERTY_CONTEXT, (propertyContext: UmbPropertyContext) => {
this._propertyContext = propertyContext; this._propertyContext = propertyContext;
}); });
} }

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUIBlockGridBlockConfigurationElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockGridBlockConfigurationElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockGridBlockConfigurationElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUIBlockGridGroupConfigurationElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockGridGroupConfigurationElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockGridGroupConfigurationElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUIBlockGridStylesheetPickerElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockGridStylesheetPickerElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockGridStylesheetPickerElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -1,4 +1,4 @@
import { UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN } from '../../../workspace/workspace-property/workspace-property.context.js'; import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property';
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
@@ -33,7 +33,7 @@ export class UmbPropertyEditorUIBlockGridElement extends UmbLitElement implement
constructor() { constructor() {
super(); super();
this.consumeContext(UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, (context) => { this.consumeContext(UMB_PROPERTY_CONTEXT, (context) => {
this.observe(context?.variantId, (propertyVariantId) => { this.observe(context?.variantId, (propertyVariantId) => {
this._variantId = propertyVariantId; this._variantId = propertyVariantId;
this.setupRoutes(); this.setupRoutes();

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIBlockGridElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockGridElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockGridElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -19,7 +19,7 @@ export class UmbPropertyEditorUIBlockListBlockConfigurationElement
public config?: UmbPropertyEditorConfigCollection; public config?: UmbPropertyEditorConfigCollection;
render() { render() {
return html`<div>umb-property-editor-ui-block-list-block-configuration</div>`; return html` <div>umb-property-editor-ui-block-list-block-configuration</div> `;
} }
static styles = [UmbTextStyles]; static styles = [UmbTextStyles];

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUIBlockListBlockConfigurationElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockListBlockConfigurationElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockListBlockConfigurationElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIBlockListElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockListElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIBlockListElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUICheckboxListElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUICheckboxListElement); expect(element).to.be.instanceOf(UmbPropertyEditorUICheckboxListElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUICollectionViewBulkActionPermissionsElement', () =>
expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewBulkActionPermissionsElement); expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewBulkActionPermissionsElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUICollectionViewColumnConfigurationElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewColumnConfigurationElement); expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewColumnConfigurationElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUICollectionViewLayoutConfigurationElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewLayoutConfigurationElement); expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewLayoutConfigurationElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUICollectionViewOrderByElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewOrderByElement); expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewOrderByElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUICollectionViewElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewElement); expect(element).to.be.instanceOf(UmbPropertyEditorUICollectionViewElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIColorPickerElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIColorPickerElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIColorPickerElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -37,7 +37,9 @@ describe('UmbPropertyEditorUIDatePickerElement', () => {
expect(inputElement.type).to.equal('time'); expect(inputElement.type).to.equal('time');
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIDropdownElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIDropdownElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIDropdownElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIEyeDropperElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIEyeDropperElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIEyeDropperElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIIconPickerElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIIconPickerElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIIconPickerElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIImageCropperElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIImageCropperElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIImageCropperElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -1,6 +1,6 @@
import { expect, fixture, html } from '@open-wc/testing'; import { expect, fixture, html } from '@open-wc/testing';
import { UmbPropertyEditorUIImageCropsConfigurationElement } from './property-editor-ui-image-crops-configuration.element.js'; import { UmbPropertyEditorUIImageCropsConfigurationElement } from './property-editor-ui-image-crops-configuration.element.js';
import { defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; //import { defaultA11yConfig } from '@umbraco-cms/internal/test-utils';
describe('UmbPropertyEditorUIImageCropsConfigurationElement', () => { describe('UmbPropertyEditorUIImageCropsConfigurationElement', () => {
let element: UmbPropertyEditorUIImageCropsConfigurationElement; let element: UmbPropertyEditorUIImageCropsConfigurationElement;
@@ -15,8 +15,10 @@ describe('UmbPropertyEditorUIImageCropsConfigurationElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIImageCropsConfigurationElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIImageCropsConfigurationElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
//TODO: This test is broken. It fails at forms because of missing labels even if you have them. it('passes the a11y audit', async () => {
// await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); //TODO: This test is broken. It fails at forms because of missing labels even if you have them.
}); // await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUILabelElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUILabelElement); expect(element).to.be.instanceOf(UmbPropertyEditorUILabelElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIMarkdownEditorElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIMarkdownEditorElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIMarkdownEditorElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIMediaPickerElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIMediaPickerElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIMediaPickerElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUIMemberGroupPickerElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIMemberGroupPickerElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIMemberGroupPickerElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIMemberPickerElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIMemberPickerElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIMemberPickerElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -1,8 +1,8 @@
import { UMB_PROPERTY_CONTEXT } from '@umbraco-cms/backoffice/property';
import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit'; import { html, customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui'; import type { UUIModalSidebarSize } from '@umbraco-cms/backoffice/external/uui';
import { UmbInputMultiUrlElement } from '@umbraco-cms/backoffice/components'; import { UmbInputMultiUrlElement } from '@umbraco-cms/backoffice/components';
import { UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN } from '@umbraco-cms/backoffice/workspace';
import { UmbLinkPickerLink } from '@umbraco-cms/backoffice/modal'; import { UmbLinkPickerLink } from '@umbraco-cms/backoffice/modal';
import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry'; import { UmbPropertyEditorUiElement } from '@umbraco-cms/backoffice/extension-registry';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
@@ -49,7 +49,7 @@ export class UmbPropertyEditorUIMultiUrlPickerElement extends UmbLitElement impl
constructor() { constructor() {
super(); super();
this.consumeContext(UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, (context) => { this.consumeContext(UMB_PROPERTY_CONTEXT, (context) => {
this.observe(context.alias, (alias) => { this.observe(context.alias, (alias) => {
this._alias = alias; this._alias = alias;
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUIMultiUrlPickerElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIMultiUrlPickerElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIMultiUrlPickerElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUIMultipleTextStringElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIMultipleTextStringElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIMultipleTextStringElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUINumberRangeElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUINumberRangeElement); expect(element).to.be.instanceOf(UmbPropertyEditorUINumberRangeElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -1,6 +1,6 @@
import { expect, fixture, html } from '@open-wc/testing'; import { expect, fixture, html } from '@open-wc/testing';
import { UmbPropertyEditorUIOrderDirectionElement } from './property-editor-ui-order-direction.element.js'; import { UmbPropertyEditorUIOrderDirectionElement } from './property-editor-ui-order-direction.element.js';
//import { defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; import { defaultA11yConfig } from '@umbraco-cms/internal/test-utils';
describe('UmbPropertyEditorUIOrderDirectionElement', () => { describe('UmbPropertyEditorUIOrderDirectionElement', () => {
let element: UmbPropertyEditorUIOrderDirectionElement; let element: UmbPropertyEditorUIOrderDirectionElement;
@@ -12,9 +12,10 @@ describe('UmbPropertyEditorUIOrderDirectionElement', () => {
it('is defined with its own instance', () => { it('is defined with its own instance', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIOrderDirectionElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIOrderDirectionElement);
}); });
/*
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
*/ });
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIOverlaySizeElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIOverlaySizeElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIOverlaySizeElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUIRadioButtonListElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIRadioButtonListElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIRadioButtonListElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUISliderElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUISliderElement); expect(element).to.be.instanceOf(UmbPropertyEditorUISliderElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -31,7 +31,9 @@ export class UmbPropertyEditorUITextBoxElement extends UmbLitElement implements
} }
private onChange(e: Event) { private onChange(e: Event) {
this.value = (e.target as HTMLInputElement).value; const newValue = (e.target as HTMLInputElement).value;
if (newValue === this.value) return;
this.value = newValue;
this.dispatchEvent(new CustomEvent('property-value-change')); this.dispatchEvent(new CustomEvent('property-value-change'));
} }
@@ -41,7 +43,7 @@ export class UmbPropertyEditorUITextBoxElement extends UmbLitElement implements
.type=${this._type} .type=${this._type}
inputMode=${ifDefined(this._inputMode)} inputMode=${ifDefined(this._inputMode)}
maxlength=${ifDefined(this._maxChars)} maxlength=${ifDefined(this._maxChars)}
@change=${this.onChange}></uui-input>`; @input=${this.onChange}></uui-input>`;
} }
static styles = [ static styles = [

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUITinyMceDimensionsConfigurationElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUITinyMceDimensionsConfigurationElement); expect(element).to.be.instanceOf(UmbPropertyEditorUITinyMceDimensionsConfigurationElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUITinyMceMaxImSizeConfigurationElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUITinyMceMaxImageSizeConfigurationElement); expect(element).to.be.instanceOf(UmbPropertyEditorUITinyMceMaxImageSizeConfigurationElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUITinyMceStylesheetsConfigurationElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUITinyMceStylesheetsConfigurationElement); expect(element).to.be.instanceOf(UmbPropertyEditorUITinyMceStylesheetsConfigurationElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUITinyMceToolbarConfigurationElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUITinyMceToolbarConfigurationElement); expect(element).to.be.instanceOf(UmbPropertyEditorUITinyMceToolbarConfigurationElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUITinyMceElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUITinyMceElement); expect(element).to.be.instanceOf(UmbPropertyEditorUITinyMceElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIToggleElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIToggleElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIToggleElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -15,7 +15,9 @@ describe('UmbPropertyEditorUITreePickerStartNodeElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUITreePickerStartNodeElement); expect(element).to.be.instanceOf(UmbPropertyEditorUITreePickerStartNodeElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUITreePickerElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUITreePickerElement); expect(element).to.be.instanceOf(UmbPropertyEditorUITreePickerElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIUploadFieldElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIUploadFieldElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIUploadFieldElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIUserPickerElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIUserPickerElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIUserPickerElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -13,7 +13,9 @@ describe('UmbPropertyEditorUIValueTypeElement', () => {
expect(element).to.be.instanceOf(UmbPropertyEditorUIValueTypeElement); expect(element).to.be.instanceOf(UmbPropertyEditorUIValueTypeElement);
}); });
it('passes the a11y audit', async () => { if ((window as any).__UMBRACO_TEST_RUN_A11Y_TEST) {
await expect(element).shadowDom.to.be.accessible(defaultA11yConfig); it('passes the a11y audit', async () => {
}); await expect(element).shadowDom.to.be.accessible(defaultA11yConfig);
});
}
}); });

View File

@@ -0,0 +1,4 @@
export * from './property/index.js';
export * from './property-dataset/index.js';
export * from './property-layout/index.js';
export * from './types/index.js';

View File

@@ -0,0 +1,6 @@
export * from './nameable-property-dataset-context.interface.js';
export * from './nameable-property-dataset-context.token.js';
export * from './property-dataset-base-context.js';
export * from './property-dataset-context.interface.js';
export * from './property-dataset-context.token.js';
export * from './property-dataset.element.js';

View File

@@ -0,0 +1,8 @@
import { UmbPropertyDatasetContext } from './property-dataset-context.interface.js';
/**
* A variant context with ability to set the name of it.
*/
export interface UmbNameablePropertyDatasetContext extends UmbPropertyDatasetContext {
setName(name: string): void;
}

View File

@@ -0,0 +1,13 @@
import { type UmbPropertyDatasetContext } from './property-dataset-context.interface.js';
import { UmbNameablePropertyDatasetContext } from './nameable-property-dataset-context.interface.js';
import { UMB_PROPERTY_DATASET_CONTEXT } from './property-dataset-context.token.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const isNameablePropertyDatasetContext = (
context: UmbPropertyDatasetContext,
): context is UmbNameablePropertyDatasetContext => 'setName' in context;
export const UMB_NAMEABLE_PROPERTY_DATASET_CONTEXT = new UmbContextToken<
UmbPropertyDatasetContext,
UmbNameablePropertyDatasetContext
>(UMB_PROPERTY_DATASET_CONTEXT.toString(), undefined, isNameablePropertyDatasetContext);

View File

@@ -0,0 +1,74 @@
import {
UMB_PROPERTY_DATASET_CONTEXT,
type UmbNameablePropertyDatasetContext,
type UmbPropertyDatasetContext,
} from '@umbraco-cms/backoffice/property';
import type { UmbPropertyValueData } from '../types/property-value-data.type.js';
import { type UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UmbContextBase } from '@umbraco-cms/backoffice/class-api';
import { UmbArrayState, UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
/**
* A base property dataset context implementation.
* @class UmbPropertyDatasetBaseContext
* @extends {UmbContextBase}
*/
export class UmbPropertyDatasetBaseContext
extends UmbContextBase<typeof UMB_PROPERTY_DATASET_CONTEXT.TYPE>
implements UmbPropertyDatasetContext, UmbNameablePropertyDatasetContext
{
#name = new UmbStringState(undefined);
name = this.#name.asObservable();
#values = new UmbArrayState<UmbPropertyValueData>([], (x) => x.alias);
public readonly values = this.#values.asObservable();
private _entityType!: string;
private _unique!: string;
getEntityType() {
return this._entityType;
}
getUnique() {
return this._unique;
}
getName() {
return this.#name.getValue();
}
setName(name: string | undefined) {
this.#name.next(name);
}
getVariantId() {
return UmbVariantId.CreateInvariant();
}
// variant id for a specific property?
constructor(host: UmbControllerHost) {
// The controller alias, is a very generic name cause we want only one of these for this controller host.
super(host, UMB_PROPERTY_DATASET_CONTEXT);
}
/**
* TODO: Write proper JSDocs here.
*/
async propertyValueByAlias<ReturnType = unknown>(propertyAlias: string) {
return this.#values.asObservablePart((values) => {
const valueObj = values.find((x) => x.alias === propertyAlias);
return valueObj ? (valueObj.value as ReturnType) : undefined;
});
}
/**
* TODO: Write proper JSDocs here.
*/
setPropertyValue(alias: string, value: unknown) {
this.#values.appendOne({ alias, value });
}
getValues() {
return this.#values.getValue();
}
setValues(map: Array<UmbPropertyValueData>) {
this.#values.next(map);
}
}

View File

@@ -2,31 +2,34 @@ import type { UmbVariantId } from '../../variant/variant-id.class.js';
import type { Observable } from '@umbraco-cms/backoffice/external/rxjs'; import type { Observable } from '@umbraco-cms/backoffice/external/rxjs';
/** /**
* A variant context, represents a set of properties. * A property dataset context, represents the data of a set of properties.
* This can take form as many, so to list a few: * This can take form as many, so to list a few:
* - A specific variant of content * - A specific variant of content.
* - Content that does not vary * - Content that does not vary.
* - A block. * - A block.
* - A DataType configuration. * - A DataType configuration.
* - A property editor that hosts a set of properties.
* *
* The base type of this holds a Name and some Properties. * The base type of this holds a Name and some Properties.
* Some might be enriches with Variant Info, like culture and segment. * Some might be enriches with Variant Info, like culture and segment.
* Others might have saved publishing status. * Others might have saved publishing status.
* Also setting the name is an additional feature. * Also setting the name is an additional feature.
*/ */
export interface UmbVariantContext { export interface UmbPropertyDatasetContext {
getType(): string; getEntityType(): string;
getUnique(): string | undefined; getUnique(): string | undefined;
//getUniqueName(): string;
getVariantId: () => UmbVariantId; getVariantId: () => UmbVariantId;
getName(): string | undefined; getName(): string | undefined;
readonly name: Observable<string | undefined>; readonly name: Observable<string | undefined>;
// Should it be possible to get the properties as a list of property aliases?
//readonly properties: Observable<Array<string>>;
destroy(): void; destroy(): void;
// Property methods: // Property methods:
propertyVariantId?: (propertyAlias: string) => Promise<Observable<UmbVariantId | undefined>>; propertyVariantId?: (propertyAlias: string) => Promise<Observable<UmbVariantId | undefined>>;
propertyValueByAlias<ReturnType = unknown>(propertyAlias: string): Promise<Observable<ReturnType | undefined>>; propertyValueByAlias<ReturnType = unknown>(propertyAlias: string): Promise<Observable<ReturnType | undefined>>;
setPropertyValue(propertyAlias: string, value: unknown): Promise<void>; setPropertyValue(propertyAlias: string, value: unknown): void;
} }

View File

@@ -0,0 +1,4 @@
import { type UmbPropertyDatasetContext } from './property-dataset-context.interface.js';
import { UmbContextToken } from '@umbraco-cms/backoffice/context-api';
export const UMB_PROPERTY_DATASET_CONTEXT = new UmbContextToken<UmbPropertyDatasetContext>('UmbPropertyDatasetContext');

View File

@@ -0,0 +1,171 @@
import { expect, fixture, oneEvent } from '@open-wc/testing';
import type { UmbPropertyValueData } from '../types/property-value-data.type.js';
import { UMB_PROPERTY_DATASET_CONTEXT } from './property-dataset-context.token.js';
import { UmbPropertyDatasetElement } from './property-dataset.element.js';
import { customElement, html, property, state, LitElement } from '@umbraco-cms/backoffice/external/lit';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
@customElement('test-property-editor')
export class UmbTestPropertyEditorElement extends UmbElementMixin(LitElement) {
//
_datasetContext?: typeof UMB_PROPERTY_DATASET_CONTEXT.TYPE;
@property()
public get alias() {
return this._alias;
}
public set alias(value) {
this._alias = value;
this._observeProperty();
}
_alias?: string;
@state()
_value?: string;
getValue() {
return this._value;
}
setValue(value: string) {
if (this._alias) {
this._datasetContext?.setPropertyValue(this._alias, value);
this.dispatchEvent(new UmbChangeEvent());
}
}
protected async _observeProperty() {
if (!this._datasetContext || !this._alias) return;
this.observe(
await this._datasetContext.propertyValueByAlias(this._alias),
(value) => {
this._value = value as string;
},
'observeValue',
);
}
constructor() {
super();
this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, async (variantContext) => {
this._datasetContext = variantContext;
this._observeProperty();
});
}
}
// Adapter property editor, which tests what happens if a editor sets values of other editors.
@customElement('test-adapter-property-editor')
export class UmbTestAdapterPropertyEditorElement extends UmbTestPropertyEditorElement {
protected async _observeProperty() {
super._observeProperty();
if (!this._datasetContext || !this._alias) return;
this.observe(
await this._datasetContext.propertyValueByAlias(this._alias),
(value) => {
this._datasetContext?.setPropertyValue('testAlias', 'setByAdapter_' + value);
},
'observeValue',
);
}
}
const dataSet: Array<UmbPropertyValueData> = [
{
alias: 'testAlias',
value: 'testValue',
},
];
describe('UmbBasicVariantElement', () => {
describe('Public API', () => {
let datasetElement: UmbPropertyDatasetElement;
beforeEach(async () => {
datasetElement = await fixture(
html`<umb-property-dataset .name=${'hi'} .value=${dataSet}> </umb-property-dataset>`,
);
});
describe('properties', () => {
it('has a value property', () => {
expect(datasetElement).to.have.property('value').to.be.an.a('array');
});
it('has a name property', () => {
expect(datasetElement).to.have.property('name').to.be.an.a('string');
});
});
});
describe('Data bindings', () => {
let datasetElement: UmbPropertyDatasetElement;
let propertyEditor: UmbTestPropertyEditorElement;
beforeEach(async () => {
datasetElement = await fixture(
html`<umb-property-dataset .value=${dataSet}>
<test-property-editor alias="testAlias"></test-property-editor>
</umb-property-dataset>`,
);
propertyEditor = datasetElement.querySelector('test-property-editor') as UmbTestPropertyEditorElement;
});
it('defines with its own instance', () => {
expect(datasetElement).to.be.instanceOf(UmbPropertyDatasetElement);
});
it('provides the value for the property editor to get', () => {
expect(propertyEditor.alias).to.equal('testAlias');
expect(propertyEditor.getValue()).to.equal('testValue');
});
it('property editor sets value on it self', () => {
propertyEditor.setValue('testValue2');
expect(propertyEditor.getValue()).to.equal('testValue2');
});
it('retrieves value set by child', () => {
propertyEditor.setValue('testValue2');
expect(datasetElement.context.getValues()[0].alias).to.equal('testAlias');
expect(datasetElement.context.getValues()[0].value).to.equal('testValue2');
});
it('fires change event', async () => {
const listener = oneEvent(datasetElement, UmbChangeEvent.TYPE);
expect(propertyEditor.alias).to.eq('testAlias');
propertyEditor.setValue('testValue3');
const event = (await listener) as unknown as UmbChangeEvent;
expect(event).to.exist;
expect(event.type).to.eq(UmbChangeEvent.TYPE);
expect(event.target).to.equal(datasetElement);
});
it('does respond to changes triggered internally', async () => {
const adapterPropertyEditor = document.createElement(
'test-adapter-property-editor',
) as UmbTestAdapterPropertyEditorElement;
// We actually do not use the alias of the adapter property editor, but we need to set this to start observing the property value:
adapterPropertyEditor.alias = 'testAdapterAlias';
datasetElement.appendChild(adapterPropertyEditor);
const listener = oneEvent(datasetElement, UmbChangeEvent.TYPE);
// The alias of the original property editor must be 'testAlias' for the adapter to set the value of it.
expect(propertyEditor.alias).to.eq('testAlias');
expect(adapterPropertyEditor.alias).to.eq('testAdapterAlias');
adapterPropertyEditor.setValue('testValue4');
const event = (await listener) as unknown as UmbChangeEvent;
expect(event).to.exist;
expect(event.type).to.eq(UmbChangeEvent.TYPE);
expect(event.target).to.equal(datasetElement);
expect(adapterPropertyEditor.getValue()).to.equal('testValue4');
expect(propertyEditor.getValue()).to.equal('setByAdapter_testValue4');
});
});
});

View File

@@ -0,0 +1,123 @@
import type { UmbPropertyValueData } from '../types/property-value-data.type.js';
import { UmbPropertyDatasetBaseContext } from './property-dataset-base-context.js';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { customElement, html, property } from '@umbraco-cms/backoffice/external/lit';
import { UmbChangeEvent } from '@umbraco-cms/backoffice/event';
/**
* @element umb-property-dataset
* @description - Element for hosting a property dataset. This is needed for umb-property to work.
* @slot default - Slot for rendering content within.
*/
@customElement('umb-property-dataset')
export class UmbPropertyDatasetElement extends UmbLitElement {
// Determine wether state change should fire an event when the value is changed.
#allowChangeEvent = false;
public readonly context: UmbPropertyDatasetBaseContext;
/**
* The value of the dataset.
* @returns {Array<UmbPropertyValueData>}
* @memberof UmbBasicVariantElement
* @example
* ```ts
* const dataSet = [
* {
* alias: 'testAlias',
* value: 'value as a string',
* },
* {
* alias: 'anotherAlias',
* value: 123,
* }
* ]
*
* html`
* <umb-property-dataset .value="${dataSet}">
* <umb-property
* label="My label for this property"
* description="The description to show on the property"
* alias="testAlias"
* property-editor-ui-alias="Umb.PropertyEditorUi.TextBox"
* .config=${...}>
* </umb-property>
* </umb-property-dataset>
* `
* ```
*/
@property({ attribute: false })
public get value(): Array<UmbPropertyValueData> {
return this.context.getValues();
}
public set value(value: Array<UmbPropertyValueData>) {
this.#allowChangeEvent = false;
this.context.setValues(value);
// Above might not trigger a observer callback (if no change), so set the allow change event to true:
this.#allowChangeEvent = true;
}
/**
* The name of the dataset, this name varies depending on the use-case. But this is either
* @type {string}
* @returns {string}
* @memberof UmbBasicVariantElement
* @example
* ```ts
* html`
* <umb-property-dataset name="My variant name">
* ...
* </umb-property-dataset>
* `
*/
@property({ attribute: false })
public get name(): string | undefined {
return this.context.getName();
}
public set name(value: string | undefined) {
this.#allowChangeEvent = false;
this.context.setName(value);
// Above might not trigger a observer callback (if no change), so set the allow change event to true:
this.#allowChangeEvent = true;
}
constructor() {
super();
// Prevent any child events escaping this element.
this.addEventListener('change', (e) => {
if (e.target !== this) {
e.stopImmediatePropagation();
}
});
this.context = new UmbPropertyDatasetBaseContext(this);
// prevent the first change event from firing:
this.#allowChangeEvent = false;
this.observe(this.context.name, this.#observerCallback);
// prevent the first change event from firing:
this.#allowChangeEvent = false;
this.observe(this.context.values, this.#observerCallback);
}
#observerCallback = () => {
if (this.#allowChangeEvent) {
this.dispatchEvent(new UmbChangeEvent());
} else {
// Set allow change event to true.
this.#allowChangeEvent = true;
}
};
render() {
return html`<slot></slot>`;
}
}
export default UmbPropertyDatasetElement;
declare global {
interface HTMLElementTagNameMap {
'umb-property-dataset': UmbPropertyDatasetElement;
}
}

View File

@@ -0,0 +1 @@
export * from './property-layout.element.js';

View File

@@ -2,14 +2,14 @@ import { css, html, LitElement, customElement, property } from '@umbraco-cms/bac
import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
/** /**
* @element umb-workspace-property-layout * @element umb-property-layout
* @description - Element for displaying a property in an workspace. * @description - Element for displaying a property in an workspace.
* @slot editor - Slot for rendering the Property Editor * @slot editor - Slot for rendering the Property Editor
* @slot description - Slot for rendering things below the label. * @slot description - Slot for rendering things below the label.
* @slot property-action-menu - Slot for rendering the Property Action Menu * @slot action-menu - Slot for rendering the Property Action Menu
*/ */
@customElement('umb-workspace-property-layout') @customElement('umb-property-layout')
export class UmbWorkspacePropertyLayoutElement extends LitElement { export class UmbPropertyLayoutElement extends LitElement {
/** /**
* Alias. The technical name of the property. * Alias. The technical name of the property.
* @type {string} * @type {string}
@@ -52,7 +52,7 @@ export class UmbWorkspacePropertyLayoutElement extends LitElement {
return html` return html`
<div id="headerColumn"> <div id="headerColumn">
<uui-label title=${this.alias}>${this.label}</uui-label> <uui-label title=${this.alias}>${this.label}</uui-label>
<slot name="property-action-menu"></slot> <slot name="action-menu"></slot>
<div id="description">${this.description}</div> <div id="description">${this.description}</div>
<slot name="description"></slot> <slot name="description"></slot>
</div> </div>
@@ -117,6 +117,6 @@ export class UmbWorkspacePropertyLayoutElement extends LitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
'umb-workspace-property-layout': UmbWorkspacePropertyLayoutElement; 'umb-property-layout': UmbPropertyLayoutElement;
} }
} }

View File

@@ -0,0 +1,19 @@
import { Meta, Story } from '@storybook/web-components';
import type { UmbPropertyLayoutElement } from './property-layout.element.js';
import { html } from '@umbraco-cms/backoffice/external/lit';
import './property-layout.element.js';
export default {
title: 'Workspaces/Property Layout',
component: 'umb-property-layout',
id: 'umb-property-layout',
} as Meta;
export const AAAOverview: Story<UmbPropertyLayoutElement> = () =>
html` <umb-property-layout label="Label" description="Description">
<div slot="action-menu"><uui-button color="" look="placeholder">Action Menu</uui-button></div>
<div slot="editor"><uui-button color="" look="placeholder">Editor</uui-button></div>
</umb-property-layout>`;
AAAOverview.storyName = 'Overview';

View File

@@ -0,0 +1,2 @@
export * from './property.context.js';
export * from './property.element.js';

View File

@@ -1,20 +1,20 @@
import { UMB_PROPERTY_DATASET_CONTEXT } from '@umbraco-cms/backoffice/property';
import { UmbPropertyEditorUiElement } from '../../extension-registry/interfaces/property-editor-ui-element.interface.js'; import { UmbPropertyEditorUiElement } from '../../extension-registry/interfaces/property-editor-ui-element.interface.js';
import { type WorkspacePropertyData } from '../types/workspace-property-data.type.js'; import { type WorkspacePropertyData } from '../../workspace/types/workspace-property-data.type.js';
import { UMB_VARIANT_CONTEXT } from '@umbraco-cms/backoffice/workspace';
import { UmbVariantId } from '@umbraco-cms/backoffice/variant'; import { UmbVariantId } from '@umbraco-cms/backoffice/variant';
import { type UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api'; import { type UmbControllerHostElement } from '@umbraco-cms/backoffice/controller-api';
import { UmbBaseController } from '@umbraco-cms/backoffice/class-api'; import { UmbBaseController } from '@umbraco-cms/backoffice/class-api';
import { import {
UmbBasicState,
UmbClassState, UmbClassState,
UmbObjectState, UmbObjectState,
UmbStringState,
UmbObserverController, UmbObserverController,
UmbBasicState, UmbStringState,
} from '@umbraco-cms/backoffice/observable-api'; } from '@umbraco-cms/backoffice/observable-api';
import { UmbContextProviderController, UmbContextToken } from '@umbraco-cms/backoffice/context-api'; import { UmbContextProviderController, UmbContextToken } from '@umbraco-cms/backoffice/context-api';
import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor';
export class UmbWorkspacePropertyContext<ValueType = any> extends UmbBaseController { export class UmbPropertyContext<ValueType = any> extends UmbBaseController {
private _providerController: UmbContextProviderController; private _providerController: UmbContextProviderController;
#data = new UmbObjectState<WorkspacePropertyData<ValueType>>({}); #data = new UmbObjectState<WorkspacePropertyData<ValueType>>({});
@@ -44,13 +44,13 @@ export class UmbWorkspacePropertyContext<ValueType = any> extends UmbBaseControl
private _variantDifference = new UmbStringState(undefined); private _variantDifference = new UmbStringState(undefined);
public readonly variantDifference = this._variantDifference.asObservable(); public readonly variantDifference = this._variantDifference.asObservable();
#variantContext?: typeof UMB_VARIANT_CONTEXT.TYPE; #datasetContext?: typeof UMB_PROPERTY_DATASET_CONTEXT.TYPE;
constructor(host: UmbControllerHostElement) { constructor(host: UmbControllerHostElement) {
super(host); super(host);
this.consumeContext(UMB_VARIANT_CONTEXT, (variantContext) => { this.consumeContext(UMB_PROPERTY_DATASET_CONTEXT, (variantContext) => {
this.#variantContext = variantContext; this.#datasetContext = variantContext;
this._generateVariantDifferenceString(); this._generateVariantDifferenceString();
this._observeProperty(); this._observeProperty();
}); });
@@ -59,7 +59,7 @@ export class UmbWorkspacePropertyContext<ValueType = any> extends UmbBaseControl
this._observeProperty(); this._observeProperty();
}); });
this._providerController = new UmbContextProviderController(host, UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN, this); this._providerController = new UmbContextProviderController(host, UMB_PROPERTY_CONTEXT, this);
this.observe(this.configValues, (configValues) => { this.observe(this.configValues, (configValues) => {
this.#configCollection.next(configValues ? new UmbPropertyEditorConfigCollection(configValues) : undefined); this.#configCollection.next(configValues ? new UmbPropertyEditorConfigCollection(configValues) : undefined);
@@ -74,9 +74,9 @@ export class UmbWorkspacePropertyContext<ValueType = any> extends UmbBaseControl
private _observePropertyValue?: UmbObserverController<ValueType | undefined>; private _observePropertyValue?: UmbObserverController<ValueType | undefined>;
private async _observeProperty() { private async _observeProperty() {
const alias = this.#data.getValue().alias; const alias = this.#data.getValue().alias;
if (!this.#variantContext || !alias) return; if (!this.#datasetContext || !alias) return;
const variantIdSubject = (await this.#variantContext.propertyVariantId?.(alias)) ?? undefined; const variantIdSubject = (await this.#datasetContext.propertyVariantId?.(alias)) ?? undefined;
this._observePropertyVariant?.destroy(); this._observePropertyVariant?.destroy();
if (variantIdSubject) { if (variantIdSubject) {
this._observePropertyVariant = this.observe(variantIdSubject, (variantId) => { this._observePropertyVariant = this.observe(variantIdSubject, (variantId) => {
@@ -85,7 +85,7 @@ export class UmbWorkspacePropertyContext<ValueType = any> extends UmbBaseControl
} }
// TODO: Verify if we need to optimize runtime by parsing the propertyVariantID, cause this method retrieves it again: // TODO: Verify if we need to optimize runtime by parsing the propertyVariantID, cause this method retrieves it again:
const subject = await this.#variantContext.propertyValueByAlias<ValueType>(alias); const subject = await this.#datasetContext.propertyValueByAlias<ValueType>(alias);
this._observePropertyValue?.destroy(); this._observePropertyValue?.destroy();
if (subject) { if (subject) {
@@ -97,8 +97,8 @@ export class UmbWorkspacePropertyContext<ValueType = any> extends UmbBaseControl
} }
private _generateVariantDifferenceString() { private _generateVariantDifferenceString() {
if (!this.#variantContext) return; if (!this.#datasetContext) return;
const contextVariantId = this.#variantContext.getVariantId?.() ?? undefined; const contextVariantId = this.#datasetContext.getVariantId?.() ?? undefined;
this._variantDifference.next( this._variantDifference.next(
contextVariantId ? this.#variantId.getValue()?.toDifferencesString(contextVariantId) : '', contextVariantId ? this.#variantId.getValue()?.toDifferencesString(contextVariantId) : '',
); );
@@ -115,9 +115,8 @@ export class UmbWorkspacePropertyContext<ValueType = any> extends UmbBaseControl
} }
public setValue(value: WorkspacePropertyData<ValueType>['value']) { public setValue(value: WorkspacePropertyData<ValueType>['value']) {
const alias = this.#data.getValue().alias; const alias = this.#data.getValue().alias;
if (!this.#variantContext || !alias) return; if (!this.#datasetContext || !alias) return;
this.#datasetContext?.setPropertyValue(alias, value);
this.#variantContext?.setPropertyValue(alias, value);
} }
public setConfig(config: WorkspacePropertyData<ValueType>['config'] | undefined) { public setConfig(config: WorkspacePropertyData<ValueType>['config'] | undefined) {
this.#data.update({ config }); this.#data.update({ config });
@@ -139,6 +138,4 @@ export class UmbWorkspacePropertyContext<ValueType = any> extends UmbBaseControl
} }
} }
export const UMB_WORKSPACE_PROPERTY_CONTEXT_TOKEN = new UmbContextToken<UmbWorkspacePropertyContext>( export const UMB_PROPERTY_CONTEXT = new UmbContextToken<UmbPropertyContext>('UmbPropertyContext');
'UmbWorkspacePropertyContext',
);

View File

@@ -1,21 +1,24 @@
import { type UmbPropertyEditorConfig } from '../../property-editor/index.js'; import { UmbPropertyContext } from './property.context.js';
import { UmbWorkspacePropertyContext } from './workspace-property.context.js';
import { UmbTextStyles } from '@umbraco-cms/backoffice/style'; import { UmbTextStyles } from '@umbraco-cms/backoffice/style';
import { css, html, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit'; import { css, html, customElement, property, state, ifDefined } from '@umbraco-cms/backoffice/external/lit';
import { createExtensionElement } from '@umbraco-cms/backoffice/extension-api'; import { createExtensionElement } from '@umbraco-cms/backoffice/extension-api';
import { ManifestPropertyEditorUi, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry'; import { ManifestPropertyEditorUi, umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api'; import { UmbObserverController } from '@umbraco-cms/backoffice/observable-api';
import { UmbLitElement } from '@umbraco-cms/internal/lit-element'; import { UmbLitElement } from '@umbraco-cms/internal/lit-element';
import { UmbPropertyEditorConfigCollection } from '@umbraco-cms/backoffice/property-editor'; import {
UmbPropertyEditorConfigCollection,
type UmbPropertyEditorConfig,
} from '@umbraco-cms/backoffice/property-editor';
/** /**
* @element umb-workspace-property * @element umb-property
* @description - Component for displaying a entity property. The Element will render a Property Editor based on the Property Editor UI alias passed to the element. * @description Component for displaying a property with editor from extension registry.
* The element will also render all Property Actions related to the Property Editor. * The Element will render a Property Editor based on the Property Editor UI alias passed to the element.
* This will also render all Property Actions related to the Property Editor UI Alias.
*/ */
@customElement('umb-workspace-property') @customElement('umb-property')
export class UmbWorkspacePropertyElement extends UmbLitElement { export class UmbPropertyElement extends UmbLitElement {
/** /**
* Label. Name of the property * Label. Name of the property
* @type {string} * @type {string}
@@ -24,7 +27,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
*/ */
@property({ type: String }) @property({ type: String })
public set label(label: string) { public set label(label: string) {
this._propertyContext.setLabel(label); this.#propertyContext.setLabel(label);
} }
/** /**
@@ -35,7 +38,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
*/ */
@property({ type: String }) @property({ type: String })
public set description(description: string) { public set description(description: string) {
this._propertyContext.setDescription(description); this.#propertyContext.setDescription(description);
} }
/** /**
@@ -47,7 +50,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
*/ */
@property({ type: String }) @property({ type: String })
public set alias(alias: string) { public set alias(alias: string) {
this._propertyContext.setAlias(alias); this.#propertyContext.setAlias(alias);
} }
/** /**
@@ -57,13 +60,13 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
* @attr * @attr
* @default '' * @default ''
*/ */
private _propertyEditorUiAlias = '';
@property({ type: String, attribute: 'property-editor-ui-alias' }) @property({ type: String, attribute: 'property-editor-ui-alias' })
public set propertyEditorUiAlias(value: string) { public set propertyEditorUiAlias(value: string) {
if (this._propertyEditorUiAlias === value) return; if (this._propertyEditorUiAlias === value) return;
this._propertyEditorUiAlias = value; this._propertyEditorUiAlias = value;
this._observePropertyEditorUI(); this._observePropertyEditorUI();
} }
private _propertyEditorUiAlias = '';
/** /**
* Config. Configuration to pass to the Property Editor UI. This is also the configuration data stored on the Data Type. * Config. Configuration to pass to the Property Editor UI. This is also the configuration data stored on the Data Type.
@@ -74,7 +77,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
*/ */
@property({ type: Array, attribute: false }) @property({ type: Array, attribute: false })
public set config(value: UmbPropertyEditorConfig | undefined) { public set config(value: UmbPropertyEditorConfig | undefined) {
this._propertyContext.setConfig(value); this.#propertyContext.setConfig(value);
} }
@state() @state()
@@ -95,24 +98,24 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
@state() @state()
private _description?: string; private _description?: string;
private _propertyContext = new UmbWorkspacePropertyContext(this); #propertyContext = new UmbPropertyContext(this);
private _valueObserver?: UmbObserverController<unknown>; #valueObserver?: UmbObserverController<unknown>;
private _configObserver?: UmbObserverController<UmbPropertyEditorConfigCollection | undefined>; #configObserver?: UmbObserverController<UmbPropertyEditorConfigCollection | undefined>;
constructor() { constructor() {
super(); super();
this.observe(this._propertyContext.alias, (alias) => { this.observe(this.#propertyContext.alias, (alias) => {
this._alias = alias; this._alias = alias;
}); });
this.observe(this._propertyContext.label, (label) => { this.observe(this.#propertyContext.label, (label) => {
this._label = label; this._label = label;
}); });
this.observe(this._propertyContext.description, (description) => { this.observe(this.#propertyContext.description, (description) => {
this._description = description; this._description = description;
}); });
this.observe(this._propertyContext.variantDifference, (variantDifference) => { this.observe(this.#propertyContext.variantDifference, (variantDifference) => {
this._variantDifference = variantDifference; this._variantDifference = variantDifference;
}); });
} }
@@ -121,7 +124,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
const target = e.composedPath()[0] as any; const target = e.composedPath()[0] as any;
//this.value = target.value; // Sets value in context. //this.value = target.value; // Sets value in context.
this._propertyContext.setValue(target.value); this.#propertyContext.setValue(target.value);
e.stopPropagation(); e.stopPropagation();
}; };
@@ -136,7 +139,7 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
} }
private async _gotEditorUI(manifest?: ManifestPropertyEditorUi | null) { private async _gotEditorUI(manifest?: ManifestPropertyEditorUi | null) {
this._propertyContext.setEditor(undefined); this.#propertyContext.setEditor(undefined);
if (!manifest) { if (!manifest) {
// TODO: if propertyEditorUiAlias didn't exist in store, we should do some nice fail UI. // TODO: if propertyEditorUiAlias didn't exist in store, we should do some nice fail UI.
@@ -144,49 +147,44 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
} }
const el = await createExtensionElement(manifest); const el = await createExtensionElement(manifest);
if (el) {
const oldValue = this._element;
oldValue?.removeEventListener('change', this._onPropertyEditorChange as any as EventListener); if (el) {
const oldElement = this._element;
// cleanup:
this.#valueObserver?.destroy();
this.#configObserver?.destroy();
oldElement?.removeEventListener('property-value-change', this._onPropertyEditorChange as any as EventListener);
this._element = el as ManifestPropertyEditorUi['ELEMENT_TYPE']; this._element = el as ManifestPropertyEditorUi['ELEMENT_TYPE'];
this._propertyContext.setEditor(this._element); this.#propertyContext.setEditor(this._element);
this._valueObserver?.destroy();
this._configObserver?.destroy();
if (this._element) { if (this._element) {
// TODO: Could this be changed to change event? (or additionally support change?)
this._element.addEventListener('property-value-change', this._onPropertyEditorChange as any as EventListener); this._element.addEventListener('property-value-change', this._onPropertyEditorChange as any as EventListener);
this._valueObserver = this.observe( // No need for a controller alias, as the clean is handled via the observer prop:
this._propertyContext.value, this.#valueObserver = this.observe(this.#propertyContext.value, (value) => {
(value) => { this._value = value;
this._value = value; if (this._element) {
if (this._element) { this._element.value = value;
this._element.value = value; }
} });
}, this.#configObserver = this.observe(this.#propertyContext.config, (config) => {
'_observePropertyValue', if (this._element && config) {
); this._element.config = config;
this._configObserver = this.observe( }
this._propertyContext.config, });
(config) => {
if (this._element && config) {
this._element.config = config;
}
},
'_observePropertyConfig',
);
} }
this.requestUpdate('element', oldValue); this.requestUpdate('element', oldElement);
} }
} }
render() { render() {
return html` return html`
<umb-workspace-property-layout <umb-property-layout
id="layout" id="layout"
alias="${ifDefined(this._alias)}" alias="${ifDefined(this._alias)}"
label="${ifDefined(this._label)}" label="${ifDefined(this._label)}"
@@ -196,15 +194,15 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
? html`<uui-tag look="secondary" slot="description">${this._variantDifference}</uui-tag>` ? html`<uui-tag look="secondary" slot="description">${this._variantDifference}</uui-tag>`
: ''} : ''}
<div slot="editor">${this._element}</div> <div slot="editor">${this._element}</div>
</umb-workspace-property-layout> </umb-property-layout>
`; `;
} }
private _renderPropertyActionMenu() { private _renderPropertyActionMenu() {
return html`${this._propertyEditorUiAlias return html`${this._propertyEditorUiAlias
? html`<umb-property-action-menu ? html`<umb-property-action-menu
slot="property-action-menu" slot="action-menu"
id="property-action-menu" id="action-menu"
.propertyEditorUiAlias=${this._propertyEditorUiAlias} .propertyEditorUiAlias=${this._propertyEditorUiAlias}
.value=${this._value}></umb-property-action-menu>` .value=${this._value}></umb-property-action-menu>`
: ''}`; : ''}`;
@@ -221,13 +219,13 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
color: var(--uui-color-text-alt); color: var(--uui-color-text-alt);
} }
#property-action-menu { #action-menu {
opacity: 0; opacity: 0;
} }
#layout:focus-within #property-action-menu, #layout:focus-within #action-menu,
#layout:hover #property-action-menu, #layout:hover #action-menu,
#property-action-menu[open] { #action-menu[open] {
opacity: 1; opacity: 1;
} }
@@ -240,6 +238,6 @@ export class UmbWorkspacePropertyElement extends UmbLitElement {
declare global { declare global {
interface HTMLElementTagNameMap { interface HTMLElementTagNameMap {
'umb-workspace-property': UmbWorkspacePropertyElement; 'umb-property': UmbPropertyElement;
} }
} }

View File

@@ -0,0 +1,20 @@
import { Meta, Story } from '@storybook/web-components';
import type { UmbPropertyElement } from './property.element.js';
import { html } from '@umbraco-cms/backoffice/external/lit';
import './property.element.js';
export default {
title: 'Components/Property',
component: 'umb-property',
id: 'umb-property',
} as Meta;
export const AAAOverview: Story<UmbPropertyElement> = () =>
html` <umb-property
label="Property"
description="Description"
alias="textProperty"
property-editor-ui-alias="Umb.PropertyEditorUi.TextBox"
.value="${'Hello'}"></umb-property>`;
AAAOverview.storyName = 'Overview';

View File

@@ -0,0 +1 @@
export * from './property-value-data.type.js';

Some files were not shown because too many files have changed in this diff Show More