diff --git a/change/@microsoft-fast-element-a97fa752-2e4b-4d49-b865-cb4d53fc92b0.json b/change/@microsoft-fast-element-a97fa752-2e4b-4d49-b865-cb4d53fc92b0.json new file mode 100644 index 00000000000..c1f79402e6a --- /dev/null +++ b/change/@microsoft-fast-element-a97fa752-2e4b-4d49-b865-cb4d53fc92b0.json @@ -0,0 +1,7 @@ +{ + "type": "patch", + "comment": "Hide the element so it no longer renders a layout box in the DOM.", + "packageName": "@microsoft/fast-element", + "email": "144495202+AKnassa@users.noreply.github.com", + "dependentChangeType": "none" +} diff --git a/packages/fast-element/DESIGN.md b/packages/fast-element/DESIGN.md index 5e6a8a8c6fb..1a7e90d2da4 100644 --- a/packages/fast-element/DESIGN.md +++ b/packages/fast-element/DESIGN.md @@ -458,7 +458,9 @@ the imperative `html` API: `ViewTemplate` instances through the registry-aware declarative template bridge. If duplicate connected publishers share a name, the first connected publisher supplies the definition template and later duplicates do not - reassign it. + reassign it. Connecting an `` adopts a `display: none` stylesheet + into its root node, so the authoring wrapper never generates a layout box in + the document or in a shadow tree. - `TemplateParser` lowers declarative syntax to the same `strings` / `values` shape used by `ViewTemplate.create()`. - `attributeMap()` and `observerMap()` are `FASTElementExtension` factories diff --git a/packages/fast-element/SIZES.md b/packages/fast-element/SIZES.md index 92d854e5126..77dae0d91ce 100644 --- a/packages/fast-element/SIZES.md +++ b/packages/fast-element/SIZES.md @@ -19,7 +19,7 @@ Bundle sizes for `@microsoft/fast-element` exports. | repeat (@microsoft/fast-element/repeat.js) | 31.80 KB | 10.00 KB | 9.03 KB | | css (@microsoft/fast-element/css.js) | 2.43 KB | 1.00 KB | 911 B | | enableHydration (@microsoft/fast-element/hydration.js) | 46.53 KB | 13.91 KB | 12.49 KB | -| declarativeTemplate (@microsoft/fast-element/declarative.js) | 62.15 KB | 19.46 KB | 17.42 KB | +| declarativeTemplate (@microsoft/fast-element/declarative.js) | 63.88 KB | 20.04 KB | 17.86 KB | | ArrayObserver (@microsoft/fast-element/arrays.js) | 12.55 KB | 4.46 KB | 4.03 KB | | observerMap (@microsoft/fast-element/observer-map.js) | 21.96 KB | 7.73 KB | 6.97 KB | | attributeMap (@microsoft/fast-element/attribute-map.js) | 15.31 KB | 5.41 KB | 4.88 KB | diff --git a/packages/fast-element/src/declarative/template-bridge.pw.spec.ts b/packages/fast-element/src/declarative/template-bridge.pw.spec.ts index be149fa427b..071d6514b2a 100644 --- a/packages/fast-element/src/declarative/template-bridge.pw.spec.ts +++ b/packages/fast-element/src/declarative/template-bridge.pw.spec.ts @@ -476,6 +476,83 @@ test.describe("declarativeTemplate", () => { expect(result.hasFastDefinition).toBe(false); }); + test("does not render the element", async ({ page }) => { + await page.goto("/"); + + const result = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { FASTElement, declarativeTemplate, uniqueElementName } = await import( + "/declarative-main.js" + ); + + const elementName = uniqueElementName(); + + document.body.insertAdjacentHTML( + "beforeend", + ``, + ); + + class TestElement extends FASTElement {} + + await TestElement.define({ + name: elementName, + template: declarativeTemplate(), + }); + + const templateElement = document.querySelector( + `f-template[name="${elementName}"]`, + )!; + const bounds = templateElement.getBoundingClientRect(); + + return { + display: getComputedStyle(templateElement).display, + height: bounds.height, + width: bounds.width, + }; + }); + + expect(result.display).toBe("none"); + expect(result.height).toBe(0); + expect(result.width).toBe(0); + }); + + test("does not render an element inside a shadow root", async ({ + page, + }) => { + await page.goto("/"); + + const result = await page.evaluate(async () => { + // @ts-expect-error: Client module. + const { FASTElement, declarativeTemplate, uniqueElementName } = await import( + "/declarative-main.js" + ); + + const elementName = uniqueElementName(); + const host = document.createElement("div"); + const shadowRoot = host.attachShadow({ mode: "open" }); + + shadowRoot.innerHTML = ``; + document.body.appendChild(host); + + class TestElement extends FASTElement {} + + await TestElement.define({ + name: elementName, + template: declarativeTemplate(), + }); + + const templateElement = shadowRoot.querySelector("f-template")!; + + return { + display: getComputedStyle(templateElement).display, + height: templateElement.getBoundingClientRect().height, + }; + }); + + expect(result.display).toBe("none"); + expect(result.height).toBe(0); + }); + test("allows nested templates inside the authored template", async ({ page }) => { await page.goto("/"); diff --git a/packages/fast-element/src/declarative/template.ts b/packages/fast-element/src/declarative/template.ts index 3a8085f410b..dc5ade11ddc 100644 --- a/packages/fast-element/src/declarative/template.ts +++ b/packages/fast-element/src/declarative/template.ts @@ -6,6 +6,8 @@ import type { import { Schema } from "../components/schema.js"; import type { Constructable } from "../interfaces.js"; import { FAST } from "../platform.js"; +import { ElementStyles } from "../styles/element-styles.js"; +import type { StyleTarget } from "../styles/style-strategy.js"; import type { ElementViewTemplate } from "../templating/template.js"; import { Message } from "./interfaces.js"; import { ensureDeclarativeRuntime } from "./runtime.js"; @@ -17,6 +19,8 @@ const templateElementName = "f-template"; const ensuredTemplateElements = new WeakMap>(); +const templateElementStyles = new ElementStyles([`${templateElementName}{display:none}`]); + function isTemplateElementConstructor( value: CustomElementConstructor | undefined, ): boolean { @@ -102,6 +106,12 @@ class FTemplateElement extends HTMLElement implements TemplatePublisher { } public connectedCallback(): void { + const root = this.getRootNode() as unknown as StyleTarget; + + if (!templateElementStyles.isAttachedTo(root)) { + templateElementStyles.addStylesTo(root); + } + declarativeTemplateBridge.registerPublisher(this.registry, this.name, this); } diff --git a/sites/website/src/docs/3.x/resources/export-sizes.md b/sites/website/src/docs/3.x/resources/export-sizes.md index 8cc65070e6d..475c0f64e6d 100644 --- a/sites/website/src/docs/3.x/resources/export-sizes.md +++ b/sites/website/src/docs/3.x/resources/export-sizes.md @@ -34,7 +34,7 @@ Bundle sizes for `@microsoft/fast-element` exports. | repeat (@microsoft/fast-element/repeat.js) | 31.80 KB | 10.00 KB | 9.03 KB | | css (@microsoft/fast-element/css.js) | 2.43 KB | 1.00 KB | 911 B | | enableHydration (@microsoft/fast-element/hydration.js) | 46.53 KB | 13.91 KB | 12.49 KB | -| declarativeTemplate (@microsoft/fast-element/declarative.js) | 62.15 KB | 19.46 KB | 17.42 KB | +| declarativeTemplate (@microsoft/fast-element/declarative.js) | 63.88 KB | 20.04 KB | 17.86 KB | | ArrayObserver (@microsoft/fast-element/arrays.js) | 12.55 KB | 4.46 KB | 4.03 KB | | observerMap (@microsoft/fast-element/observer-map.js) | 21.96 KB | 7.73 KB | 6.97 KB | | attributeMap (@microsoft/fast-element/attribute-map.js) | 15.31 KB | 5.41 KB | 4.88 KB |