From d5c27c2a9a9aef631893da568b27ce9976119f11 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 13 Jan 2025 12:53:40 +0200 Subject: [PATCH 01/24] chore: nvm use lts --- .nvmrc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.nvmrc b/.nvmrc index 790e1105f2..2c022021b8 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -v20.10.0 +v22.13.0 From ba4408c40a27ab3319f65dcbdd8043a21f2ccabb Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 13 Jan 2025 12:53:53 +0200 Subject: [PATCH 02/24] chore: bump lit-labs/ssr --- package-lock.json | 24 ++++++++++++------------ tools/pfe-tools/package.json | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/package-lock.json b/package-lock.json index 196e03c708..cdd25761a9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -53,7 +53,7 @@ }, "core/pfe-core": { "name": "@patternfly/pfe-core", - "version": "4.0.1", + "version": "4.0.4", "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.6.10", @@ -63,7 +63,7 @@ }, "elements": { "name": "@patternfly/elements", - "version": "4.0.1", + "version": "4.0.2", "license": "MIT", "dependencies": { "@lit/context": "^1.1.2", @@ -2795,13 +2795,13 @@ } }, "node_modules/@lit-labs/ssr": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr/-/ssr-3.2.2.tgz", - "integrity": "sha512-He5TzeNPM9ECmVpgXRYmVlz0UA5YnzHlT43kyLi2Lu6mUidskqJVonk9W5K699+2DKhoXp8Ra4EJmHR6KrcW1Q==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr/-/ssr-3.3.0.tgz", + "integrity": "sha512-OGlPfWfJIC2CXQLuXXRtbWlgidryVI8VOEFUPc++Vk7gQ4aapAJwHJFi7Mi614ekebNLzhkpA/10IZy5g+nGcQ==", "peer": true, "dependencies": { "@lit-labs/ssr-client": "^1.1.7", - "@lit-labs/ssr-dom-shim": "^1.2.0", + "@lit-labs/ssr-dom-shim": "^1.3.0", "@lit/reactive-element": "^2.0.4", "@parse5/tools": "^0.3.0", "@types/node": "^16.0.0", @@ -2828,9 +2828,9 @@ } }, "node_modules/@lit-labs/ssr-dom-shim": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz", - "integrity": "sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ==" + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.3.0.tgz", + "integrity": "sha512-nQIWonJ6eFAvUUrSlwyHDm/aE8PBDu5kRpL0vHMg6K8fK3Diq1xdPjTnsJSwxABhaZ+5eBi1btQB5ShUTKo4nQ==" }, "node_modules/@lit-labs/ssr/node_modules/@types/node": { "version": "16.18.105", @@ -17593,7 +17593,7 @@ }, "tools/pfe-tools": { "name": "@patternfly/pfe-tools", - "version": "3.0.1", + "version": "4.0.1", "license": "MIT", "devDependencies": { "@types/dedent": "^0.7.2", @@ -17611,14 +17611,14 @@ "node": ">=18" }, "peerDependencies": { - "@11ty/eleventy": "^2.0.1", + "@11ty/eleventy": "^2.0.1 || ^3.0.0", "@11ty/eleventy-plugin-directory-output": "^1.0.1", "@11ty/eleventy-plugin-syntaxhighlight": "^5.0.0", "@changesets/cli": "^2.27.1", "@custom-elements-manifest/analyzer": "^0.5.7", "@jspm/generator": "^2.0.1", "@koa/router": "^12.0.1", - "@lit-labs/ssr": "^3.2.2", + "@lit-labs/ssr": "^3.3.0", "@open-wc/testing": "^4.0.0", "@playwright/test": "^1.44.0", "@rollup/plugin-replace": "^5.0.5", diff --git a/tools/pfe-tools/package.json b/tools/pfe-tools/package.json index c8df6670bb..67283ae623 100644 --- a/tools/pfe-tools/package.json +++ b/tools/pfe-tools/package.json @@ -70,7 +70,7 @@ "@custom-elements-manifest/analyzer": "^0.5.7", "@jspm/generator": "^2.0.1", "@koa/router": "^12.0.1", - "@lit-labs/ssr": "^3.2.2", + "@lit-labs/ssr": "^3.3.0", "@open-wc/testing": "^4.0.0", "@playwright/test": "^1.44.0", "@rollup/plugin-replace": "^5.0.5", From cd54b8bfd073cb560b78dab26892f905b1fb8473 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 13 Jan 2025 13:36:42 +0200 Subject: [PATCH 03/24] chore: bump lit/context --- core/pfe-core/package.json | 2 +- package-lock.json | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/core/pfe-core/package.json b/core/pfe-core/package.json index 55fdb012d6..2950d595f1 100644 --- a/core/pfe-core/package.json +++ b/core/pfe-core/package.json @@ -62,7 +62,7 @@ }, "dependencies": { "@floating-ui/dom": "^1.6.10", - "@lit/context": "^1.1.2", + "@lit/context": "^1.1.3", "lit": "^3.2.0" }, "repository": { diff --git a/package-lock.json b/package-lock.json index cdd25761a9..dee8b97917 100644 --- a/package-lock.json +++ b/package-lock.json @@ -57,7 +57,7 @@ "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.6.10", - "@lit/context": "^1.1.2", + "@lit/context": "^1.1.3", "lit": "^3.2.0" } }, @@ -2839,9 +2839,10 @@ "peer": true }, "node_modules/@lit/context": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.2.tgz", - "integrity": "sha512-S0nw2C6Tkm7fVX5TGYqeROGD+Z9Coa2iFpW+ysYBDH3YvCqOY3wVQvSgwbaliLJkjTnSEYCBe9qFqKV8WUFpVw==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@lit/context/-/context-1.1.3.tgz", + "integrity": "sha512-Auh37F4S0PZM93HTDfZWs97mmzaQ7M3vnTc9YvxAGyP3UItSK/8Fs0vTOGT+njuvOwbKio/l8Cx/zWL4vkutpQ==", + "license": "BSD-3-Clause", "dependencies": { "@lit/reactive-element": "^1.6.2 || ^2.0.0" } From 1bddaa9f43055ceac7e913df9ef220bf20c6c2eb Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 13 Jan 2025 13:37:02 +0200 Subject: [PATCH 04/24] fix: update context usage for ssr --- .changeset/clear-pugs-make.md | 5 +++++ core/pfe-core/functions/context.ts | 5 +++++ core/pfe-core/ssr-shims.ts | 3 ++- 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 .changeset/clear-pugs-make.md diff --git a/.changeset/clear-pugs-make.md b/.changeset/clear-pugs-make.md new file mode 100644 index 0000000000..c4b55e1a98 --- /dev/null +++ b/.changeset/clear-pugs-make.md @@ -0,0 +1,5 @@ +--- +"@patternfly/pfe-core": patch +"@patternfly/elements": patch +--- +Enable context protocol in SSR scenarios. diff --git a/core/pfe-core/functions/context.ts b/core/pfe-core/functions/context.ts index 2e109b6b2c..d3ebc1f0e4 100644 --- a/core/pfe-core/functions/context.ts +++ b/core/pfe-core/functions/context.ts @@ -7,6 +7,11 @@ function makeContextRoot() { const root = new ContextRoot(); if (!isServer) { root.attach(document.body); + } else { + root.attach( + // @ts-expect-error: enable context root in ssr + globalThis.litServerRoot, + ); } return root; } diff --git a/core/pfe-core/ssr-shims.ts b/core/pfe-core/ssr-shims.ts index 2fe31e6d6a..dd6248e381 100644 --- a/core/pfe-core/ssr-shims.ts +++ b/core/pfe-core/ssr-shims.ts @@ -34,6 +34,8 @@ globalThis.window ??= globalThis; globalThis.document ??= new MiniDocument(); // @ts-expect-error: this runs in node globalThis.navigator ??= { userAgent: '' }; +// @ts-expect-error: opt in to event support in ssr +globalThis.litSsrCallConnectedCallback = true; // @ts-expect-error: this runs in node globalThis.ErrorEvent ??= Event; // @ts-expect-error: this runs in node @@ -55,4 +57,3 @@ globalThis.getComputedStyle ??= function() { } ; - From ed2a4c94947c9e8f2580f03236de1cb6570c5296 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 13 Jan 2025 13:46:47 +0200 Subject: [PATCH 05/24] fix: use lit-ssr html function --- tools/pfe-tools/ssr/ssr.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/pfe-tools/ssr/ssr.ts b/tools/pfe-tools/ssr/ssr.ts index a7a956fbac..4239b6c0e8 100644 --- a/tools/pfe-tools/ssr/ssr.ts +++ b/tools/pfe-tools/ssr/ssr.ts @@ -1,9 +1,9 @@ -import { render } from '@lit-labs/ssr'; +import { render, html } from '@lit-labs/ssr'; import { collectResult } from '@lit-labs/ssr/lib/render-result.js'; -import { html } from 'lit'; import { unsafeHTML } from 'lit/directives/unsafe-html.js'; /** + * composes the `unsafeHTML`, `html`, `render`, and `collectResult` functions from lit ssr * @param input html partial */ export async function ssr(input: string): Promise { From 0088148e6ef1f4f7c71476edeacb59d03ad519e7 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 13 Jan 2025 14:52:11 +0200 Subject: [PATCH 06/24] fix(core): rely on lit's dom-shim --- core/pfe-core/ssr-shims.ts | 57 +++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/core/pfe-core/ssr-shims.ts b/core/pfe-core/ssr-shims.ts index dd6248e381..32d0168960 100644 --- a/core/pfe-core/ssr-shims.ts +++ b/core/pfe-core/ssr-shims.ts @@ -1,3 +1,5 @@ +import { installWindowOnGlobal } from '@lit-labs/ssr/lib/dom-shim.js'; + class ObserverShim { observe(): void { void 0; @@ -17,35 +19,7 @@ class MiniHTMLTemplateElement extends MiniHTMLElement { content = { cloneNode: (): string => this.innerHTML }; } -class MiniDocument { - createElement(tagName: string): MiniHTMLElement { - switch (tagName) { - case 'template': - return new MiniHTMLTemplateElement(tagName); - default: - return new MiniHTMLElement(tagName); - } - } -} - -// @ts-expect-error: this runs in node -globalThis.window ??= globalThis; -// @ts-expect-error: this runs in node -globalThis.document ??= new MiniDocument(); -// @ts-expect-error: this runs in node -globalThis.navigator ??= { userAgent: '' }; -// @ts-expect-error: opt in to event support in ssr -globalThis.litSsrCallConnectedCallback = true; -// @ts-expect-error: this runs in node -globalThis.ErrorEvent ??= Event; -// @ts-expect-error: this runs in node -globalThis.IntersectionObserver ??= ObserverShim; -// @ts-expect-error: this runs in node -globalThis.MutationObserver ??= ObserverShim; -// @ts-expect-error: this runs in node -globalThis.ResizeObserver ??= ObserverShim; -// @ts-expect-error: this runs in node -globalThis.getComputedStyle ??= function() { +function getComputedStyle() { return { getPropertyPriority() { return ''; @@ -54,6 +28,27 @@ globalThis.getComputedStyle ??= function() { return ''; }, }; -} +}; -; +// @ts-expect-error: opt in to event support in ssr +globalThis.litSsrCallConnectedCallback = true; + +installWindowOnGlobal({ + ErrorEvent: Event, + IntersectionObserver: ObserverShim, + MutationObserver: ObserverShim, + ResizeObserver: ObserverShim, + getComputedStyle, +}); + +// @ts-expect-error: this runs in node +globalThis.navigator.userAgent ??= '@lit-labs/ssr'; + +globalThis.document.createElement = function createElement(tagName: string): HTMLElement { + switch (tagName) { + case 'template': + return new MiniHTMLTemplateElement(tagName) as unknown as HTMLElement; + default: + return new MiniHTMLElement(tagName) as HTMLElement; + } +}; From 837f66a4e6a2f0d40187b5f05a57587484805df7 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 13 Jan 2025 14:52:32 +0200 Subject: [PATCH 07/24] chore: update and align lit version --- core/pfe-core/package.json | 2 +- elements/package.json | 2 +- package-lock.json | 15 ++++++++------- package.json | 2 +- tools/pfe-tools/package.json | 2 +- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/core/pfe-core/package.json b/core/pfe-core/package.json index 2950d595f1..1b84a32f27 100644 --- a/core/pfe-core/package.json +++ b/core/pfe-core/package.json @@ -63,7 +63,7 @@ "dependencies": { "@floating-ui/dom": "^1.6.10", "@lit/context": "^1.1.3", - "lit": "^3.2.0" + "lit": "^3.2.1" }, "repository": { "type": "git", diff --git a/elements/package.json b/elements/package.json index 4b159d1d9c..f82c073a11 100644 --- a/elements/package.json +++ b/elements/package.json @@ -131,7 +131,7 @@ "@lit/context": "^1.1.2", "@patternfly/icons": "^1.0.3", "@patternfly/pfe-core": "^4.0.1", - "lit": "^3.2.0", + "lit": "^3.2.1", "tslib": "^2.6.3" } } diff --git a/package-lock.json b/package-lock.json index dee8b97917..033bc9e7ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -34,7 +34,7 @@ "fuse.js": "^7.0.0", "husky": "^9.1.4", "leasot": "^14.4.0", - "lit": "^3.2.0", + "lit": "^3.2.1", "nunjucks": "^3.2.4", "postcss-nesting": "^13.0.0", "prompts": "^2.4.2", @@ -58,7 +58,7 @@ "dependencies": { "@floating-ui/dom": "^1.6.10", "@lit/context": "^1.1.3", - "lit": "^3.2.0" + "lit": "^3.2.1" } }, "elements": { @@ -69,7 +69,7 @@ "@lit/context": "^1.1.2", "@patternfly/icons": "^1.0.3", "@patternfly/pfe-core": "^4.0.1", - "lit": "^3.2.0", + "lit": "^3.2.1", "tslib": "^2.6.3" } }, @@ -11442,9 +11442,10 @@ "peer": true }, "node_modules/lit": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/lit/-/lit-3.2.0.tgz", - "integrity": "sha512-s6tI33Lf6VpDu7u4YqsSX78D28bYQulM+VAzsGch4fx2H0eLZnJsUBsPWmGYSGoKDNbjtRv02rio1o+UdPVwvw==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.2.1.tgz", + "integrity": "sha512-1BBa1E/z0O9ye5fZprPtdqnc0BFzxIxTTOO/tQFmyC/hj1O3jL4TfmLBw0WEwjAokdLwpclkvGgDJwTIh0/22w==", + "license": "BSD-3-Clause", "dependencies": { "@lit/reactive-element": "^2.0.4", "lit-element": "^4.1.0", @@ -17649,7 +17650,7 @@ "eslint": "^9.0.0", "execa": "^9.3.0", "glob": "^10.3.12", - "lit": "^3.1.2", + "lit": "^3.2.1", "markdown-it-anchor": "^8.6.7", "nunjucks": "^3.2.4", "patch-package": "^8.0.0", diff --git a/package.json b/package.json index 6bf63797e6..4f6d8fee37 100644 --- a/package.json +++ b/package.json @@ -314,7 +314,7 @@ "fuse.js": "^7.0.0", "husky": "^9.1.4", "leasot": "^14.4.0", - "lit": "^3.2.0", + "lit": "^3.2.1", "nunjucks": "^3.2.4", "postcss-nesting": "^13.0.0", "prompts": "^2.4.2", diff --git a/tools/pfe-tools/package.json b/tools/pfe-tools/package.json index 67283ae623..185ef125dd 100644 --- a/tools/pfe-tools/package.json +++ b/tools/pfe-tools/package.json @@ -100,7 +100,7 @@ "eslint": "^9.0.0", "execa": "^9.3.0", "glob": "^10.3.12", - "lit": "^3.1.2", + "lit": "^3.2.1", "markdown-it-anchor": "^8.6.7", "nunjucks": "^3.2.4", "patch-package": "^8.0.0", From a1271145770f05973343d0671d689b17564d31de Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 13 Jan 2025 14:52:45 +0200 Subject: [PATCH 08/24] fix(core): ssr connected callback --- core/pfe-core/controllers/light-dom-controller.ts | 14 +++++++++----- core/pfe-core/controllers/scroll-spy-controller.ts | 4 ++-- core/pfe-core/controllers/slot-controller.ts | 8 ++++++-- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/core/pfe-core/controllers/light-dom-controller.ts b/core/pfe-core/controllers/light-dom-controller.ts index e53d11cd5c..321010a467 100644 --- a/core/pfe-core/controllers/light-dom-controller.ts +++ b/core/pfe-core/controllers/light-dom-controller.ts @@ -1,4 +1,4 @@ -import type { ReactiveController, ReactiveElement } from 'lit'; +import { isServer, type ReactiveController, type ReactiveElement } from 'lit'; import { Logger } from './logger.js'; @@ -52,9 +52,13 @@ export class LightDOMController implements ReactiveController { * Returns a boolean statement of whether or not this component contains any light DOM. */ hasLightDOM(): boolean { - return !!( - this.host.children.length > 0 - || (this.host.textContent ?? '').trim().length > 0 - ); + if (isServer) { + return false; + } else { + return !!( + this.host.children.length > 0 + || (this.host.textContent ?? '').trim().length > 0 + ); + } } } diff --git a/core/pfe-core/controllers/scroll-spy-controller.ts b/core/pfe-core/controllers/scroll-spy-controller.ts index 89af9d2481..90cb254b1d 100644 --- a/core/pfe-core/controllers/scroll-spy-controller.ts +++ b/core/pfe-core/controllers/scroll-spy-controller.ts @@ -44,7 +44,7 @@ export class ScrollSpyController implements ReactiveController { #rootMargin?: string; #threshold: number | number[]; - #getRootNode: () => Node; + #getRootNode: () => Node | null; #getHash: (el: Element) => string | null; get #linkChildren(): Element[] { @@ -92,7 +92,7 @@ export class ScrollSpyController implements ReactiveController { this.#rootMargin = options.rootMargin; this.#activeAttribute = options.activeAttribute ?? 'active'; this.#threshold = options.threshold ?? 0.85; - this.#getRootNode = () => options.rootNode ?? host.getRootNode(); + this.#getRootNode = () => options.rootNode ?? host.getRootNode?.() ?? null; this.#getHash = options?.getHash ?? ((el: Element) => el.getAttribute('href')); } diff --git a/core/pfe-core/controllers/slot-controller.ts b/core/pfe-core/controllers/slot-controller.ts index a18344b989..2a7671bc33 100644 --- a/core/pfe-core/controllers/slot-controller.ts +++ b/core/pfe-core/controllers/slot-controller.ts @@ -191,8 +191,12 @@ export class SlotController implements ReactiveController { #getChildrenForSlot( name: string | typeof SlotController.default, ): T[] { - const children = Array.from(this.host.children) as T[]; - return children.filter(isSlot(name)); + if (isServer) { + return []; + } else { + const children = Array.from(this.host.children) as T[]; + return children.filter(isSlot(name)); + } } #initSlot = (slotName: string | null) => { From 1fffb2c3477024065925f9a784528a109fb2b2ad Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 13 Jan 2025 14:53:15 +0200 Subject: [PATCH 09/24] fix(elements): ssr connected callbacks --- elements/pf-back-to-top/pf-back-to-top.ts | 4 +++- elements/pf-clipboard-copy/pf-clipboard-copy.ts | 8 +++++--- elements/pf-table/pf-table.ts | 2 +- elements/pf-tabs/pf-tab.ts | 7 ++++--- elements/pf-tooltip/pf-tooltip.ts | 4 ++-- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/elements/pf-back-to-top/pf-back-to-top.ts b/elements/pf-back-to-top/pf-back-to-top.ts index 7912dcd2eb..3d3da33ec9 100644 --- a/elements/pf-back-to-top/pf-back-to-top.ts +++ b/elements/pf-back-to-top/pf-back-to-top.ts @@ -158,7 +158,9 @@ export class PfBackToTop extends LitElement { } this.#scrollSpy = !!this.scrollableSelector; - if (this.#scrollSpy && this.scrollableSelector) { + if (isServer) { + return; + } else if (this.#scrollSpy && this.scrollableSelector) { const scrollableElement = this.#rootNode?.querySelector?.(this.scrollableSelector); if (!scrollableElement) { this.#logger.error(`unable to find element with selector ${this.scrollableSelector}`); diff --git a/elements/pf-clipboard-copy/pf-clipboard-copy.ts b/elements/pf-clipboard-copy/pf-clipboard-copy.ts index 6b69717f93..1dfbf823a7 100644 --- a/elements/pf-clipboard-copy/pf-clipboard-copy.ts +++ b/elements/pf-clipboard-copy/pf-clipboard-copy.ts @@ -1,4 +1,4 @@ -import { LitElement, html, type TemplateResult } from 'lit'; +import { LitElement, html, isServer, type TemplateResult } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; import { property } from 'lit/decorators/property.js'; import { classMap } from 'lit/directives/class-map.js'; @@ -108,7 +108,9 @@ export class PfClipboardCopy extends LitElement { connectedCallback(): void { super.connectedCallback(); this.#mo.observe(this, { characterData: true }); - this.#onMutation(); + if (!isServer) { + this.#onMutation(); + } } /** @@ -167,7 +169,7 @@ export class PfClipboardCopy extends LitElement { } #onMutation() { - if (this.childNodes.length > 0) { + if (this.childNodes?.length > 0) { this.value = this.getAttribute('value') ?? this.#dedent(Array.from(this.childNodes, child => (child instanceof Element || child instanceof Text) ? (child.textContent ?? '') : '') .join('')); diff --git a/elements/pf-table/pf-table.ts b/elements/pf-table/pf-table.ts index 4611daf11c..bf44f8f7dc 100644 --- a/elements/pf-table/pf-table.ts +++ b/elements/pf-table/pf-table.ts @@ -708,7 +708,7 @@ export class PfTable extends LitElement { } #onSlotchange() { - this.columns = this.querySelector('pf-tr')?.querySelectorAll('pf-th')?.length ?? 0; + this.columns = this.querySelector?.('pf-tr')?.querySelectorAll('pf-th')?.length ?? 0; this.requestUpdate(); } diff --git a/elements/pf-tabs/pf-tab.ts b/elements/pf-tabs/pf-tab.ts index 69c9b1ca71..601daa5e9d 100644 --- a/elements/pf-tabs/pf-tab.ts +++ b/elements/pf-tabs/pf-tab.ts @@ -1,4 +1,4 @@ -import { LitElement, html, type TemplateResult } from 'lit'; +import { LitElement, html, isServer, type TemplateResult } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; import { property } from 'lit/decorators/property.js'; import { queryAssignedElements } from 'lit/decorators/query-assigned-elements.js'; @@ -63,7 +63,7 @@ export class PfTab extends LitElement { static readonly styles: CSSStyleSheet[] = [styles]; @queryAssignedElements({ slot: 'icon', flatten: true }) - private icons!: HTMLElement[]; + private icons?: HTMLElement[]; @property({ reflect: true, type: Boolean }) active = false; @@ -105,13 +105,14 @@ export class PfTab extends LitElement { const { box, fill = false, vertical = false } = this.ctx ?? {}; const light = box === 'light'; const dark = box === 'dark'; + const icons = isServer ? [] : this.icons; return html`
diff --git a/elements/pf-tooltip/pf-tooltip.ts b/elements/pf-tooltip/pf-tooltip.ts index e97344d9a1..6bd4954c3a 100644 --- a/elements/pf-tooltip/pf-tooltip.ts +++ b/elements/pf-tooltip/pf-tooltip.ts @@ -130,11 +130,11 @@ export class PfTooltip extends LitElement { }) flipBehavior?: Placement[]; get #invoker(): HTMLSlotElement | null { - return this.shadowRoot?.querySelector('#invoker') ?? null; + return this.shadowRoot?.querySelector?.('#invoker') ?? null; } get #content(): HTMLElement | null { - return this.shadowRoot?.querySelector('#tooltip') ?? null; + return this.shadowRoot?.querySelector?.('#tooltip') ?? null; } #referenceTrigger?: HTMLElement | null; From 9bd0874b22b1c4c14cfa67fe01a9898c13b3c3cb Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 13 Jan 2025 15:04:22 +0200 Subject: [PATCH 10/24] fix(elements): table th role from context instead of dom --- elements/pf-table/context.ts | 5 +++++ elements/pf-table/pf-table.ts | 7 ++++++- elements/pf-table/pf-th.ts | 13 ++++++++----- elements/pf-table/pf-thead.ts | 5 +++++ 4 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 elements/pf-table/context.ts diff --git a/elements/pf-table/context.ts b/elements/pf-table/context.ts new file mode 100644 index 0000000000..d128980e17 --- /dev/null +++ b/elements/pf-table/context.ts @@ -0,0 +1,5 @@ +import { createContextWithRoot } from '@patternfly/pfe-core/functions/context.js'; + +export const thRoleContext: { + __context__: unknown; +} = createContextWithRoot<'rowheader' | 'colheader'>('pf-th-role'); diff --git a/elements/pf-table/pf-table.ts b/elements/pf-table/pf-table.ts index bf44f8f7dc..94034a1ee1 100644 --- a/elements/pf-table/pf-table.ts +++ b/elements/pf-table/pf-table.ts @@ -3,7 +3,11 @@ import { customElement } from 'lit/decorators/custom-element.js'; import { styleMap } from 'lit/directives/style-map.js'; import { state } from 'lit/decorators/state.js'; +import { provide } from '@lit/context'; +import { thRoleContext } from './context.js'; + import { PfTh, RequestSortEvent } from './pf-th.js'; +import { PfTd } from './pf-td.js'; import { PfTr, RequestExpandEvent } from './pf-tr.js'; export * from './pf-caption.js'; @@ -14,7 +18,6 @@ export * from './pf-th.js'; export * from './pf-td.js'; import styles from './pf-table.css'; -import { PfTd } from './pf-td.js'; const rowQuery = [ ':scope > pf-tbody:not([expandable]) > pf-tr', @@ -671,6 +674,8 @@ export class PfTable extends LitElement { @state() private columns = 0; + @provide({ context: thRoleContext }) private thRowContext = 'rowheader'; + override connectedCallback(): void { super.connectedCallback(); this.setAttribute('role', 'table'); diff --git a/elements/pf-table/pf-th.ts b/elements/pf-table/pf-th.ts index ed95cee6ed..9effd562bf 100644 --- a/elements/pf-table/pf-th.ts +++ b/elements/pf-table/pf-th.ts @@ -3,6 +3,10 @@ import { customElement } from 'lit/decorators/custom-element.js'; import { property } from 'lit/decorators/property.js'; import { classMap } from 'lit/directives/class-map.js'; +import { consume } from '@lit/context'; + +import { thRoleContext } from './context.js'; + import '@patternfly/elements/pf-button/pf-button.js'; import styles from './pf-th.css'; @@ -46,13 +50,12 @@ export class PfTh extends LitElement { @property() key!: string; + @consume({ context: thRoleContext }) + private contextualRole: 'colheader' | 'rowheader' = 'rowheader'; + override connectedCallback(): void { super.connectedCallback(); - const closestThead = this.closest('pf-thead'); - const closestTable = this.closest('pf-table'); - const isChildOfThead = !!closestThead && !!closestTable?.contains(closestThead); - const role = isChildOfThead ? 'colheader' : 'rowheader'; - this.setAttribute('role', role); + this.setAttribute('role', this.contextualRole); } render(): TemplateResult<1> { diff --git a/elements/pf-table/pf-thead.ts b/elements/pf-table/pf-thead.ts index 35bc35c897..c46a51ccb4 100644 --- a/elements/pf-table/pf-thead.ts +++ b/elements/pf-table/pf-thead.ts @@ -1,7 +1,10 @@ import { LitElement, html, type TemplateResult } from 'lit'; import { customElement } from 'lit/decorators/custom-element.js'; +import { thRoleContext } from './context.js'; + import styles from './pf-thead.css'; +import { provide } from '@lit/context'; /** * Table head @@ -11,6 +14,8 @@ import styles from './pf-thead.css'; export class PfThead extends LitElement { static readonly styles: CSSStyleSheet[] = [styles]; + @provide({ context: thRoleContext }) private thRowContext = 'colheader'; + connectedCallback(): void { super.connectedCallback(); this.setAttribute('role', 'rowgroup'); From d605a41c0cd5434ed4a071d3f262c41c42afca17 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Tue, 21 Jan 2025 16:45:05 +0200 Subject: [PATCH 11/24] feat(core): wip slots decorator --- core/pfe-core/controllers/slot-controller.ts | 160 +++++++++---------- core/pfe-core/decorators/slots.ts | 27 ++++ 2 files changed, 106 insertions(+), 81 deletions(-) create mode 100644 core/pfe-core/decorators/slots.ts diff --git a/core/pfe-core/controllers/slot-controller.ts b/core/pfe-core/controllers/slot-controller.ts index 2a7671bc33..7a2896028f 100644 --- a/core/pfe-core/controllers/slot-controller.ts +++ b/core/pfe-core/controllers/slot-controller.ts @@ -1,7 +1,5 @@ import { isServer, type ReactiveController, type ReactiveElement } from 'lit'; -import { Logger } from './logger.js'; - interface AnonymousSlot { hasContent: boolean; elements: Element[]; @@ -32,7 +30,9 @@ export interface SlotsConfig { deprecations?: Record; } -function isObjectConfigSpread( +export type SlotControllerArgs = [SlotsConfig] | (string | null)[]; + +function isObjectSpread( config: ([SlotsConfig] | (string | null)[]), ): config is [SlotsConfig] { return config.length === 1 && typeof config[0] === 'object' && config[0] !== null; @@ -55,60 +55,108 @@ export class SlotController implements ReactiveController { /** @deprecated use `default` */ public static anonymous: symbol = this.default; - #nodes = new Map(); + private static singletons = new WeakMap(); - #logger: Logger; + #nodes = new Map(); - #firstUpdated = false; + #slotMapInitialized = false; - #mo = new MutationObserver(records => this.#onMutation(records)); + #slotNames: (string | null)[] = []; - #slotNames: (string | null)[]; + #ssrHintHasSlotted: (string | null)[] = []; #deprecations: Record = {}; - constructor(public host: ReactiveElement, ...config: ([SlotsConfig] | (string | null)[])) { - this.#logger = new Logger(this.host); + #mo = new MutationObserver(this.#initSlotMap.bind(this)); + + constructor(public host: ReactiveElement, ...args: SlotControllerArgs) { + const singleton = SlotController.singletons.get(host); + if (singleton) { + singleton.#initialize(...args); + return singleton; + } + this.#initialize(...args); + host.addController(this); + SlotController.singletons.set(host, this); + if (!this.#slotNames.length) { + this.#slotNames = [null]; + } + } - if (isObjectConfigSpread(config)) { + #initialize(...config: SlotControllerArgs) { + if (isObjectSpread(config)) { const [{ slots, deprecations }] = config; this.#slotNames = slots; this.#deprecations = deprecations ?? {}; } else if (config.length >= 1) { this.#slotNames = config; this.#deprecations = {}; - } else { - this.#slotNames = [null]; } - - - host.addController(this); } async hostConnected(): Promise { - this.host.addEventListener('slotchange', this.#onSlotChange as EventListener); - this.#firstUpdated = false; this.#mo.observe(this.host, { childList: true }); + this.#ssrHintHasSlotted = + this.host + // @ts-expect-error: this is a ponyfill for ::has-slotted, is not intended as a public API + .ssrHintHasSlotted + ?? []; // Map the defined slots into an object that is easier to query this.#nodes.clear(); - // Loop over the properties provided by the schema - this.#slotNames.forEach(this.#initSlot); - Object.values(this.#deprecations).forEach(this.#initSlot); - this.host.requestUpdate(); + this.#initSlotMap(); // insurance for framework integrations await this.host.updateComplete; this.host.requestUpdate(); } + hostDisconnected(): void { + this.#mo.disconnect(); + } + hostUpdated(): void { - if (!this.#firstUpdated) { - this.#slotNames.forEach(this.#initSlot); - this.#firstUpdated = true; + if (!this.#slotMapInitialized) { + this.#initSlotMap(); } } - hostDisconnected(): void { - this.#mo.disconnect(); + #initSlotMap() { + // Loop over the properties provided by the schema + for (const slotName of this.#slotNames + .concat(Object.values(this.#deprecations))) { + const slotId = slotName || SlotController.default; + const name = slotName ?? ''; + const elements = this.#getChildrenForSlot(slotId); + const slot = this.#getSlotElement(slotId); + const hasContent = + isServer ? this.#ssrHintHasSlotted.includes(slotName) + : !!elements.length || !!slot?.assignedNodes?.()?.filter(x => x.textContent?.trim()).length; + this.#nodes.set(slotId, { elements, name, hasContent, slot }); + } + this.host.requestUpdate(); + this.#slotMapInitialized = true; + } + + #getSlotElement(slotId: string | symbol) { + if (isServer) { + return null; + } else { + const selector = + slotId === SlotController.default ? 'slot:not([name])' : `slot[name="${slotId as string}"]`; + return this.host.shadowRoot?.querySelector?.(selector) ?? null; + } + } + + #getChildrenForSlot( + name: string | typeof SlotController.default, + ): T[] { + if (isServer) { + return []; + } else if (this.#nodes.has(name)) { + return this.#nodes.get(name)!.slot?.assignedElements?.() as T[]; + } else { + const children = Array.from(this.host.children) as T[]; + return children.filter(isSlot(name)); + } } /** @@ -143,19 +191,11 @@ export class SlotController implements ReactiveController { * @example this.hasSlotted('header'); */ hasSlotted(...names: (string | null | undefined)[]): boolean { - if (isServer) { - return this.host - .getAttribute('ssr-hint-has-slotted') - ?.split(',') - .map(name => name.trim()) - .some(name => names.includes(name === 'default' ? null : name)) ?? false; - } else { - const slotNames = Array.from(names, x => x == null ? SlotController.default : x); - if (!slotNames.length) { - slotNames.push(SlotController.default); - } - return slotNames.some(x => this.#nodes.get(x)?.hasContent ?? false); + const slotNames = Array.from(names, x => x == null ? SlotController.default : x); + if (!slotNames.length) { + slotNames.push(SlotController.default); } + return slotNames.some(x => this.#nodes.get(x)?.hasContent ?? false); } /** @@ -168,46 +208,4 @@ export class SlotController implements ReactiveController { isEmpty(...names: (string | null | undefined)[]): boolean { return !this.hasSlotted(...names); } - - #onSlotChange = (event: Event & { target: HTMLSlotElement }) => { - const slotName = event.target.name; - this.#initSlot(slotName); - this.host.requestUpdate(); - }; - - #onMutation = async (records: MutationRecord[]) => { - const changed = []; - for (const { addedNodes, removedNodes } of records) { - for (const node of [...addedNodes, ...removedNodes]) { - if (node instanceof HTMLElement && node.slot) { - this.#initSlot(node.slot); - changed.push(node.slot); - } - } - } - this.host.requestUpdate(); - }; - - #getChildrenForSlot( - name: string | typeof SlotController.default, - ): T[] { - if (isServer) { - return []; - } else { - const children = Array.from(this.host.children) as T[]; - return children.filter(isSlot(name)); - } - } - - #initSlot = (slotName: string | null) => { - const name = slotName || SlotController.default; - const elements = this.#nodes.get(name)?.slot?.assignedElements?.() - ?? this.#getChildrenForSlot(name); - const selector = slotName ? `slot[name="${slotName}"]` : 'slot:not([name])'; - const slot = this.host.shadowRoot?.querySelector?.(selector) ?? null; - const nodes = slot?.assignedNodes?.(); - const hasContent = !!elements.length || !!nodes?.filter(x => x.textContent?.trim()).length; - this.#nodes.set(name, { elements, name: slotName ?? '', hasContent, slot }); - this.#logger.debug(slotName, hasContent); - }; } diff --git a/core/pfe-core/decorators/slots.ts b/core/pfe-core/decorators/slots.ts new file mode 100644 index 0000000000..c27f16c675 --- /dev/null +++ b/core/pfe-core/decorators/slots.ts @@ -0,0 +1,27 @@ +import type { ReactiveElement } from 'lit'; + +import { SlotController, type SlotControllerArgs } from '../controllers/slot-controller.js'; + +/** + * Enable ssr hints for element + * @param args a spread of slot names, or a config object. + * @see SlotController constructor args + */ +export function slots(...args: SlotControllerArgs) { + return function(klass: T): void { + klass.createProperty('ssrHintHasSlotted', { + attribute: 'ssr-hint-has-slotted', + converter: { + fromAttribute(slots) { + return (slots ?? '') + .split(/[, ]/) + .map(x => x.trim()) + .map(x => x === 'default' ? null : x); + }, + }, + }); + klass.addInitializer(instance => { + new SlotController(instance, ...args); + }); + }; +} From 49c320279adbea85bf57a09a0514ba866e6b5c08 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 27 Jan 2025 14:20:10 +0200 Subject: [PATCH 12/24] fix(core): empty array check --- core/pfe-core/controllers/slot-controller.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/pfe-core/controllers/slot-controller.ts b/core/pfe-core/controllers/slot-controller.ts index 7a2896028f..1f3d2368e7 100644 --- a/core/pfe-core/controllers/slot-controller.ts +++ b/core/pfe-core/controllers/slot-controller.ts @@ -152,7 +152,7 @@ export class SlotController implements ReactiveController { if (isServer) { return []; } else if (this.#nodes.has(name)) { - return this.#nodes.get(name)!.slot?.assignedElements?.() as T[]; + return (this.#nodes.get(name)!.slot?.assignedElements?.() ?? []) as T[]; } else { const children = Array.from(this.host.children) as T[]; return children.filter(isSlot(name)); From 0b3dffa5083bfe22948a3f848b4192953be22c11 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Thu, 30 Jan 2025 13:18:42 +0200 Subject: [PATCH 13/24] fix(core): remove need for static decorator for ssr slot hints --- .../controllers/slot-controller-server.ts | 45 ++++++++++++ core/pfe-core/controllers/slot-controller.ts | 69 +++++++++++++++---- core/pfe-core/decorators/slots.ts | 27 -------- core/pfe-core/package.json | 6 +- 4 files changed, 105 insertions(+), 42 deletions(-) create mode 100644 core/pfe-core/controllers/slot-controller-server.ts delete mode 100644 core/pfe-core/decorators/slots.ts diff --git a/core/pfe-core/controllers/slot-controller-server.ts b/core/pfe-core/controllers/slot-controller-server.ts new file mode 100644 index 0000000000..b0ed7330d6 --- /dev/null +++ b/core/pfe-core/controllers/slot-controller-server.ts @@ -0,0 +1,45 @@ +import type { ReactiveElement } from 'lit'; +import { + type SlotControllerArgs, + type SlotControllerPublicAPI, +} from './slot-controller.js'; + +export class SlotController implements SlotControllerPublicAPI { + public static default = Symbol('default slot') satisfies symbol as symbol; + + /** @deprecated use `default` */ + public static anonymous: symbol = this.default; + + static property = 'ssrHintHasSlotted' as const; + + static attribute = 'ssr-hint-has-slotted' as const; + + static anonymousAttribute = 'ssr-hint-has-slotted-anonymous' as const; + + constructor(public host: ReactiveElement, ..._: SlotControllerArgs) { + host.addController(this); + } + + hostConnected?(): Promise; + + private fromAttribute(slots: string | null) { + return (slots ?? '') + .split(/[, ]/) + .map(x => x.trim()); + } + + getSlotted(..._: string[]): T[] { + return []; + } + + hasSlotted(...names: (string | null)[]): boolean { + const attr = this.host.getAttribute(SlotController.attribute); + const anon = this.host.hasAttribute(SlotController.anonymousAttribute); + const hints = new Set(this.fromAttribute(attr)); + return names.every(x => x === null ? anon : hints.has(x)); + } + + isEmpty(...names: (string | null)[]): boolean { + return !this.hasSlotted(...names); + } +} diff --git a/core/pfe-core/controllers/slot-controller.ts b/core/pfe-core/controllers/slot-controller.ts index 1f3d2368e7..3e0a7c7bca 100644 --- a/core/pfe-core/controllers/slot-controller.ts +++ b/core/pfe-core/controllers/slot-controller.ts @@ -13,8 +13,10 @@ interface NamedSlot extends AnonymousSlot { export type Slot = NamedSlot | AnonymousSlot; +export type SlotName = string | null; + export interface SlotsConfig { - slots: (string | null)[]; + slots: SlotName[]; /** * Object mapping new slot name keys to deprecated slot name values * @example `pf-modal--header` is deprecated in favour of `header` @@ -30,11 +32,9 @@ export interface SlotsConfig { deprecations?: Record; } -export type SlotControllerArgs = [SlotsConfig] | (string | null)[]; +export type SlotControllerArgs = [SlotsConfig] | SlotName[]; -function isObjectSpread( - config: ([SlotsConfig] | (string | null)[]), -): config is [SlotsConfig] { +export function isObjectSpread(config: SlotControllerArgs): config is [SlotsConfig] { return config.length === 1 && typeof config[0] === 'object' && config[0] !== null; } @@ -49,14 +49,61 @@ const isSlot = n === SlotController.default ? !child.hasAttribute('slot') : child.getAttribute('slot') === n; -export class SlotController implements ReactiveController { +export declare class SlotControllerPublicAPI implements ReactiveController { + static default: symbol; + + public host: ReactiveElement; + + constructor(host: ReactiveElement, ...args: SlotControllerArgs); + + hostConnected?(): Promise; + + hostDisconnected?(): void; + + hostUpdated?(): void; + + /** + * Given a slot name or slot names, returns elements assigned to the requested slots as an array. + * If no value is provided, it returns all children not assigned to a slot (without a slot attribute). + * @param slotNames slots to query + * @example Get header-slotted elements + * ```js + * this.getSlotted('header') + * ``` + * @example Get header- and footer-slotted elements + * ```js + * this.getSlotted('header', 'footer') + * ``` + * @example Get default-slotted elements + * ```js + * this.getSlotted(); + * ``` + */ + getSlotted(...slotNames: string[]): T[]; + + /** + * Returns a boolean statement of whether or not any of those slots exists in the light DOM. + * @param names The slot names to check. + * @example this.hasSlotted('header'); + */ + hasSlotted(...names: (string | null | undefined)[]): boolean; + + /** + * Whether or not all the requested slots are empty. + * @param names The slot names to query. If no value is provided, it returns the default slot. + * @example this.isEmpty('header', 'footer'); + * @example this.isEmpty(); + * @returns + */ + isEmpty(...names: (string | null | undefined)[]): boolean; +} + +export class SlotController implements SlotControllerPublicAPI { public static default = Symbol('default slot') satisfies symbol as symbol; /** @deprecated use `default` */ public static anonymous: symbol = this.default; - private static singletons = new WeakMap(); - #nodes = new Map(); #slotMapInitialized = false; @@ -70,14 +117,8 @@ export class SlotController implements ReactiveController { #mo = new MutationObserver(this.#initSlotMap.bind(this)); constructor(public host: ReactiveElement, ...args: SlotControllerArgs) { - const singleton = SlotController.singletons.get(host); - if (singleton) { - singleton.#initialize(...args); - return singleton; - } this.#initialize(...args); host.addController(this); - SlotController.singletons.set(host, this); if (!this.#slotNames.length) { this.#slotNames = [null]; } diff --git a/core/pfe-core/decorators/slots.ts b/core/pfe-core/decorators/slots.ts deleted file mode 100644 index c27f16c675..0000000000 --- a/core/pfe-core/decorators/slots.ts +++ /dev/null @@ -1,27 +0,0 @@ -import type { ReactiveElement } from 'lit'; - -import { SlotController, type SlotControllerArgs } from '../controllers/slot-controller.js'; - -/** - * Enable ssr hints for element - * @param args a spread of slot names, or a config object. - * @see SlotController constructor args - */ -export function slots(...args: SlotControllerArgs) { - return function(klass: T): void { - klass.createProperty('ssrHintHasSlotted', { - attribute: 'ssr-hint-has-slotted', - converter: { - fromAttribute(slots) { - return (slots ?? '') - .split(/[, ]/) - .map(x => x.trim()) - .map(x => x === 'default' ? null : x); - }, - }, - }); - klass.addInitializer(instance => { - new SlotController(instance, ...args); - }); - }; -} diff --git a/core/pfe-core/package.json b/core/pfe-core/package.json index 1b84a32f27..3b56186a3c 100644 --- a/core/pfe-core/package.json +++ b/core/pfe-core/package.json @@ -32,7 +32,11 @@ "./controllers/property-observer-controller.js": "./controllers/property-observer-controller.js", "./controllers/roving-tabindex-controller.js": "./controllers/roving-tabindex-controller.js", "./controllers/scroll-spy-controller.js": "./controllers/scroll-spy-controller.js", - "./controllers/slot-controller.js": "./controllers/slot-controller.js", + "./controllers/slot-controller.js": { + "node": "./controllers/slot-controller-server.js", + "import": "./controllers/slot-controller.js", + "default": "./controllers/slot-controller.js" + }, "./controllers/style-controller.js": "./controllers/style-controller.js", "./controllers/timestamp-controller.js": "./controllers/timestamp-controller.js", "./controllers/tabs-controller.js": "./controllers/tabs-controller.js", From 98313ce2edd7214a11282a3cc1b0c0590be36064 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Thu, 30 Jan 2025 18:36:07 +0200 Subject: [PATCH 14/24] fix(core): client-side slot controller doens't use isServer --- core/pfe-core/controllers/slot-controller.ts | 24 ++++---------------- 1 file changed, 5 insertions(+), 19 deletions(-) diff --git a/core/pfe-core/controllers/slot-controller.ts b/core/pfe-core/controllers/slot-controller.ts index 3e0a7c7bca..a0d9f276c6 100644 --- a/core/pfe-core/controllers/slot-controller.ts +++ b/core/pfe-core/controllers/slot-controller.ts @@ -1,4 +1,4 @@ -import { isServer, type ReactiveController, type ReactiveElement } from 'lit'; +import type { ReactiveController, ReactiveElement } from 'lit'; interface AnonymousSlot { hasContent: boolean; @@ -110,8 +110,6 @@ export class SlotController implements SlotControllerPublicAPI { #slotNames: (string | null)[] = []; - #ssrHintHasSlotted: (string | null)[] = []; - #deprecations: Record = {}; #mo = new MutationObserver(this.#initSlotMap.bind(this)); @@ -137,11 +135,6 @@ export class SlotController implements SlotControllerPublicAPI { async hostConnected(): Promise { this.#mo.observe(this.host, { childList: true }); - this.#ssrHintHasSlotted = - this.host - // @ts-expect-error: this is a ponyfill for ::has-slotted, is not intended as a public API - .ssrHintHasSlotted - ?? []; // Map the defined slots into an object that is easier to query this.#nodes.clear(); this.#initSlotMap(); @@ -169,8 +162,7 @@ export class SlotController implements SlotControllerPublicAPI { const elements = this.#getChildrenForSlot(slotId); const slot = this.#getSlotElement(slotId); const hasContent = - isServer ? this.#ssrHintHasSlotted.includes(slotName) - : !!elements.length || !!slot?.assignedNodes?.()?.filter(x => x.textContent?.trim()).length; + !!elements.length || !!slot?.assignedNodes?.()?.filter(x => x.textContent?.trim()).length; this.#nodes.set(slotId, { elements, name, hasContent, slot }); } this.host.requestUpdate(); @@ -178,21 +170,15 @@ export class SlotController implements SlotControllerPublicAPI { } #getSlotElement(slotId: string | symbol) { - if (isServer) { - return null; - } else { - const selector = + const selector = slotId === SlotController.default ? 'slot:not([name])' : `slot[name="${slotId as string}"]`; - return this.host.shadowRoot?.querySelector?.(selector) ?? null; - } + return this.host.shadowRoot?.querySelector?.(selector) ?? null; } #getChildrenForSlot( name: string | typeof SlotController.default, ): T[] { - if (isServer) { - return []; - } else if (this.#nodes.has(name)) { + if (this.#nodes.has(name)) { return (this.#nodes.get(name)!.slot?.assignedElements?.() ?? []) as T[]; } else { const children = Array.from(this.host.children) as T[]; From 9f5ecaa4d03eaffd34157dea7c02f9ff9bfb575f Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Thu, 30 Jan 2025 18:38:14 +0200 Subject: [PATCH 15/24] fix(core): ssr-hint-has-default-slotted attr name --- core/pfe-core/controllers/slot-controller-server.ts | 2 +- elements/pf-card/test/pf-card.e2e.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/pfe-core/controllers/slot-controller-server.ts b/core/pfe-core/controllers/slot-controller-server.ts index b0ed7330d6..e2c0cb1565 100644 --- a/core/pfe-core/controllers/slot-controller-server.ts +++ b/core/pfe-core/controllers/slot-controller-server.ts @@ -14,7 +14,7 @@ export class SlotController implements SlotControllerPublicAPI { static attribute = 'ssr-hint-has-slotted' as const; - static anonymousAttribute = 'ssr-hint-has-slotted-anonymous' as const; + static anonymousAttribute = 'ssr-hint-has-default-slotted' as const; constructor(public host: ReactiveElement, ..._: SlotControllerArgs) { host.addController(this); diff --git a/elements/pf-card/test/pf-card.e2e.ts b/elements/pf-card/test/pf-card.e2e.ts index 106ae16648..54036b0abb 100644 --- a/elements/pf-card/test/pf-card.e2e.ts +++ b/elements/pf-card/test/pf-card.e2e.ts @@ -29,7 +29,7 @@ test.describe(tagName, () => { browser, importSpecifiers: [`@patternfly/elements/${tagName}/${tagName}.js`], demoContent: /* html */ ` - +

Header

Body Footer From 444d4e8d79c36886d45e6cf3ce83a6f89a3c1857 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Sun, 6 Apr 2025 15:00:14 +0300 Subject: [PATCH 16/24] test: ssr demos backported from #2505 --- .../controllers/slot-controller-server.ts | 4 +- docs/components/demos.11tydata.cjs | 64 ++++++++ docs/components/demos.html | 145 +++++++----------- elements/pf-card/demo/ssr.html | 50 ++++++ elements/pf-card/test/pf-card.e2e.ts | 7 +- eleventy.config.cjs | 9 ++ tools/pfe-tools/dev-server/demo.css | 8 + .../dev-server/plugins/templates/index.html | 18 ++- 8 files changed, 202 insertions(+), 103 deletions(-) create mode 100644 docs/components/demos.11tydata.cjs create mode 100644 elements/pf-card/demo/ssr.html diff --git a/core/pfe-core/controllers/slot-controller-server.ts b/core/pfe-core/controllers/slot-controller-server.ts index e2c0cb1565..a9ea9ae53f 100644 --- a/core/pfe-core/controllers/slot-controller-server.ts +++ b/core/pfe-core/controllers/slot-controller-server.ts @@ -10,11 +10,9 @@ export class SlotController implements SlotControllerPublicAPI { /** @deprecated use `default` */ public static anonymous: symbol = this.default; - static property = 'ssrHintHasSlotted' as const; - static attribute = 'ssr-hint-has-slotted' as const; - static anonymousAttribute = 'ssr-hint-has-default-slotted' as const; + static anonymousAttribute = 'ssr-hint-has-slotted-default' as const; constructor(public host: ReactiveElement, ..._: SlotControllerArgs) { host.addController(this); diff --git a/docs/components/demos.11tydata.cjs b/docs/components/demos.11tydata.cjs new file mode 100644 index 0000000000..a6b271e305 --- /dev/null +++ b/docs/components/demos.11tydata.cjs @@ -0,0 +1,64 @@ +module.exports = { + templateEngineOverride: 'njk', + permalink: '{{ demo.permalink }}', + pagination: { + data: 'demos', + alias: 'demo', + size: 1, + before: xs => xs.filter(x => x.permalink), + }, + preloads: [ + '@lit/reactive-element@1.0.2/development/css-tag.js', + '@lit/reactive-element@1.0.2/development/decorators/base.js', + '@lit/reactive-element@1.0.2/development/decorators/custom-element.js', + '@lit/reactive-element@1.0.2/development/decorators/event-options.js', + '@lit/reactive-element@1.0.2/development/decorators/property.js', + '@lit/reactive-element@1.0.2/development/decorators/query-all.js', + '@lit/reactive-element@1.0.2/development/decorators/query-assigned-nodes.js', + '@lit/reactive-element@1.0.2/development/decorators/query-async.js', + '@lit/reactive-element@1.0.2/development/decorators/query.js', + '@lit/reactive-element@1.0.2/development/decorators/state.js', + '@lit/reactive-element@1.0.2/development/reactive-element.js', + 'lit-element@3.0.2/development/experimental-hydrate-support.js', + 'lit-element@3.0.2/development/lit-element.js', + 'lit-html@2.0.2/_/a39ea7cf.js', + 'lit-html@2.0.2/development/async-directive.js', + 'lit-html@2.0.2/development/directive-helpers.js', + 'lit-html@2.0.2/development/directive.js', + 'lit-html@2.0.2/development/directives/guard.js', + 'lit-html@2.0.2/development/directives/if-defined.js', + 'lit-html@2.0.2/development/directives/live.js', + 'lit-html@2.0.2/development/directives/ref.js', + 'lit-html@2.0.2/development/directives/repeat.js', + 'lit-html@2.0.2/development/directives/style-map.js', + 'lit-html@2.0.2/development/directives/template-content.js', + 'lit-html@2.0.2/development/directives/unsafe-html.js', + 'lit-html@2.0.2/development/directives/unsafe-svg.js', + 'lit-html@2.0.2/development/directives/until.js', + 'lit-html@2.0.2/development/experimental-hydrate.js', + 'lit-html@2.0.2/development/lit-html.js', + 'lit-html@2.0.2/development/static.js', + 'lit@2.0.2/async-directive.js', + 'lit@2.0.2/decorators.js', + 'lit@2.0.2/decorators/query-all.js', + 'lit@2.0.2/decorators/query-assigned-nodes.js', + 'lit@2.0.2/decorators/query-async.js', + 'lit@2.0.2/decorators/query.js', + 'lit@2.0.2/directives/guard.js', + 'lit@2.0.2/directives/if-defined.js', + 'lit@2.0.2/directives/live.js', + 'lit@2.0.2/directives/ref.js', + 'lit@2.0.2/directives/repeat.js', + 'lit@2.0.2/directives/style-map.js', + 'lit@2.0.2/directives/template-content.js', + 'lit@2.0.2/directives/unsafe-html.js', + 'lit@2.0.2/directives/unsafe-svg.js', + 'lit@2.0.2/directives/until.js', + 'lit@2.0.2/experimental-hydrate-support.js', + 'lit@2.0.2/experimental-hydrate.js', + 'lit@2.0.2/html.js', + 'lit@2.0.2/index.js', + 'lit@2.0.2/polyfill-support.js', + 'lit@2.0.2/static-html.js', + ], +}; diff --git a/docs/components/demos.html b/docs/components/demos.html index 0d17a73eec..9eaa90f977 100644 --- a/docs/components/demos.html +++ b/docs/components/demos.html @@ -1,93 +1,56 @@ ----js -{ -templateEngineOverride: 'njk', -permalink: '{{ demo.permalink }}', -pagination: { - data: 'demos', - alias: 'demo', - size: 1, - before: xs => xs.filter(x => x.permalink), -}, -preloads: [ - '@lit/reactive-element@1.0.2/development/css-tag.js', - '@lit/reactive-element@1.0.2/development/decorators/base.js', - '@lit/reactive-element@1.0.2/development/decorators/custom-element.js', - '@lit/reactive-element@1.0.2/development/decorators/event-options.js', - '@lit/reactive-element@1.0.2/development/decorators/property.js', - '@lit/reactive-element@1.0.2/development/decorators/query-all.js', - '@lit/reactive-element@1.0.2/development/decorators/query-assigned-nodes.js', - '@lit/reactive-element@1.0.2/development/decorators/query-async.js', - '@lit/reactive-element@1.0.2/development/decorators/query.js', - '@lit/reactive-element@1.0.2/development/decorators/state.js', - '@lit/reactive-element@1.0.2/development/reactive-element.js', - 'lit-element@3.0.2/development/experimental-hydrate-support.js', - 'lit-element@3.0.2/development/lit-element.js', - 'lit-html@2.0.2/_/a39ea7cf.js', - 'lit-html@2.0.2/development/async-directive.js', - 'lit-html@2.0.2/development/directive-helpers.js', - 'lit-html@2.0.2/development/directive.js', - 'lit-html@2.0.2/development/directives/guard.js', - 'lit-html@2.0.2/development/directives/if-defined.js', - 'lit-html@2.0.2/development/directives/live.js', - 'lit-html@2.0.2/development/directives/ref.js', - 'lit-html@2.0.2/development/directives/repeat.js', - 'lit-html@2.0.2/development/directives/style-map.js', - 'lit-html@2.0.2/development/directives/template-content.js', - 'lit-html@2.0.2/development/directives/unsafe-html.js', - 'lit-html@2.0.2/development/directives/unsafe-svg.js', - 'lit-html@2.0.2/development/directives/until.js', - 'lit-html@2.0.2/development/experimental-hydrate.js', - 'lit-html@2.0.2/development/lit-html.js', - 'lit-html@2.0.2/development/static.js', - 'lit@2.0.2/async-directive.js', - 'lit@2.0.2/decorators.js', - 'lit@2.0.2/decorators/query-all.js', - 'lit@2.0.2/decorators/query-assigned-nodes.js', - 'lit@2.0.2/decorators/query-async.js', - 'lit@2.0.2/decorators/query.js', - 'lit@2.0.2/directives/guard.js', - 'lit@2.0.2/directives/if-defined.js', - 'lit@2.0.2/directives/live.js', - 'lit@2.0.2/directives/ref.js', - 'lit@2.0.2/directives/repeat.js', - 'lit@2.0.2/directives/style-map.js', - 'lit@2.0.2/directives/template-content.js', - 'lit@2.0.2/directives/unsafe-html.js', - 'lit@2.0.2/directives/unsafe-svg.js', - 'lit@2.0.2/directives/until.js', - 'lit@2.0.2/experimental-hydrate-support.js', - 'lit@2.0.2/experimental-hydrate.js', - 'lit@2.0.2/html.js', - 'lit@2.0.2/index.js', - 'lit@2.0.2/polyfill-support.js', - 'lit@2.0.2/static-html.js', -] -} ---- - - - - - - - {{ demo.title or (demo.tagName) }} | PatternFly Elements - - - - {%- for path in preloads -%} - - {%- endfor -%} - - - - - - -
-
{% if demo.filePath %} - {%- include demo.filePath -%}{% endif %} -
-
- + + + + + + + + {{ demo.title or (demo.tagName) }} | PatternFly Elements + + + + {%- for path in preloads -%} + + {%- endfor -%} + + + + + + + + + +
+
{% if demo.filePath %} + {%- include demo.filePath -%}{% endif %} +
+
+ + diff --git a/elements/pf-card/demo/ssr.html b/elements/pf-card/demo/ssr.html new file mode 100644 index 0000000000..1713e50a47 --- /dev/null +++ b/elements/pf-card/demo/ssr.html @@ -0,0 +1,50 @@ + +

Header

+

Body

+ Footer +
+ + +

Body

+
+ + +

Header

+
+ + +

Header

+

Body

+
+ + +

Header

+ Footer +
+ + +

Body

+ Footer +
+ + + Footer + + + diff --git a/elements/pf-card/test/pf-card.e2e.ts b/elements/pf-card/test/pf-card.e2e.ts index 54036b0abb..27ea980470 100644 --- a/elements/pf-card/test/pf-card.e2e.ts +++ b/elements/pf-card/test/pf-card.e2e.ts @@ -4,6 +4,8 @@ import { SSRPage } from '@patternfly/pfe-tools/test/playwright/SSRPage.js'; const tagName = 'pf-card'; +const html = String.raw; + test.describe(tagName, () => { test('snapshot', async ({ page }) => { const componentPage = new PfeDemoPage(page, tagName); @@ -28,8 +30,9 @@ test.describe(tagName, () => { tagName, browser, importSpecifiers: [`@patternfly/elements/${tagName}/${tagName}.js`], - demoContent: /* html */ ` - + demoContent: html` +

Header

Body Footer diff --git a/eleventy.config.cjs b/eleventy.config.cjs index 5003dbbdb4..46ce51ec11 100644 --- a/eleventy.config.cjs +++ b/eleventy.config.cjs @@ -1,6 +1,7 @@ const { EleventyRenderPlugin } = require('@11ty/eleventy'); const SyntaxHighlightPlugin = require('@11ty/eleventy-plugin-syntaxhighlight'); const DirectoryOutputPlugin = require('@11ty/eleventy-plugin-directory-output'); +const LitSSRPlugin = require('@lit-labs/eleventy-plugin-lit'); const PfeAssetsPlugin = require('./docs/_plugins/pfe-assets.cjs'); const EmptyParagraphPlugin = require('./docs/_plugins/empty-p.cjs'); @@ -92,6 +93,14 @@ module.exports = function(eleventyConfig) { }, }); + eleventyConfig.addPlugin(LitSSRPlugin, { + componentModules: [ + 'elements/pf-card/pf-card.js', + 'elements/pf-jump-links/pf-jump-links.js', + ], + }); + + if (!process.argv.some(arg => arg.match(/--((w)(atch)?)|((s)(erve))?/))) { eleventyConfig.addPlugin(DirectoryOutputPlugin, { diff --git a/tools/pfe-tools/dev-server/demo.css b/tools/pfe-tools/dev-server/demo.css index fa90fd5a08..4b86971a1d 100644 --- a/tools/pfe-tools/dev-server/demo.css +++ b/tools/pfe-tools/dev-server/demo.css @@ -597,3 +597,11 @@ footer ul { top: 2rem; } } + +strong.noscript { + background-color: var(--pf-global--danger-color--100, #c9190b); + color: white; + border-radius: 4px; + padding: 4px 12px; + display: inline-block; +} diff --git a/tools/pfe-tools/dev-server/plugins/templates/index.html b/tools/pfe-tools/dev-server/plugins/templates/index.html index b910a4a0a3..df77100625 100644 --- a/tools/pfe-tools/dev-server/plugins/templates/index.html +++ b/tools/pfe-tools/dev-server/plugins/templates/index.html @@ -1,3 +1,6 @@ + + + {% set groupeddemos = demos | sort(false, false, 'permalink') | groupby('primaryElementName') %} {% if demo.title %} {% set title = demo.title + ' | ' + options.site.title %} @@ -16,8 +19,6 @@ {% set repoHost = '' %} {% endif %} {% endif %} - - @@ -28,6 +29,7 @@ {{ title }} + {% for sheet in options.site.stylesheets %} {% endfor %} @@ -43,16 +45,18 @@
- + {{ title }} + + + = 12" } @@ -7422,7 +7419,6 @@ "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", - "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -8783,7 +8779,6 @@ "url": "https://paypal.me/jimmywarting" } ], - "peer": true, "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -8974,7 +8969,6 @@ "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", - "peer": true, "dependencies": { "fetch-blob": "^3.1.2" }, @@ -12214,7 +12208,6 @@ "url": "https://paypal.me/jimmywarting" } ], - "peer": true, "engines": { "node": ">=10.5.0" } @@ -12223,7 +12216,6 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", - "peer": true, "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", @@ -12787,7 +12779,6 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", - "peer": true, "dependencies": { "entities": "^4.4.0" }, @@ -15771,7 +15762,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", - "peer": true, "engines": { "node": ">=6" } @@ -16477,7 +16467,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", - "peer": true, "engines": { "node": ">= 8" } @@ -16904,7 +16893,7 @@ }, "tools/create-element": { "name": "@patternfly/create-element", - "version": "1.0.3", + "version": "1.0.4", "license": "MIT", "dependencies": { "case": "^1.6.3", @@ -17049,7 +17038,7 @@ }, "tools/pfe-tools": { "name": "@patternfly/pfe-tools", - "version": "4.0.1", + "version": "4.0.3", "license": "MIT", "devDependencies": { "@types/dedent": "^0.7.2", diff --git a/package.json b/package.json index 4f6d8fee37..70fe4da7bb 100644 --- a/package.json +++ b/package.json @@ -298,6 +298,8 @@ "@commitlint/cli": "^19.4.0", "@commitlint/config-conventional": "^19.2.2", "@jspm/generator": "^2.1.3", + "@lit-labs/ssr": "^3.3.1", + "@lit-labs/ssr-client": "^1.1.7", "@lit/react": "^1.0.5", "@octokit/core": "^6.1.2", "@patternfly/patternfly": "^4.224.5", From 7e61848a942605e8e569a9e658bfb4297e048ffb Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Sun, 6 Apr 2025 15:33:01 +0300 Subject: [PATCH 18/24] chore: deps --- package-lock.json | 14 ++++++++++++++ package.json | 1 + 2 files changed, 15 insertions(+) diff --git a/package-lock.json b/package-lock.json index 8f257ac57c..230789eda7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@commitlint/cli": "^19.4.0", "@commitlint/config-conventional": "^19.2.2", "@jspm/generator": "^2.1.3", + "@lit-labs/eleventy-plugin-lit": "^1.0.5", "@lit-labs/ssr": "^3.3.1", "@lit-labs/ssr-client": "^1.1.7", "@lit/react": "^1.0.5", @@ -2501,6 +2502,19 @@ "node": ">= 12" } }, + "node_modules/@lit-labs/eleventy-plugin-lit": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@lit-labs/eleventy-plugin-lit/-/eleventy-plugin-lit-1.0.5.tgz", + "integrity": "sha512-RwNxBxYQs69o14hDlWQXgNEZRl2ZCreAeiewNmgLqEJmXO2ayefkwINs+xh659WaoZrIrg6c3kVpxS9g8/QjlA==", + "dev": true, + "dependencies": { + "@lit-labs/ssr": "^3.3.0", + "lit": "^2.7.0 || ^3.0.0" + }, + "engines": { + "node": ">=12.16.0" + } + }, "node_modules/@lit-labs/ssr": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@lit-labs/ssr/-/ssr-3.3.1.tgz", diff --git a/package.json b/package.json index 70fe4da7bb..47f510e9e3 100644 --- a/package.json +++ b/package.json @@ -298,6 +298,7 @@ "@commitlint/cli": "^19.4.0", "@commitlint/config-conventional": "^19.2.2", "@jspm/generator": "^2.1.3", + "@lit-labs/eleventy-plugin-lit": "^1.0.5", "@lit-labs/ssr": "^3.3.1", "@lit-labs/ssr-client": "^1.1.7", "@lit/react": "^1.0.5", From 0d9f9780a72bfe044bd7532891aae8d7ad4536cf Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Sun, 6 Apr 2025 17:56:14 +0300 Subject: [PATCH 19/24] docs: build elements --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 47f510e9e3..b5c603d64d 100644 --- a/package.json +++ b/package.json @@ -186,6 +186,7 @@ ], "dependencies": [ "build:core", + "build:elements", "analyze" ] }, From f4a6dd9113d08ecb761484abf18c777227a0be84 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Sun, 6 Apr 2025 20:03:48 +0300 Subject: [PATCH 20/24] docs: format --- .changeset/clear-pugs-make.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.changeset/clear-pugs-make.md b/.changeset/clear-pugs-make.md index 1423f19920..65ca6272b9 100644 --- a/.changeset/clear-pugs-make.md +++ b/.changeset/clear-pugs-make.md @@ -14,17 +14,17 @@ Before: ```js connectedCallback() { -super.connectedCallback(); -this.items = this.querySelectorAll('my-item'); + super.connectedCallback(); + this.items = [...this.querySelectorAll('my-item')]; } ``` After: ```js connectedCallback() { -super.connectedCallback(); -if (!isServer) { -this.items = this.querySelectorAll('my-item'); -} + super.connectedCallback(); + if (!isServer) { + this.items = isServer ? [] : [...this.querySelectorAll('my-item')]; + } } ``` From 848a2d641b3b4fa6fecf743a178d4de212b85b51 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Sun, 6 Apr 2025 20:04:55 +0300 Subject: [PATCH 21/24] chore: revert ssr we'll have to either patch or reimplement like we did in rhds --- eleventy.config.cjs | 19 +++++-------------- package-lock.json | 31 ++++++++++++++----------------- package.json | 4 ---- 3 files changed, 19 insertions(+), 35 deletions(-) diff --git a/eleventy.config.cjs b/eleventy.config.cjs index 46ce51ec11..831781feb9 100644 --- a/eleventy.config.cjs +++ b/eleventy.config.cjs @@ -1,7 +1,6 @@ const { EleventyRenderPlugin } = require('@11ty/eleventy'); const SyntaxHighlightPlugin = require('@11ty/eleventy-plugin-syntaxhighlight'); const DirectoryOutputPlugin = require('@11ty/eleventy-plugin-directory-output'); -const LitSSRPlugin = require('@lit-labs/eleventy-plugin-lit'); const PfeAssetsPlugin = require('./docs/_plugins/pfe-assets.cjs'); const EmptyParagraphPlugin = require('./docs/_plugins/empty-p.cjs'); @@ -19,7 +18,7 @@ const TocPlugin = require('@patternfly/pfe-tools/11ty/plugins/table-of-contents. const markdownItAnchor = require('markdown-it-anchor'); /** @param {import('@11ty/eleventy/src/UserConfig')} eleventyConfig */ -module.exports = function(eleventyConfig) { +module.exports = function (eleventyConfig) { eleventyConfig.amendLibrary('md', md => md.use(markdownItAnchor)); eleventyConfig.setQuietMode(true); @@ -50,10 +49,10 @@ module.exports = function(eleventyConfig) { eleventyConfig.addPlugin(TodosPlugin); /** format date strings */ - eleventyConfig.addFilter('prettyDate', function(dateStr, options = {}) { + eleventyConfig.addFilter('prettyDate', function (dateStr, options = {}) { const { dateStyle = 'medium' } = options; return new Intl.DateTimeFormat('en-US', { dateStyle }) - .format(new Date(dateStr)); + .format(new Date(dateStr)); }); /** fancy syntax highlighting with diff support */ @@ -87,20 +86,12 @@ module.exports = function(eleventyConfig) { return null; } else { return eleventyConfig.javascriptFunctions - .slug($.text()) - .replace(/[&,+()$~%.'":*?!<>{}]/g, ''); + .slug($.text()) + .replace(/[&,+()$~%.'":*?!<>{}]/g, ''); } }, }); - eleventyConfig.addPlugin(LitSSRPlugin, { - componentModules: [ - 'elements/pf-card/pf-card.js', - 'elements/pf-jump-links/pf-jump-links.js', - ], - }); - - if (!process.argv.some(arg => arg.match(/--((w)(atch)?)|((s)(erve))?/))) { eleventyConfig.addPlugin(DirectoryOutputPlugin, { diff --git a/package-lock.json b/package-lock.json index 230789eda7..a6b6a9fde3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,9 +18,6 @@ "@commitlint/cli": "^19.4.0", "@commitlint/config-conventional": "^19.2.2", "@jspm/generator": "^2.1.3", - "@lit-labs/eleventy-plugin-lit": "^1.0.5", - "@lit-labs/ssr": "^3.3.1", - "@lit-labs/ssr-client": "^1.1.7", "@lit/react": "^1.0.5", "@octokit/core": "^6.1.2", "@patternfly/patternfly": "^4.224.5", @@ -2502,23 +2499,11 @@ "node": ">= 12" } }, - "node_modules/@lit-labs/eleventy-plugin-lit": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@lit-labs/eleventy-plugin-lit/-/eleventy-plugin-lit-1.0.5.tgz", - "integrity": "sha512-RwNxBxYQs69o14hDlWQXgNEZRl2ZCreAeiewNmgLqEJmXO2ayefkwINs+xh659WaoZrIrg6c3kVpxS9g8/QjlA==", - "dev": true, - "dependencies": { - "@lit-labs/ssr": "^3.3.0", - "lit": "^2.7.0 || ^3.0.0" - }, - "engines": { - "node": ">=12.16.0" - } - }, "node_modules/@lit-labs/ssr": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/@lit-labs/ssr/-/ssr-3.3.1.tgz", "integrity": "sha512-JlF1PempxvzrGEpRFrF+Ki0MHzR3HA51SK8Zv0cFpW9p0bPW4k0FeCwrElCu371UEpXF7RcaE2wgYaE1az0XKg==", + "peer": true, "dependencies": { "@lit-labs/ssr-client": "^1.1.7", "@lit-labs/ssr-dom-shim": "^1.3.0", @@ -2540,6 +2525,7 @@ "version": "1.1.7", "resolved": "https://registry.npmjs.org/@lit-labs/ssr-client/-/ssr-client-1.1.7.tgz", "integrity": "sha512-VvqhY/iif3FHrlhkzEPsuX/7h/NqnfxLwVf0p8ghNIlKegRyRqgeaJevZ57s/u/LiFyKgqksRP5n+LmNvpxN+A==", + "peer": true, "dependencies": { "@lit/reactive-element": "^2.0.4", "lit": "^3.1.2", @@ -2554,7 +2540,8 @@ "node_modules/@lit-labs/ssr/node_modules/@types/node": { "version": "16.18.105", "resolved": "https://registry.npmjs.org/@types/node/-/node-16.18.105.tgz", - "integrity": "sha512-w2d0Z9yMk07uH3+Cx0N8lqFyi3yjXZxlbYappPj+AsOlT02OyxyiuNoNHdGt6EuiSm8Wtgp2YV7vWg+GMFrvFA==" + "integrity": "sha512-w2d0Z9yMk07uH3+Cx0N8lqFyi3yjXZxlbYappPj+AsOlT02OyxyiuNoNHdGt6EuiSm8Wtgp2YV7vWg+GMFrvFA==", + "peer": true }, "node_modules/@lit/context": { "version": "1.1.3", @@ -2897,6 +2884,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/@parse5/tools/-/tools-0.3.0.tgz", "integrity": "sha512-zxRyTHkqb7WQMV8kTNBKWb1BeOFUKXBXTBWuxg9H9hfvQB3IwP6Iw2U75Ia5eyRxPNltmY7E8YAlz6zWwUnjKg==", + "peer": true, "dependencies": { "parse5": "^7.0.0" } @@ -6910,6 +6898,7 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "peer": true, "engines": { "node": ">= 12" } @@ -7433,6 +7422,7 @@ "version": "5.17.1", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -8793,6 +8783,7 @@ "url": "https://paypal.me/jimmywarting" } ], + "peer": true, "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" @@ -8983,6 +8974,7 @@ "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "peer": true, "dependencies": { "fetch-blob": "^3.1.2" }, @@ -12222,6 +12214,7 @@ "url": "https://paypal.me/jimmywarting" } ], + "peer": true, "engines": { "node": ">=10.5.0" } @@ -12230,6 +12223,7 @@ "version": "3.3.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "peer": true, "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", @@ -12793,6 +12787,7 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", + "peer": true, "dependencies": { "entities": "^4.4.0" }, @@ -15776,6 +15771,7 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "peer": true, "engines": { "node": ">=6" } @@ -16481,6 +16477,7 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "peer": true, "engines": { "node": ">= 8" } diff --git a/package.json b/package.json index b5c603d64d..4f6d8fee37 100644 --- a/package.json +++ b/package.json @@ -186,7 +186,6 @@ ], "dependencies": [ "build:core", - "build:elements", "analyze" ] }, @@ -299,9 +298,6 @@ "@commitlint/cli": "^19.4.0", "@commitlint/config-conventional": "^19.2.2", "@jspm/generator": "^2.1.3", - "@lit-labs/eleventy-plugin-lit": "^1.0.5", - "@lit-labs/ssr": "^3.3.1", - "@lit-labs/ssr-client": "^1.1.7", "@lit/react": "^1.0.5", "@octokit/core": "^6.1.2", "@patternfly/patternfly": "^4.224.5", From 81ed882e3ba388ff3b4f99ed1b516deafba1be1e Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Sun, 6 Apr 2025 20:13:07 +0300 Subject: [PATCH 22/24] style: lint --- eleventy.config.cjs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/eleventy.config.cjs b/eleventy.config.cjs index 831781feb9..5003dbbdb4 100644 --- a/eleventy.config.cjs +++ b/eleventy.config.cjs @@ -18,7 +18,7 @@ const TocPlugin = require('@patternfly/pfe-tools/11ty/plugins/table-of-contents. const markdownItAnchor = require('markdown-it-anchor'); /** @param {import('@11ty/eleventy/src/UserConfig')} eleventyConfig */ -module.exports = function (eleventyConfig) { +module.exports = function(eleventyConfig) { eleventyConfig.amendLibrary('md', md => md.use(markdownItAnchor)); eleventyConfig.setQuietMode(true); @@ -49,10 +49,10 @@ module.exports = function (eleventyConfig) { eleventyConfig.addPlugin(TodosPlugin); /** format date strings */ - eleventyConfig.addFilter('prettyDate', function (dateStr, options = {}) { + eleventyConfig.addFilter('prettyDate', function(dateStr, options = {}) { const { dateStyle = 'medium' } = options; return new Intl.DateTimeFormat('en-US', { dateStyle }) - .format(new Date(dateStr)); + .format(new Date(dateStr)); }); /** fancy syntax highlighting with diff support */ @@ -86,8 +86,8 @@ module.exports = function (eleventyConfig) { return null; } else { return eleventyConfig.javascriptFunctions - .slug($.text()) - .replace(/[&,+()$~%.'":*?!<>{}]/g, ''); + .slug($.text()) + .replace(/[&,+()$~%.'":*?!<>{}]/g, ''); } }, }); From cdacbf08439dde3fba87fb694575c8864b6922c2 Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 7 Apr 2025 07:53:47 +0300 Subject: [PATCH 23/24] docs: remove preloads --- docs/components/demos.11tydata.cjs | 54 ------------------------------ docs/components/demos.html | 12 +++---- 2 files changed, 4 insertions(+), 62 deletions(-) diff --git a/docs/components/demos.11tydata.cjs b/docs/components/demos.11tydata.cjs index a6b271e305..449e25f0d6 100644 --- a/docs/components/demos.11tydata.cjs +++ b/docs/components/demos.11tydata.cjs @@ -7,58 +7,4 @@ module.exports = { size: 1, before: xs => xs.filter(x => x.permalink), }, - preloads: [ - '@lit/reactive-element@1.0.2/development/css-tag.js', - '@lit/reactive-element@1.0.2/development/decorators/base.js', - '@lit/reactive-element@1.0.2/development/decorators/custom-element.js', - '@lit/reactive-element@1.0.2/development/decorators/event-options.js', - '@lit/reactive-element@1.0.2/development/decorators/property.js', - '@lit/reactive-element@1.0.2/development/decorators/query-all.js', - '@lit/reactive-element@1.0.2/development/decorators/query-assigned-nodes.js', - '@lit/reactive-element@1.0.2/development/decorators/query-async.js', - '@lit/reactive-element@1.0.2/development/decorators/query.js', - '@lit/reactive-element@1.0.2/development/decorators/state.js', - '@lit/reactive-element@1.0.2/development/reactive-element.js', - 'lit-element@3.0.2/development/experimental-hydrate-support.js', - 'lit-element@3.0.2/development/lit-element.js', - 'lit-html@2.0.2/_/a39ea7cf.js', - 'lit-html@2.0.2/development/async-directive.js', - 'lit-html@2.0.2/development/directive-helpers.js', - 'lit-html@2.0.2/development/directive.js', - 'lit-html@2.0.2/development/directives/guard.js', - 'lit-html@2.0.2/development/directives/if-defined.js', - 'lit-html@2.0.2/development/directives/live.js', - 'lit-html@2.0.2/development/directives/ref.js', - 'lit-html@2.0.2/development/directives/repeat.js', - 'lit-html@2.0.2/development/directives/style-map.js', - 'lit-html@2.0.2/development/directives/template-content.js', - 'lit-html@2.0.2/development/directives/unsafe-html.js', - 'lit-html@2.0.2/development/directives/unsafe-svg.js', - 'lit-html@2.0.2/development/directives/until.js', - 'lit-html@2.0.2/development/experimental-hydrate.js', - 'lit-html@2.0.2/development/lit-html.js', - 'lit-html@2.0.2/development/static.js', - 'lit@2.0.2/async-directive.js', - 'lit@2.0.2/decorators.js', - 'lit@2.0.2/decorators/query-all.js', - 'lit@2.0.2/decorators/query-assigned-nodes.js', - 'lit@2.0.2/decorators/query-async.js', - 'lit@2.0.2/decorators/query.js', - 'lit@2.0.2/directives/guard.js', - 'lit@2.0.2/directives/if-defined.js', - 'lit@2.0.2/directives/live.js', - 'lit@2.0.2/directives/ref.js', - 'lit@2.0.2/directives/repeat.js', - 'lit@2.0.2/directives/style-map.js', - 'lit@2.0.2/directives/template-content.js', - 'lit@2.0.2/directives/unsafe-html.js', - 'lit@2.0.2/directives/unsafe-svg.js', - 'lit@2.0.2/directives/until.js', - 'lit@2.0.2/experimental-hydrate-support.js', - 'lit@2.0.2/experimental-hydrate.js', - 'lit@2.0.2/html.js', - 'lit@2.0.2/index.js', - 'lit@2.0.2/polyfill-support.js', - 'lit@2.0.2/static-html.js', - ], }; diff --git a/docs/components/demos.html b/docs/components/demos.html index 9eaa90f977..9716c96064 100644 --- a/docs/components/demos.html +++ b/docs/components/demos.html @@ -8,7 +8,7 @@ content="width=device-width, initial-scale=1"> - {{ demo.title or (demo.tagName) }} | PatternFly Elements - {%- for path in preloads -%} - - {%- endfor -%} + href="/main.css"> + src="https://ga.jspm.io/npm:es-module-shims@2.0.10/dist/es-module-shims.js">