From 34cad5ffef568f2752902f84f85d85e232f9d063 Mon Sep 17 00:00:00 2001 From: Jacob Overgaard <752371+iOvergaard@users.noreply.github.com> Date: Wed, 4 Dec 2024 12:28:10 +0100 Subject: [PATCH] fix: Blocks do not work in the rich text editors (#17711) * fix: do not add attributes or html elements to custom elements before they are created Due to the way browsers treat custom elements, they are not allowed to add attributes and/or child elements upon creation. They must wait until the `connectedCallback`. That means any controller that wants to add elements should also wait until the host is connected (`hostConnected`). * test: add generic tests for rte block elements * chore: cleanup imports --- .../block-rte-entry-inline.test.ts | 23 ++++++++++++++ .../block-rte-entry.element.ts | 1 - .../block-rte-entry/block-rte-entry.test.ts | 23 ++++++++++++++ .../ref-rte-block/ref-rte-block.test.ts | 22 +++++++++++++ .../ufm-virtual-render.controller.ts | 31 +++++++++++++------ 5 files changed, 89 insertions(+), 11 deletions(-) create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry-inline.test.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.test.ts create mode 100644 src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.test.ts diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry-inline.test.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry-inline.test.ts new file mode 100644 index 0000000000..c1001d1dfa --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry-inline.test.ts @@ -0,0 +1,23 @@ +import { expect, fixture, html } from '@open-wc/testing'; +import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; +import UmbBlockRteEntryInlineElement from './block-rte-entry-inline.element.js'; + +const blockGuid = '00000000-0000-0000-0000-000000000000'; + +describe('UmbBlockRteEntryInline', () => { + let element: UmbBlockRteEntryInlineElement; + + beforeEach(async () => { + element = await fixture(html``); + }); + + it('is defined with its own instance', () => { + expect(element).to.be.instanceOf(UmbBlockRteEntryInlineElement); + }); + + if ((window as UmbTestRunnerWindow).__UMBRACO_TEST_RUN_A11Y_TEST) { + it('passes the a11y audit', async () => { + await expect(element).to.be.accessible(defaultA11yConfig); + }); + } +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts index 2f325a36bc..3696f7ba05 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.element.ts @@ -21,7 +21,6 @@ import type { UmbExtensionElementInitializer } from '@umbraco-cms/backoffice/ext */ @customElement('umb-rte-block') export class UmbBlockRteEntryElement extends UmbLitElement implements UmbPropertyEditorUiElement { - // @property({ type: String, attribute: 'data-content-key', reflect: true }) public get contentKey(): string | undefined { return this._contentKey; diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.test.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.test.ts new file mode 100644 index 0000000000..ff232731f4 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/block-rte-entry/block-rte-entry.test.ts @@ -0,0 +1,23 @@ +import { expect, fixture, html } from '@open-wc/testing'; +import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; +import UmbBlockRteEntryElement from './block-rte-entry.element.js'; + +const blockGuid = '00000000-0000-0000-0000-000000000000'; + +describe('UmbBlockRteEntry', () => { + let element: UmbBlockRteEntryElement; + + beforeEach(async () => { + element = await fixture(html``); + }); + + it('is defined with its own instance', () => { + expect(element).to.be.instanceOf(UmbBlockRteEntryElement); + }); + + if ((window as UmbTestRunnerWindow).__UMBRACO_TEST_RUN_A11Y_TEST) { + it('passes the a11y audit', async () => { + await expect(element).to.be.accessible(defaultA11yConfig); + }); + } +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.test.ts b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.test.ts new file mode 100644 index 0000000000..f5e4912a58 --- /dev/null +++ b/src/Umbraco.Web.UI.Client/src/packages/block/block-rte/components/ref-rte-block/ref-rte-block.test.ts @@ -0,0 +1,22 @@ +import { expect, fixture, html } from '@open-wc/testing'; + +import { type UmbTestRunnerWindow, defaultA11yConfig } from '@umbraco-cms/internal/test-utils'; +import UmbRefRteBlockElement from './ref-rte-block.element.js'; + +describe('UmbRefRteBlock', () => { + let element: UmbRefRteBlockElement; + + beforeEach(async () => { + element = await fixture(html``); + }); + + it('is defined with its own instance', () => { + expect(element).to.be.instanceOf(UmbRefRteBlockElement); + }); + + if ((window as UmbTestRunnerWindow).__UMBRACO_TEST_RUN_A11Y_TEST) { + it('passes the a11y audit', async () => { + await expect(element).to.be.accessible(defaultA11yConfig); + }); + } +}); diff --git a/src/Umbraco.Web.UI.Client/src/packages/ufm/controllers/ufm-virtual-render.controller.ts b/src/Umbraco.Web.UI.Client/src/packages/ufm/controllers/ufm-virtual-render.controller.ts index c8ead67af2..840be8b92d 100644 --- a/src/Umbraco.Web.UI.Client/src/packages/ufm/controllers/ufm-virtual-render.controller.ts +++ b/src/Umbraco.Web.UI.Client/src/packages/ufm/controllers/ufm-virtual-render.controller.ts @@ -1,12 +1,11 @@ import { UmbUfmRenderElement } from '../components/index.js'; import { UmbControllerBase } from '@umbraco-cms/backoffice/class-api'; -import type { UmbControllerHost } from '@umbraco-cms/backoffice/controller-api'; /** * Renders a UFM */ export class UmbUfmVirtualRenderController extends UmbControllerBase { - #element: UmbUfmRenderElement; + #element?: UmbUfmRenderElement; #getTextFromDescendants(element?: Element | null): string { if (!element) return ''; @@ -31,30 +30,42 @@ export class UmbUfmVirtualRenderController extends UmbControllerBase { } set markdown(markdown: string | undefined) { - this.#element.markdown = markdown; + this.#markdown = markdown; + if (this.#element) { + this.#element.markdown = markdown; + } } get markdown(): string | undefined { - return this.#element.markdown; + return this.#markdown; } + #markdown: string | undefined; set value(value: unknown | undefined) { - this.#element.value = value; + this.#value = value; + if (this.#element) { + this.#element.value = value; + } } get value(): unknown | undefined { - return this.#element.value; + return this.#value; } + #value: unknown | undefined; - constructor(host: UmbControllerHost) { - super(host); - + override hostConnected(): void { const element = new UmbUfmRenderElement(); element.inline = true; element.style.visibility = 'hidden'; + + element.markdown = this.#markdown; + element.value = this.#value; + this.getHostElement().appendChild(element); this.#element = element; } - override hostConnected(): void {} + override hostDisconnected(): void { + this.#element?.remove(); + } override toString(): string { return this.#getTextFromDescendants(this.#element);