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);