diff --git a/src/Umbraco.Web.UI.Client/.github/README.md b/src/Umbraco.Web.UI.Client/.github/README.md
index c8ee810d64..d9f0f72c16 100644
--- a/src/Umbraco.Web.UI.Client/.github/README.md
+++ b/src/Umbraco.Web.UI.Client/.github/README.md
@@ -2,7 +2,9 @@
This is the working repository of the upcoming new Backoffice to Umbraco CMS.
+[](https://github.com/umbraco/Umbraco.CMS.Backoffice/actions/workflows/build_test.yml)
[](https://github.com/umbraco/Umbraco.CMS.Backoffice/actions/workflows/azure-static-web-apps-ambitious-stone-0033b3603.yml)
+[](https://sonarcloud.io/summary/new_code?id=umbraco_Umbraco.CMS.Backoffice)
## Installation instructions
diff --git a/src/Umbraco.Web.UI.Client/sonar-project.properties b/src/Umbraco.Web.UI.Client/sonar-project.properties
new file mode 100644
index 0000000000..44998fae7d
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/sonar-project.properties
@@ -0,0 +1,9 @@
+# Define the same root directory for sources and tests
+sonar.sources = src/
+sonar.tests = src/
+
+# Include test files in the test scope
+sonar.test.inclusions = src/**/*.test.ts
+
+# Exclude test and stories files from the source scope
+sonar.exclusions = src/**/*.test.ts, src/**/*.stories.ts
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/components/table/table.element.ts b/src/Umbraco.Web.UI.Client/src/packages/core/components/table/table.element.ts
index cb2b5cbba9..299d8b0405 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/core/components/table/table.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/core/components/table/table.element.ts
@@ -279,7 +279,7 @@ export class UmbTableElement extends LitElement {
const element = document.createElement('umb-ufm-render') as UmbUfmRenderElement;
element.inline = true;
element.markdown = column.labelTemplate;
- element.value = value;
+ element.value = { value };
return element;
}
diff --git a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/ufm-component.model.ts b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/ufm-component.model.ts
index 9c3a2afa14..9cf61264f4 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/ufm-component.model.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/core/extension-registry/models/ufm-component.model.ts
@@ -1,8 +1,8 @@
import type { ManifestApi, UmbApi } from '@umbraco-cms/backoffice/extension-api';
-import type { Tokens } from '@umbraco-cms/backoffice/external/marked';
+import type { UfmToken } from '@umbraco-cms/backoffice/ufm';
export interface UmbUfmComponentApi extends UmbApi {
- render(token: Tokens.Generic): string | undefined;
+ render(token: UfmToken): string | undefined;
}
export interface MetaUfmComponent {
diff --git a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/grid/document-grid-collection-view.element.ts b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/grid/document-grid-collection-view.element.ts
index d651ea1ce5..5218ca0e54 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/grid/document-grid-collection-view.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/documents/documents/collection/views/grid/document-grid-collection-view.element.ts
@@ -172,26 +172,26 @@ export class UmbDocumentGridCollectionViewElement extends UmbLitElement {
${repeat(
this._userDefinedProperties,
(column) => column.alias,
- (column) => html`
-
- ${column.header}:
- ${when(
- column.nameTemplate,
- () => html`
-
- `,
- () => html`${getPropertyValueByAlias(item, column.alias)}`,
- )}
-
- `,
+ (column) => this.#renderProperty(item, column),
)}
`;
}
+ #renderProperty(item: UmbDocumentCollectionItemModel, column: UmbCollectionColumnConfiguration) {
+ const value = getPropertyValueByAlias(item, column.alias);
+ return html`
+
+ ${column.header}:
+ ${when(
+ column.nameTemplate,
+ () => html``,
+ () => html`${value}`,
+ )}
+
+ `;
+ }
+
static override styles = [
UmbTextStyles,
css`
diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/components/ufm-render/ufm-render.element.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/components/ufm-render/ufm-render.element.ts
index 65f33c51b4..56173caf27 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/ufm/components/ufm-render/ufm-render.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/components/ufm-render/ufm-render.element.ts
@@ -28,7 +28,7 @@ UmbDomPurify.addHook('afterSanitizeAttributes', function (node) {
}
});
-const UmbMarked = new Marked({
+export const UmbMarked = new Marked({
async: true,
gfm: true,
breaks: true,
diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/manifests.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/manifests.ts
index b32b66dddf..ec604e0842 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/ufm/manifests.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/manifests.ts
@@ -4,19 +4,15 @@ export const manifests: Array = [
{
type: 'ufmComponent',
alias: 'Umb.Markdown.LabelValue',
- name: 'Label Value Markdown Component',
+ name: 'Label Value UFM Component',
api: () => import('./ufm-components/label-value.component.js'),
- meta: {
- marker: '=',
- },
+ meta: { marker: '=' },
},
{
type: 'ufmComponent',
alias: 'Umb.Markdown.Localize',
- name: 'Localize Markdown Component',
+ name: 'Localize UFM Component',
api: () => import('./ufm-components/localize.component.js'),
- meta: {
- marker: '#',
- },
+ meta: { marker: '#' },
},
];
diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/plugins/marked-ufm.plugin.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/plugins/marked-ufm.plugin.ts
index 9ed749585d..86c17cc4c9 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/ufm/plugins/marked-ufm.plugin.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/plugins/marked-ufm.plugin.ts
@@ -3,7 +3,11 @@ import type { MarkedExtension, Tokens } from '@umbraco-cms/backoffice/external/m
export interface UfmPlugin {
alias: string;
marker: string;
- render?: (token: Tokens.Generic) => string | undefined;
+ render?: (token: UfmToken) => string | undefined;
+}
+
+export interface UfmToken extends Tokens.Generic {
+ text?: string;
}
export function ufm(plugins: Array = []): MarkedExtension {
@@ -12,13 +16,9 @@ export function ufm(plugins: Array = []): MarkedExtension {
return {
name: alias,
level: 'inline',
- start: (src: string) => {
- const regex = new RegExp(`\\{${marker}`);
- const match = src.match(regex);
- return match ? match.index : -1;
- },
- tokenizer(src: string): Tokens.Generic | undefined {
- const pattern = `^(? src.indexOf(`{${marker}`),
+ tokenizer: (src: string) => {
+ const pattern = `^\\{${marker}([^}]*)\\}`;
const regex = new RegExp(pattern);
const match = src.match(regex);
diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/plugins/marked-ufm.test.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/plugins/marked-ufm.test.ts
new file mode 100644
index 0000000000..ef01944675
--- /dev/null
+++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/plugins/marked-ufm.test.ts
@@ -0,0 +1,32 @@
+import { expect } from '@open-wc/testing';
+import { ufm } from './marked-ufm.plugin.js';
+import { UmbMarked } from '../index.js';
+import { UmbUfmLabelValueComponent } from '../ufm-components/label-value.component.js';
+import { UmbUfmLocalizeComponent } from '../ufm-components/localize.component.js';
+
+describe('UmbMarkedUfm', () => {
+ describe('UFM parsing', () => {
+ const runs = [
+ { ufm: '{=prop1}', expected: '' },
+ { ufm: '{= prop1}', expected: '' },
+ { ufm: '{= prop1 }', expected: '' },
+ { ufm: '{{=prop1}}', expected: '{}' },
+ { ufm: '{#general_add}', expected: '' },
+ ];
+
+ // Manually configuring the UFM components for testing.
+ UmbMarked.use(
+ ufm([
+ { alias: 'Umb.Markdown.LabelValue', marker: '=', render: new UmbUfmLabelValueComponent().render },
+ { alias: 'Umb.Markdown.Localize', marker: '#', render: new UmbUfmLocalizeComponent().render },
+ ]),
+ );
+
+ runs.forEach((run) => {
+ it(`Parsing "${run.ufm}"`, async () => {
+ const markup = await UmbMarked.parseInline(run.ufm);
+ expect(markup).to.equal(run.expected);
+ });
+ });
+ });
+});
diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/label-value.component.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/label-value.component.ts
index 00c58d0027..7fa6b5d815 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/label-value.component.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/label-value.component.ts
@@ -1,10 +1,11 @@
+import type { UfmToken } from '../plugins/marked-ufm.plugin.js';
import { UmbUfmComponentBase } from './ufm-component-base.js';
-import type { Tokens } from '@umbraco-cms/backoffice/external/marked';
import './label-value.element.js';
export class UmbUfmLabelValueComponent extends UmbUfmComponentBase {
- render(token: Tokens.Generic) {
+ render(token: UfmToken) {
+ if (!token.text) return;
return ``;
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/label-value.element.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/label-value.element.ts
index be54d466fc..cb28e01989 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/label-value.element.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/label-value.element.ts
@@ -1,5 +1,5 @@
import { UMB_UFM_RENDER_CONTEXT } from '../components/ufm-render/index.js';
-import { customElement, property, state } from '@umbraco-cms/backoffice/external/lit';
+import { customElement, nothing, property, state } from '@umbraco-cms/backoffice/external/lit';
import { UmbLitElement } from '@umbraco-cms/backoffice/lit-element';
const elementName = 'ufm-label-value';
@@ -31,7 +31,7 @@ export class UmbUfmLabelValueElement extends UmbLitElement {
}
override render() {
- return this._value ?? `{${this.alias}}`;
+ return this._value !== undefined ? this._value : nothing;
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/localize.component.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/localize.component.ts
index aacc2cd503..79890f75f5 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/localize.component.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/localize.component.ts
@@ -1,8 +1,9 @@
+import type { UfmToken } from '../plugins/marked-ufm.plugin.js';
import { UmbUfmComponentBase } from './ufm-component-base.js';
-import type { Tokens } from '@umbraco-cms/backoffice/external/marked';
export class UmbUfmLocalizeComponent extends UmbUfmComponentBase {
- render(token: Tokens.Generic) {
+ render(token: UfmToken) {
+ if (!token.text) return;
return ``;
}
}
diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/ufm-component-base.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/ufm-component-base.ts
index 1109c69e76..71f552363e 100644
--- a/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/ufm-component-base.ts
+++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/ufm-components/ufm-component-base.ts
@@ -1,7 +1,7 @@
-import type { Tokens } from '@umbraco-cms/backoffice/external/marked';
+import type { UfmToken } from '../plugins/marked-ufm.plugin.js';
import type { UmbUfmComponentApi } from '@umbraco-cms/backoffice/extension-registry';
export abstract class UmbUfmComponentBase implements UmbUfmComponentApi {
- abstract render(token: Tokens.Generic): string | undefined;
+ abstract render(token: UfmToken): string | undefined;
destroy() {}
}