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
This commit is contained in:
Jacob Overgaard
2024-12-04 12:28:10 +01:00
committed by GitHub
parent e8d46340ac
commit 34cad5ffef
5 changed files with 89 additions and 11 deletions

View File

@@ -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`<umb-rte-block-inline .contentKey=${blockGuid}></umb-rte-block-inline>`);
});
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);
});
}
});

View File

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

View File

@@ -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`<umb-rte-block .contentKey=${blockGuid}></umb-rte-block>`);
});
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);
});
}
});

View File

@@ -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`<umb-ref-rte-block></umb-ref-rte-block>`);
});
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);
});
}
});

View File

@@ -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.#markdown = markdown;
if (this.#element) {
this.#element.markdown = markdown;
}
get markdown(): string | undefined {
return this.#element.markdown;
}
get markdown(): string | undefined {
return this.#markdown;
}
#markdown: string | undefined;
set value(value: unknown | undefined) {
this.#value = value;
if (this.#element) {
this.#element.value = value;
}
get value(): unknown | undefined {
return this.#element.value;
}
get value(): unknown | undefined {
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);