diff --git a/packages/base/src/UI5Element.ts b/packages/base/src/UI5Element.ts index de5623f169bc..ae343c08376b 100644 --- a/packages/base/src/UI5Element.ts +++ b/packages/base/src/UI5Element.ts @@ -234,7 +234,9 @@ abstract class UI5Element extends HTMLElement { const ctor = this.constructor as typeof UI5Element; if (ctor._needsShadowDOM()) { const defaultOptions = { mode: "open" } as ShadowRootInit; - this.attachShadow({ ...defaultOptions, ...ctor.getMetadata().getShadowRootOptions() }); + if (!this.shadowRoot) { // if there is already a declarative shadow root, do not destroy it + this.attachShadow({ ...defaultOptions, ...ctor.getMetadata().getShadowRootOptions() }); + } const slotsAreManaged = ctor.getMetadata().slotsAreManaged(); if (slotsAreManaged) { diff --git a/packages/base/src/util/toLowercaseEnumValue.ts b/packages/base/src/util/toLowercaseEnumValue.ts index de8d571e306c..760f0c000088 100644 --- a/packages/base/src/util/toLowercaseEnumValue.ts +++ b/packages/base/src/util/toLowercaseEnumValue.ts @@ -1,5 +1,5 @@ type LowercaseEnum = T extends string ? Lowercase : never; export default function toLowercaseEnumValue(value: T): LowercaseEnum { - return value.toLowerCase() as LowercaseEnum; + return value?.toLowerCase() as LowercaseEnum; } diff --git a/packages/main/test/pages/DeclarativeShadowDOM.html b/packages/main/test/pages/DeclarativeShadowDOM.html new file mode 100644 index 000000000000..3f931eb45ee4 --- /dev/null +++ b/packages/main/test/pages/DeclarativeShadowDOM.html @@ -0,0 +1,66 @@ + + + + + + + Declarative Shadow DOM + + + + + + + + + +

Declarative

+ + + + Button + + + + + + +
+ +

Imperative

+ +Button + + + + + + diff --git a/packages/tools/lib/ssr/transform.js b/packages/tools/lib/ssr/transform.js new file mode 100644 index 000000000000..b46348d3b867 --- /dev/null +++ b/packages/tools/lib/ssr/transform.js @@ -0,0 +1,85 @@ +const fs = require('fs'); +const puppeteer = require('puppeteer'); +const prettier = require('prettier'); + +const args = process.argv.slice(2); // Skip the first two elements +const packageName = args[0] || "main"; +const pageName = args[1] || "Button"; + +const projectRoot = __dirname.split("/packages/tools/lib/ssr")[0]; +const serverRoot = `http://localhost:8080`; + +const testPageURL = `${serverRoot}/packages/${packageName}/test/pages/${pageName}.html`; +const outputPath = `${projectRoot}/packages/${packageName}/test/pages/${pageName}-SSR.html`; + +const generateSSRPage = async (testPageURL, outputPath) => { + const browser = await puppeteer.launch(); + const page = await browser.newPage(); + await page.goto(testPageURL); + + let html = await page.evaluate(() => { + function processNode(node) { + const skipList = ["vite-plugin-checker-error-overlay", "ui5-announcement-area"]; + + if (skipList.includes(node.localName)) { + return document.createTextNode(""); + } + + if (node.nodeType === Node.ELEMENT_NODE) { + const clone = document.createElement(node.tagName.toLowerCase()); + + // Copy attributes + for (let attr of node.attributes) { + clone.setAttribute(attr.name, attr.value); + } + + // Process shadow DOM if it's a custom element + if (customElements.get(node.tagName.toLowerCase())) { + const shadowRoot = node.shadowRoot; + if (shadowRoot) { + const template = document.createElement('template'); + template.shadowRootMode = 'open'; + if (shadowRoot.delegatesFocus) { + template.shadowRootDelegatesFocus = true; + } + for (let child of shadowRoot.children) { + template.content.appendChild(processNode(child)); + } + clone.appendChild(template); + } + } + // Process children + for (let child of node.childNodes) { + clone.appendChild(processNode(child)); + } + + return clone; + } else{ + return node.cloneNode(); + } + } + + const bodyClone = processNode(document.documentElement); + return bodyClone.outerHTML; + }); + + html = await prettier.format(html, { parser: 'html' }); + + // Remove vite-injected scripts + html = html.replace(``, ''); + html = html.replace(``, ''); + html = html.replace(``, ''); + + fs.writeFileSync(outputPath, html); + console.log("Done"); + + await browser.close(); +}; + +generateSSRPage(testPageURL, outputPath); \ No newline at end of file