Skip to content
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
4001fd8
Add editorAlias to values available in collection view.
AndyButland Nov 5, 2025
e8c2b44
Add property value presentation for collection views of color, date o…
AndyButland Nov 5, 2025
e76564b
Apply to card view.
AndyButland Nov 6, 2025
7f742fc
Renamed enum to match expected convention.
AndyButland Nov 6, 2025
afaef0a
Further linting.
AndyButland Nov 6, 2025
dcef7d4
Apply for other date pickers, amend sizing of color swatch.
AndyButland Nov 6, 2025
efa3c22
Code re-use.
AndyButland Nov 7, 2025
0774248
Merge branch 'main' into v17/feature/collection-view-property-display
AndyButland Nov 7, 2025
05e4ac9
Attempt to improve performance by getting all extension of types once…
AndyButland Nov 7, 2025
3f9ff17
Linting.
AndyButland Nov 7, 2025
89b37f6
Linting.
AndyButland Nov 7, 2025
9d03e33
Linting + adjustments
leekelleher Nov 10, 2025
f2e2c44
Linting + refinements
leekelleher Nov 10, 2025
55a807d
Added importmap for "@umbraco-cms/backoffice/property-value-presentat…
leekelleher Nov 11, 2025
6653c3c
Removed "alias" and "display" from `UmbPropertyValuePresentationBaseE…
leekelleher Nov 11, 2025
808dcaf
Refactored `propertyValuePresentation` extension elements
leekelleher Nov 11, 2025
d9208fd
Renamed `propertyEditorAlias` to `forPropertyEditorSchemaAlias`
leekelleher Nov 11, 2025
4f347a4
Extracted out the system values to "umb-document-table-column-system-…
leekelleher Nov 11, 2025
3b3d3cb
Refactored "umb-document-table-column-property-value" to focus on use…
leekelleher Nov 11, 2025
3eb3670
Refactored "umb-document-grid-collection-card" to remove markup from …
leekelleher Nov 11, 2025
2da794b
Removed `getPropertyValueByAlias` from `UmbDocumentCollectionContext`
leekelleher Nov 11, 2025
0e6691b
Aligned media collection code
leekelleher Nov 11, 2025
05b0d71
Removed unused imports
leekelleher Nov 11, 2025
fd2abf7
Corrected class name (bad code copy)
leekelleher Nov 11, 2025
6f44a72
Merge branch 'main' into v17/feature/collection-view-property-display
AndyButland Nov 12, 2025
ab92e2c
Introduced `UmbDocumentCollectionItemDataResolver` to de-duplicate th…
leekelleher Nov 12, 2025
73c540c
Update src/Umbraco.Web.UI.Client/src/packages/documents/documents/col…
leekelleher Nov 12, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { UmbPropertyValuePresentationBaseElement } from './property-value-presentation-base.js';
export { UmbPropertyValuePresentationDisplayOption } from './property-value-presentation.extension.js';
export type { ManifestPropertyValuePresentation } from './property-value-presentation.extension.js';
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import type { UmbPropertyValuePresentationDisplayOption } from '../../core/property-value-presentation/property-value-presentation.extension.js';

Check failure on line 1 in src/Umbraco.Web.UI.Client/src/packages/core/property-value-presentation/property-value-presentation-base.ts

View workflow job for this annotation

GitHub Actions / build

Use the correct import map alias instead of a relative import path: ../../core/property-value-presentation/property-value-presentation.extension.js
import { property } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';

export abstract class UmbPropertyValuePresentationBaseElement<TValue = string> extends UmbLitElement {
@property()
alias: string = '';

@property()
display?: UmbPropertyValuePresentationDisplayOption;

@property({ type: Object })
value?: TValue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { ManifestElement } from '@umbraco-cms/backoffice/extension-api';

export enum UmbPropertyValuePresentationDisplayOption {
COLLECTION_COLUMN = 'collection-column',
COLLECTION_CARD = 'collection-card',
}

export interface ManifestPropertyValuePresentation extends ManifestElement {
type: 'propertyValuePresentation';
propertyEditorAlias: string;
}

declare global {
interface UmbExtensionManifestMap {
umbPropertyValuePresentation: ManifestPropertyValuePresentation;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,28 @@
import { UmbPropertyValuePresentationDisplayOption } from '../../../core/property-value-presentation/index.js';

Check failure on line 1 in src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/document-collection.context.ts

View workflow job for this annotation

GitHub Actions / build

Use the correct import map alias instead of a relative import path: ../../../core/property-value-presentation/index.js
import type { ManifestPropertyValuePresentation } from '../../../core/property-value-presentation/index.js';

Check failure on line 2 in src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/document-collection.context.ts

View workflow job for this annotation

GitHub Actions / build

Use the correct import map alias instead of a relative import path: ../../../core/property-value-presentation/index.js
import type { UmbDocumentCollectionFilterModel, UmbDocumentCollectionItemModel } from './types.js';
import { UMB_DOCUMENT_TABLE_COLLECTION_VIEW_ALIAS } from './constants.js';
import { html } from '@umbraco-cms/backoffice/external/lit';
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
import { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api';
import { UmbDefaultCollectionContext } from '@umbraco-cms/backoffice/collection';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';
import { UMB_VARIANT_CONTEXT } from '@umbraco-cms/backoffice/variant';
import { UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import { UmbDeprecation } from '@umbraco-cms/backoffice/utils';
import { UmbStringState } from '@umbraco-cms/backoffice/observable-api';
import { UMB_VARIANT_CONTEXT } from '@umbraco-cms/backoffice/variant';
import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api';

export type UmbGetPropertyValueByAliasArgs = {
alias: string;
documentTypeAlias: string;
createDate?: Date;
updateDate?: Date;
state?: string;
culture?: string | null | undefined;
creator?: string | null | undefined;
updater?: string | null | undefined;
sortOrder: number;
values: Array<{ alias: string; editorAlias: string; culture?: string; segment?: string; value: string }>;
};

export class UmbDocumentCollectionContext extends UmbDefaultCollectionContext<
UmbDocumentCollectionItemModel,
Expand All @@ -14,13 +32,17 @@
#displayCulture = new UmbStringState(undefined);
#displayCultureObservable = this.#displayCulture.asObservable();

#propertyValuePresentationManifests: ManifestPropertyValuePresentation[] = [];

constructor(host: UmbControllerHost) {
super(host, UMB_DOCUMENT_TABLE_COLLECTION_VIEW_ALIAS);

this.consumeContext(UMB_VARIANT_CONTEXT, async (variantContext) => {
this.#variantContext = variantContext;
this.#observeDisplayCulture();
});

this.#propertyValuePresentationManifests = umbExtensionsRegistry.getByType('propertyValuePresentation');
}

#observeDisplayCulture() {
Expand Down Expand Up @@ -56,6 +78,56 @@
await this.observe(this.#displayCultureObservable)?.asPromise();
await super._requestCollection();
}

getPropertyValueByAlias(args: UmbGetPropertyValueByAliasArgs) {
const alias = args.alias;
switch (alias) {
case 'contentTypeAlias':
return args.documentTypeAlias;
case 'createDate':
return args.createDate?.toLocaleString();
case 'creator':
case 'owner':
return args.creator;
case 'published':
return args.state !== DocumentVariantStateModel.DRAFT ? 'True' : 'False';
case 'sortOrder':
return args.sortOrder;
case 'updateDate':
return args.updateDate?.toLocaleString();
case 'updater':
return args.updater;
default: {
const prop = args.values.find((x) => x.alias === alias && (!x.culture || x.culture === args.culture));

if (prop) {
const value = prop.value ?? '';
const propertyValuePresentationManifest = this.#getPropertyValuePresentationManifest(prop.editorAlias);
if (propertyValuePresentationManifest.length > 0) {
return html`<umb-extension-slot
type="propertyValuePresentation"
.filter=${(x: ManifestPropertyValuePresentation) => x.propertyEditorAlias === prop.editorAlias}
.props=${{
alias: alias,
value: value,
display: UmbPropertyValuePresentationDisplayOption.COLLECTION_COLUMN,
}}>
</umb-extension-slot>`;
}

return value;
}

return '';
}
}
}

Check warning on line 124 in src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/document-collection.context.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

❌ New issue: Complex Method

UmbDocumentCollectionContext.getPropertyValueByAlias has a cyclomatic complexity of 16, threshold = 9. This function has many conditional statements (e.g. if, for, while), leading to lower code health. Avoid adding more conditionals and code to it without refactoring.

#getPropertyValuePresentationManifest(propertyEditorAlias: string) {
return this.#propertyValuePresentationManifests.filter(
(manifest) => manifest.propertyEditorAlias === propertyEditorAlias,
);
}
}

export { UmbDocumentCollectionContext as api };
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export class UmbDocumentCollectionServerDataSource implements UmbCollectionDataS
flags: item.flags,
values: item.values.map((item) => {
return {
editorAlias: item.editorAlias,
alias: item.alias,
culture: item.culture ?? undefined,
segment: item.segment ?? undefined,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface UmbDocumentCollectionItemModel extends UmbEntityWithFlags {
sortOrder: number;
unique: string;
updater?: string | null;
values: Array<{ alias: string; culture?: string; segment?: string; value: string }>;
values: Array<{ alias: string; editorAlias: string; culture?: string; segment?: string; value: string }>;
variants: Array<UmbDocumentItemVariantModel>;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { UmbDocumentItemDataResolver } from '../../../item/document-item-data-resolver.js';
import { UMB_DOCUMENT_COLLECTION_CONTEXT } from '../../document-collection.context-token.js';
import type { UmbDocumentCollectionContext } from '../../document-collection.context.js';
import type { UmbDocumentCollectionItemModel } from '../../types.js';
import { css, customElement, html, property, repeat, state, when } from '@umbraco-cms/backoffice/external/lit';
import { fromCamelCase } from '@umbraco-cms/backoffice/utils';
Expand All @@ -10,6 +12,8 @@

@customElement('umb-document-grid-collection-card')
export class UmbDocumentGridCollectionCardElement extends UmbElementMixin(UUICardContentNodeElement) {
#collectionContext?: UmbDocumentCollectionContext;

#resolver = new UmbDocumentItemDataResolver(this);

@state()
Expand Down Expand Up @@ -39,39 +43,30 @@
constructor() {
super();

this.consumeContext(UMB_DOCUMENT_COLLECTION_CONTEXT, (instance) => {
this.#collectionContext = instance;
});

this.#resolver.observe(this.#resolver.name, (name) => (this.name = name || ''));
this.#resolver.observe(this.#resolver.state, (state) => (this._state = state || ''));
this.#resolver.observe(this.#resolver.createDate, (createDate) => (this._createDate = createDate));
this.#resolver.observe(this.#resolver.updateDate, (updateDate) => (this._updateDate = updateDate));
}

#getPropertyValueByAlias(alias: string) {
switch (alias) {
case 'contentTypeAlias':
return this.item.documentType.alias;
case 'createDate':
return this._createDate?.toLocaleString();
case 'creator':
case 'owner':
return this.item.creator;
case 'name':
return this.name;
case 'state':
return this._state ? fromCamelCase(this._state) : '';
case 'published':
return this._state !== DocumentVariantStateModel.DRAFT ? 'True' : 'False';
case 'sortOrder':
return this.item.sortOrder;
case 'updateDate':
return this._updateDate?.toLocaleString();
case 'updater':
return this.item.updater;
default: {
const culture = this.#resolver.getCulture();
const prop = this.item.values.find((x) => x.alias === alias && (!x.culture || x.culture === culture));
return prop?.value ?? '';
}
}
const args = {
alias: alias,
documentTypeAlias: this.item.documentType.alias,
createDate: this._createDate,
updateDate: this._updateDate,
state: this._state,
culture: this.#resolver.getCulture(),
creator: this.item.creator,
updater: this.item.updater,
sortOrder: this.item.sortOrder,
values: this.item.values,
};
return this.#collectionContext?.getPropertyValueByAlias(args);

Check notice on line 69 in src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/grid/document-grid-collection-card.element.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

✅ No longer an issue: Complex Method

UmbDocumentGridCollectionCardElement.getPropertyValueByAlias is no longer above the threshold for cyclomatic complexity
}

#getStateTagConfig(): { color: UUIInterfaceColor; label: string } | undefined {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import type { UmbEditableDocumentCollectionItemModel } from '../../../types.js';
import { UmbDocumentItemDataResolver } from '../../../../item/index.js';
import { UMB_DOCUMENT_COLLECTION_CONTEXT } from '../../../document-collection.context-token.js';
import type { UmbDocumentCollectionContext } from '../../../document-collection.context.js';
import type { UmbEditableDocumentCollectionItemModel } from '../../../types.js';
import { customElement, html, nothing, property, state, when } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
import type { UmbTableColumn, UmbTableColumnLayoutElement, UmbTableItem } from '@umbraco-cms/backoffice/components';
import { DocumentVariantStateModel } from '@umbraco-cms/backoffice/external/backend-api';

@customElement('umb-document-table-column-property-value')
export class UmbDocumentTableColumnPropertyValueElement extends UmbLitElement implements UmbTableColumnLayoutElement {
#collectionContext?: UmbDocumentCollectionContext;

#resolver = new UmbDocumentItemDataResolver(this);

@state()
Expand Down Expand Up @@ -37,36 +40,29 @@
constructor() {
super();

this.consumeContext(UMB_DOCUMENT_COLLECTION_CONTEXT, (instance) => {
this.#collectionContext = instance;
});

this.#resolver.observe(this.#resolver.state, (state) => (this._state = state || ''));
this.#resolver.observe(this.#resolver.createDate, (createDate) => (this._createDate = createDate));
this.#resolver.observe(this.#resolver.updateDate, (updateDate) => (this._updateDate = updateDate));
}

#getPropertyValueByAlias() {
const alias = this.column.alias;
const item = this.value.item;
switch (alias) {
case 'contentTypeAlias':
return item.documentType.alias;
case 'createDate':
return this._createDate?.toLocaleString();
case 'creator':
case 'owner':
return item.creator;
case 'published':
return this._state !== DocumentVariantStateModel.DRAFT ? 'True' : 'False';
case 'sortOrder':
return item.sortOrder;
case 'updateDate':
return this._updateDate?.toLocaleString();
case 'updater':
return item.updater;
default: {
const culture = this.#resolver.getCulture();
const prop = item.values.find((x) => x.alias === alias && (!x.culture || x.culture === culture));
return prop?.value ?? '';
}
}
const args = {
alias: this.column.alias,
documentTypeAlias: this.value.item.documentType.alias,
createDate: this._createDate,
updateDate: this._updateDate,
state: this._state,
culture: this.#resolver.getCulture(),
creator: this.value.item.creator,
updater: this.value.item.updater,
sortOrder: this.value.item.sortOrder,
values: this.value.item.values,
};
return this.#collectionContext?.getPropertyValueByAlias(args);

Check notice on line 65 in src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/table/column-layouts/document-table-column-property-value.element.ts

View check run for this annotation

CodeScene Delta Analysis / CodeScene Code Health Review (main)

✅ No longer an issue: Complex Method

UmbDocumentTableColumnPropertyValueElement.getPropertyValueByAlias is no longer above the threshold for cyclomatic complexity
}

override render() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,12 @@ export const manifests: Array<UmbExtensionManifest> = [
supportsReadOnly: true,
},
},
{
type: 'propertyValuePresentation',
alias: 'Umb.PropertyValuePresentation.ColorPicker',
name: 'Color Picker Property Value Presentation',
element: () => import('./property-value-presentation-color-picker.js'),
propertyEditorAlias: 'Umbraco.ColorPicker',
},
schemaManifest,
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import {

Check failure on line 1 in src/Umbraco.Web.UI.Client/src/packages/property-editors/color-picker/property-value-presentation-color-picker.ts

View workflow job for this annotation

GitHub Actions / build

Use the correct import map alias instead of a relative import path: ../../core/property-value-presentation/index.js
UmbPropertyValuePresentationBaseElement,
UmbPropertyValuePresentationDisplayOption,
} from '../../core/property-value-presentation/index.js';
import { customElement, html, nothing } from '@umbraco-cms/backoffice/external/lit';

@customElement('umb-color-picker-property-value-presentation')
export class UmbColorPickerPropertyValuePresentation extends UmbPropertyValuePresentationBaseElement<
{ value: string; label: string } | string
> {
override render() {
const color = this.#getColor();
const label = this.#getLabel();
const size = this.display == UmbPropertyValuePresentationDisplayOption.COLLECTION_CARD ? 10 : 12;
return color
? html`<uui-color-swatch label=${label} value=${color} style="--uui-swatch-size: ${size}px"></uui-color-swatch>`
: nothing;
}

#getColor() {
if (!this.value) {
return null;
}

if (typeof this.value === 'string') {
return this.value;
}

return this.value.value;
}

#getLabel() {
if (!this.value) {
return '';
}

if (typeof this.value === 'string') {
return '';
}

return this.value.label;
}
}

export default UmbColorPickerPropertyValuePresentation;

declare global {
interface HTMLElementTagNameMap {
'umb-color-picker-property-value-presentation': UmbColorPickerPropertyValuePresentation;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,12 @@ export const manifests: Array<UmbExtensionManifest> = [
supportsReadOnly: true,
},
},
{
type: 'propertyValuePresentation',
alias: 'Umb.PropertyValuePresentation.DateOnlyPicker',
name: 'Date Only Picker Property Value Presentation',
element: () => import('./property-value-presentation-date-only-picker.js'),
propertyEditorAlias: 'Umbraco.DateOnly',
},
schemaManifest,
];
Loading
Loading