Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Expand Up @@ -29,4 +29,11 @@ export const manifests: Array<ManifestUfmComponent> = [
api: () => import('./link/link.component.js'),
meta: { alias: 'umbLink' },
},
{
type: 'ufmComponent',
alias: 'Umb.Markdown.MemberName',
name: 'Member Name UFM Component',
api: () => import('./member-name/member-name.component.js'),
meta: { alias: 'umbMemberName' },
},
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { UmbUfmComponentBase } from '../ufm-component-base.js';
import type { UfmToken } from '../../plugins/types.js';

import './member-name.element.js';

export class UmbUfmMemberNameComponent extends UmbUfmComponentBase {
render(token: UfmToken) {
if (!token.text) return;

const attributes = super.getAttributes(token.text);
return `<ufm-member-name ${attributes}></ufm-member-name>`;
}
}

export { UmbUfmMemberNameComponent as api };
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { UmbUfmElementBase } from '../ufm-element-base.js';
import { UMB_UFM_RENDER_CONTEXT } from '../ufm-render/ufm-render.context.js';
import { customElement, property } from '@umbraco-cms/backoffice/external/lit';
import { getGuidFromUdi } from '@umbraco-cms/backoffice/utils';
import { UmbId } from '@umbraco-cms/backoffice/id';
import { UmbMemberItemRepository } from '@umbraco-cms/backoffice/member';

@customElement('ufm-member-name')
export class UmbUfmMemberNameElement extends UmbUfmElementBase {
@property()
alias?: string;

#memberRepository?: UmbMemberItemRepository;

constructor() {
super();

this.consumeContext(UMB_UFM_RENDER_CONTEXT, (context) => {
this.observe(
context?.value,
async (value) => {
const temp =
this.alias && typeof value === 'object'
? (value as Record<string, unknown>)[this.alias]
: (value as unknown);

if (!temp) {
this.value = '';
return;
}

const uniques = this.#getUniques(temp);

this.value = await this.#getNames(uniques);
},
'observeValue',
);
});
}

#getUniques(value: unknown): Array<string> {
const entries = Array.isArray(value) ? value : [value];
return entries.flatMap((entry) => this.#normalizeEntry(entry)).filter((x) => x.length > 0);
}

// Returns an array because a single string entry may be comma-separated.
#normalizeEntry(entry: unknown): Array<string> {
if (entry && typeof entry === 'object') {
const unique = (entry as Record<string, unknown>).unique;
return typeof unique === 'string' ? this.#normalizeEntry(unique) : [];
}
if (typeof entry === 'string') {
return entry
.split(',')
.map((token) => this.#extractGuid(token.trim()))
.filter((x): x is string => !!x);
}
return [];
}

#extractGuid(token: string): string | undefined {
if (token.startsWith('umb://')) {
try {
Comment thread
leekelleher marked this conversation as resolved.
const guid = getGuidFromUdi(token);
return UmbId.validate(guid) ? guid : undefined;
} catch {
return undefined;
}
Comment thread
Copilot marked this conversation as resolved.
}
return UmbId.validate(token) ? token : undefined;
}

async #getNames(uniques?: Array<string>) {
if (uniques?.length) {
if (!this.#memberRepository) {
this.#memberRepository = new UmbMemberItemRepository(this);
}

const { data } = await this.#memberRepository.requestItems(uniques);
if (Array.isArray(data) && data.length > 0) {
// TODO: Review usage of `item.variants[0].name` as this needs to be implemented properly for member items [LK]
return data.map((item) => item.variants[0]?.name ?? item.name).join(', ');
}
}

return '';
}
}

export { UmbUfmMemberNameElement as element };

declare global {
interface HTMLElementTagNameMap {
'ufm-member-name': UmbUfmMemberNameElement;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { UmbMarked } from '../contexts/ufm.context.js';
import { UmbUfmContentNameComponent } from '../components/content-name/content-name.component.js';
import { UmbUfmLabelValueComponent } from '../components/label-value/label-value.component.js';
import { UmbUfmLocalizeComponent } from '../components/localize/localize.component.js';
import { UmbUfmMemberNameComponent } from '../components/member-name/member-name.component.js';

describe('UmbMarkedUfm', () => {
describe('UFM parsing', () => {
Expand All @@ -26,6 +27,11 @@ describe('UmbMarkedUfm', () => {
ufm: '{umbContentName: contentPicker}',
expected: '<ufm-content-name alias="contentPicker"></ufm-content-name>',
},
{ ufm: '{umbMemberName:memberPicker}', expected: '<ufm-member-name alias="memberPicker"></ufm-member-name>' },
{
ufm: '{umbMemberName: memberPicker}',
expected: '<ufm-member-name alias="memberPicker"></ufm-member-name>',
},
];

// Manually configuring the UFM components for testing.
Expand All @@ -34,6 +40,7 @@ describe('UmbMarkedUfm', () => {
{ alias: 'umbContentName', marker: '~', render: new UmbUfmContentNameComponent().render },
{ alias: 'umbValue', marker: '=', render: new UmbUfmLabelValueComponent().render },
{ alias: 'umbLocalize', marker: '#', render: new UmbUfmLocalizeComponent().render },
{ alias: 'umbMemberName', render: new UmbUfmMemberNameComponent().render },
]),
);

Expand Down
Loading