From 020e6001cf4a34b7eb900586d77e767012d3570e Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Thu, 24 Mar 2022 16:07:49 -0400 Subject: [PATCH 1/5] docs: patch html-includes if needed See https://github.com/justinfagnani/html-include-element/pull/21 --- docs/demo/demo.ts | 86 ++++++++++++++++++- .../pfe-jump-links/demo/pfe-jump-links.js | 4 +- 2 files changed, 87 insertions(+), 3 deletions(-) diff --git a/docs/demo/demo.ts b/docs/demo/demo.ts index 0deecf1fcb..77ddb74dab 100644 --- a/docs/demo/demo.ts +++ b/docs/demo/demo.ts @@ -1,4 +1,88 @@ -import 'html-include-element'; +import { HTMLIncludeElement } from 'html-include-element'; + +/* eslint-disable no-console */ +/** + * quick hack to avoid page load errors if subresources are missing from demo files + * @see https://github.com/justinfagnani/html-include-element/pull/21 + */ +if (!HTMLIncludeElement.prototype.attributeChangedCallback.toString().includes('await Promise.all([...this.shadowRoot.querySelectorAll')) { + console.info('No need to patch '); +} else { + console.info('Patching '); + const isLinkAlreadyLoaded = (link: HTMLLinkElement) => { + try { + return !!(link.sheet && link.sheet.cssRules); + } catch (error) { + if (error.name === 'InvalidAccessError' || error.name === 'SecurityError') { + return false; + } else { + throw error; + } + } + }; + + const linkLoaded = async function linkLoaded(link: HTMLLinkElement) { + return new Promise((resolve, reject) => { + if (!('onload' in HTMLLinkElement.prototype)) { + resolve(null); + } else if (isLinkAlreadyLoaded(link)) { + resolve(link.sheet); + } else { + link.addEventListener('load', () => resolve(link.sheet), { once: true }); + link.addEventListener('error', () => reject({ link }), { once: true }); + } + }); + }; + + HTMLIncludeElement.prototype.attributeChangedCallback = async function attributeChangedCallback(name: string, _: string, newValue: string) { + if (name === 'src') { + let text = ''; + try { + const mode = this.mode || 'cors'; + const response = await fetch(newValue, { mode }); + if (!response.ok) { + throw new Error(`html-include fetch failed: ${response.statusText}`); + } + text = await response.text(); + if (this.src !== newValue) { + // the src attribute was changed before we got the response, so bail + return; + } + } catch (e) { + console.error(e); + } + // Don't destroy the light DOM if we're using shadow DOM, so that slotted content is respected + if (this.noShadow) { + this.innerHTML = text; + } + this.shadowRoot.innerHTML = ` + + ${this.noShadow ? '' : text} + `; + + // If we're not using shadow DOM, then the consuming root + // is responsible to load its own resources + if (!this.noShadow) { + const results = await Promise.allSettled([...this.shadowRoot.querySelectorAll('link')].map(linkLoaded)); + for (const result of results) { + if (result.status === 'rejected') { + const { link } = result.reason; + const message = `Could not load ${link.href}`; + console.error(message); + } + } + } + + this.dispatchEvent(new Event('load')); + } + }; +} +/* eslint-enable no-console */ + import 'api-viewer-element'; import '@vaadin/split-layout'; diff --git a/elements/pfe-jump-links/demo/pfe-jump-links.js b/elements/pfe-jump-links/demo/pfe-jump-links.js index d00bd1e26f..6a290446e6 100644 --- a/elements/pfe-jump-links/demo/pfe-jump-links.js +++ b/elements/pfe-jump-links/demo/pfe-jump-links.js @@ -10,8 +10,8 @@ import { installRouter } from 'pwa-helpers/router.js'; * @this {HTMLElement} */ async function route(location = window.location, event) { - event.preventDefault(); - event.stopPropagating(); + event?.preventDefault(); + event?.stopPropagating(); if (location.hash) { root?.querySelector(location.hash)?.scrollIntoView({ behavior: 'smooth' }); } From 1cb58654de87600f011c5ffebbc1da44f80619bf Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Thu, 24 Mar 2022 16:21:23 -0400 Subject: [PATCH 2/5] fix(card): build lightdom at build time --- .changeset/brave-feet-sing.md | 6 ++++++ docs/demo/demo.ts | 2 ++ elements/pfe-card/demo/pfe-card.html | 2 +- elements/pfe-card/package.json | 1 + elements/pfe-cta/package.json | 2 +- package.json | 1 + 6 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 .changeset/brave-feet-sing.md diff --git a/.changeset/brave-feet-sing.md b/.changeset/brave-feet-sing.md new file mode 100644 index 0000000000..a62534a119 --- /dev/null +++ b/.changeset/brave-feet-sing.md @@ -0,0 +1,6 @@ +--- +"@patternfly/pfe-card": patch +"@patternfly/pfe-cta": patch +--- + +build pfe-card and pfe-cta's light dom CSS during the `build` command diff --git a/docs/demo/demo.ts b/docs/demo/demo.ts index 77ddb74dab..44e3426e05 100644 --- a/docs/demo/demo.ts +++ b/docs/demo/demo.ts @@ -9,6 +9,8 @@ if (!HTMLIncludeElement.prototype.attributeChangedCallback.toString().includes(' console.info('No need to patch '); } else { console.info('Patching '); + // @ts-expect-error: tla is available + await customElements.whenDefined('html-include'); const isLinkAlreadyLoaded = (link: HTMLLinkElement) => { try { return !!(link.sheet && link.sheet.cssRules); diff --git a/elements/pfe-card/demo/pfe-card.html b/elements/pfe-card/demo/pfe-card.html index d9b462970b..1ec126315e 100644 --- a/elements/pfe-card/demo/pfe-card.html +++ b/elements/pfe-card/demo/pfe-card.html @@ -3,7 +3,7 @@ - + diff --git a/elements/pfe-card/package.json b/elements/pfe-card/package.json index 52c3772332..577b8d37c0 100644 --- a/elements/pfe-card/package.json +++ b/elements/pfe-card/package.json @@ -27,6 +27,7 @@ "build:clean": "npm run clean", "build:esbuild": "node ../../scripts/build.js --include pfe-card", "build:analyze": "npm run analyze", + "build:lightdom": "sass pfe-card--lightdom.scss pfe-card--lightdom.min.css --style=compressed --load-path=../../node_modules", "build:types": "tsc -b .", "🧑‍🔬-----TEST-------🧑‍🔬": "❓ Test packages", "test": "wtr --files './test/*.spec.ts' --config ../../web-test-runner.config.js", diff --git a/elements/pfe-cta/package.json b/elements/pfe-cta/package.json index df6482d696..f19a422151 100644 --- a/elements/pfe-cta/package.json +++ b/elements/pfe-cta/package.json @@ -26,7 +26,7 @@ "build": "run-s build:*", "build:clean": "npm run clean", "build:esbuild": "node ../../scripts/build.js --include pfe-cta", - "build:lightdom": "sass pfe-cta--lightdom.scss pfe-cta--lightdom.css --load-path=../../node_modules", + "build:lightdom": "sass pfe-cta--lightdom.scss pfe-cta--lightdom.min.css --style=compressed --load-path=../../node_modules", "build:analyze": "npm run analyze", "build:types": "tsc -b .", "🧑‍🔬-----TEST-------🧑‍🔬": "❓ Test packages", diff --git a/package.json b/package.json index 21208681c3..d70659716f 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "build:analyze": "lerna run analyze --parallel", "build:styles": "npm run build --workspace @patternfly/pfe-styles", "build:core": "npm run build --workspace @patternfly/pfe-core", + "build:lightdom": "npm run build:lightdom --workspaces --if-present", "build:elements": "scripts/build.js", "build:types": "tsc -b", "build:docs": "run-s build:docs:*", From e546170a737cd6e1566108339b3b2a6d6e9d6c2a Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Sun, 27 Mar 2022 12:19:32 -0400 Subject: [PATCH 3/5] docs: fix html-includes patch --- docs/demo/demo.ts | 173 +++++++++++++++++++++++----------------------- 1 file changed, 87 insertions(+), 86 deletions(-) diff --git a/docs/demo/demo.ts b/docs/demo/demo.ts index 44e3426e05..f8b1630746 100644 --- a/docs/demo/demo.ts +++ b/docs/demo/demo.ts @@ -1,90 +1,4 @@ import { HTMLIncludeElement } from 'html-include-element'; - -/* eslint-disable no-console */ -/** - * quick hack to avoid page load errors if subresources are missing from demo files - * @see https://github.com/justinfagnani/html-include-element/pull/21 - */ -if (!HTMLIncludeElement.prototype.attributeChangedCallback.toString().includes('await Promise.all([...this.shadowRoot.querySelectorAll')) { - console.info('No need to patch '); -} else { - console.info('Patching '); - // @ts-expect-error: tla is available - await customElements.whenDefined('html-include'); - const isLinkAlreadyLoaded = (link: HTMLLinkElement) => { - try { - return !!(link.sheet && link.sheet.cssRules); - } catch (error) { - if (error.name === 'InvalidAccessError' || error.name === 'SecurityError') { - return false; - } else { - throw error; - } - } - }; - - const linkLoaded = async function linkLoaded(link: HTMLLinkElement) { - return new Promise((resolve, reject) => { - if (!('onload' in HTMLLinkElement.prototype)) { - resolve(null); - } else if (isLinkAlreadyLoaded(link)) { - resolve(link.sheet); - } else { - link.addEventListener('load', () => resolve(link.sheet), { once: true }); - link.addEventListener('error', () => reject({ link }), { once: true }); - } - }); - }; - - HTMLIncludeElement.prototype.attributeChangedCallback = async function attributeChangedCallback(name: string, _: string, newValue: string) { - if (name === 'src') { - let text = ''; - try { - const mode = this.mode || 'cors'; - const response = await fetch(newValue, { mode }); - if (!response.ok) { - throw new Error(`html-include fetch failed: ${response.statusText}`); - } - text = await response.text(); - if (this.src !== newValue) { - // the src attribute was changed before we got the response, so bail - return; - } - } catch (e) { - console.error(e); - } - // Don't destroy the light DOM if we're using shadow DOM, so that slotted content is respected - if (this.noShadow) { - this.innerHTML = text; - } - this.shadowRoot.innerHTML = ` - - ${this.noShadow ? '' : text} - `; - - // If we're not using shadow DOM, then the consuming root - // is responsible to load its own resources - if (!this.noShadow) { - const results = await Promise.allSettled([...this.shadowRoot.querySelectorAll('link')].map(linkLoaded)); - for (const result of results) { - if (result.status === 'rejected') { - const { link } = result.reason; - const message = `Could not load ${link.href}`; - console.error(message); - } - } - } - - this.dispatchEvent(new Event('load')); - } - }; -} -/* eslint-enable no-console */ - import 'api-viewer-element'; import '@vaadin/split-layout'; @@ -135,8 +49,95 @@ async function onLoad(element: string, base: 'core' | 'elements', location: Loca onContextChange(); } +async function patchHTMLIncludes() { + /* eslint-disable no-console */ + /** + * quick hack to avoid page load errors if subresources are missing from demo files + * @see https://github.com/justinfagnani/html-include-element/pull/21 + */ + if (!HTMLIncludeElement.prototype.attributeChangedCallback.toString().includes('await Promise.all([...this.shadowRoot.querySelectorAll')) { + console.info('No need to patch '); + } else { + console.info('Patching '); + await customElements.whenDefined('html-include'); + const isLinkAlreadyLoaded = (link: HTMLLinkElement) => { + try { + return !!(link.sheet && link.sheet.cssRules); + } catch (error) { + if (error.name === 'InvalidAccessError' || error.name === 'SecurityError') { + return false; + } else { + throw error; + } + } + }; + + const linkLoaded = async function linkLoaded(link: HTMLLinkElement) { + return new Promise((resolve, reject) => { + if (!('onload' in HTMLLinkElement.prototype)) { + resolve(null); + } else if (isLinkAlreadyLoaded(link)) { + resolve(link.sheet); + } else { + link.addEventListener('load', () => resolve(link.sheet), { once: true }); + link.addEventListener('error', () => reject({ link }), { once: true }); + } + }); + }; + + HTMLIncludeElement.prototype.attributeChangedCallback = async function attributeChangedCallback(name: string, _: string, newValue: string) { + if (name === 'src') { + let text = ''; + try { + const mode = this.mode || 'cors'; + const response = await fetch(newValue, { mode }); + if (!response.ok) { + throw new Error(`html-include fetch failed: ${response.statusText}`); + } + text = await response.text(); + if (this.src !== newValue) { + // the src attribute was changed before we got the response, so bail + return; + } + } catch (e) { + console.error(e); + } + // Don't destroy the light DOM if we're using shadow DOM, so that slotted content is respected + if (this.noShadow) { + this.innerHTML = text; + } + this.shadowRoot.innerHTML = ` + + ${this.noShadow ? '' : text} + `; + + // If we're not using shadow DOM, then the consuming root + // is responsible to load its own resources + if (!this.noShadow) { + const results = await Promise.allSettled([...this.shadowRoot.querySelectorAll('link')].map(linkLoaded)); + for (const result of results) { + if (result.status === 'rejected') { + const { link } = result.reason; + const message = `Could not load ${link.href}`; + console.error(message); + } + } + } + + this.dispatchEvent(new Event('load')); + } + }; + } + /* eslint-enable no-console */ +} + /** Load up the requested element's demo in a separate shadow root */ async function go(location = window.location) { + await patchHTMLIncludes(); const { element } = pattern.exec(location.href)?.pathname?.groups ?? {}; if (element) { From d7c110c836776c65ac6e0c8cd37450816b485ebf Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 28 Mar 2022 10:47:09 -0400 Subject: [PATCH 4/5] docs: fix demo hack for html-include --- docs/demo/demo.ts | 164 ++++++++++++++++++++++------------------------ 1 file changed, 80 insertions(+), 84 deletions(-) diff --git a/docs/demo/demo.ts b/docs/demo/demo.ts index f8b1630746..a358455808 100644 --- a/docs/demo/demo.ts +++ b/docs/demo/demo.ts @@ -1,4 +1,4 @@ -import { HTMLIncludeElement } from 'html-include-element'; +import 'html-include-element'; import 'api-viewer-element'; import '@vaadin/split-layout'; @@ -47,97 +47,89 @@ async function onLoad(element: string, base: 'core' | 'elements', location: Loca include.classList.remove('loading'); componentHeader.classList.remove('loading'); onContextChange(); + window.removeEventListener('unhandledrejection', handleBadLoad); } -async function patchHTMLIncludes() { - /* eslint-disable no-console */ - /** - * quick hack to avoid page load errors if subresources are missing from demo files - * @see https://github.com/justinfagnani/html-include-element/pull/21 - */ - if (!HTMLIncludeElement.prototype.attributeChangedCallback.toString().includes('await Promise.all([...this.shadowRoot.querySelectorAll')) { - console.info('No need to patch '); - } else { - console.info('Patching '); - await customElements.whenDefined('html-include'); - const isLinkAlreadyLoaded = (link: HTMLLinkElement) => { - try { - return !!(link.sheet && link.sheet.cssRules); - } catch (error) { - if (error.name === 'InvalidAccessError' || error.name === 'SecurityError') { - return false; - } else { - throw error; - } +/* eslint-disable no-console */ +/** + * quick hack to avoid page load errors if subresources are missing from demo files + * @see https://github.com/justinfagnani/html-include-element/pull/21 + */ +async function handleBadLoad() { + await loadPartial.call(document.querySelector('html-include')); +} + +const isLinkAlreadyLoaded = (link: HTMLLinkElement) => { + try { + return !!(link.sheet && link.sheet.cssRules); + } catch (error) { + if (error.name === 'InvalidAccessError' || error.name === 'SecurityError') { + return false; + } else { + throw error; + } + } +}; + +const linkLoaded = async function linkLoaded(link: HTMLLinkElement) { + return new Promise((resolve, reject) => { + if (!('onload' in HTMLLinkElement.prototype)) { + resolve(null); + } else if (isLinkAlreadyLoaded(link)) { + resolve(link.sheet); + } else { + link.addEventListener('load', () => resolve(link.sheet), { once: true }); + link.addEventListener('error', () => reject({ link }), { once: true }); + } + }); +}; + +/** @this {import('html-include-element').HTMLIncludeElement} */ +async function loadPartial() { + let text = ''; + try { + const mode = this.mode || 'cors'; + const response = await fetch(this.getAttribute('src'), { mode }); + if (!response.ok) { + throw new Error(`html-include fetch failed: ${response.statusText}`); + } + text = await response.text(); + } catch (e) { + console.error(e); + } + // Don't destroy the light DOM if we're using shadow DOM, so that slotted content is respected + if (this.noShadow) { + this.innerHTML = text; + } + + this.shadowRoot.innerHTML = ` + - ${this.noShadow ? '' : text} - `; - - // If we're not using shadow DOM, then the consuming root - // is responsible to load its own resources - if (!this.noShadow) { - const results = await Promise.allSettled([...this.shadowRoot.querySelectorAll('link')].map(linkLoaded)); - for (const result of results) { - if (result.status === 'rejected') { - const { link } = result.reason; - const message = `Could not load ${link.href}`; - console.error(message); - } - } - } - - this.dispatchEvent(new Event('load')); + + ${this.noShadow ? '' : text} + `; + + // If we're not using shadow DOM, then the consuming root + // is responsible to load its own resources + if (!this.noShadow) { + const results = await Promise.allSettled([...this.shadowRoot.querySelectorAll('link')].map(linkLoaded)); + for (const result of results) { + if (result.status === 'rejected') { + const { link } = result.reason; + const message = `Could not load ${link.href}`; + console.error(message); } - }; + } } - /* eslint-enable no-console */ + + this.dispatchEvent(new Event('load')); } +/* eslint-enable no-console */ /** Load up the requested element's demo in a separate shadow root */ async function go(location = window.location) { - await patchHTMLIncludes(); const { element } = pattern.exec(location.href)?.pathname?.groups ?? {}; if (element) { @@ -146,7 +138,11 @@ async function go(location = window.location) { componentHeader.classList.add('loading'); include.addEventListener('load', onLoad.bind(include, element, base, location), { once: true }); include.setAttribute('data-demo', element); - include.src = `/${base}/${element}/demo/${element}.html`; + + window.addEventListener('unhandledrejection', handleBadLoad, { once: true }); + + include.setAttribute('src', `/${base}/${element}/demo/${element}.html`); + viewer.src = `/${base}/${element}/custom-elements.json`; viewer.hidden = false; document.title = `${pretty(element)} | PatternFly Elements`; From 6c7831cc235336c23d79752699742c3d7ea491cb Mon Sep 17 00:00:00 2001 From: Benny Powers Date: Mon, 28 Mar 2022 10:48:38 -0400 Subject: [PATCH 5/5] docs(cta): correct lightdom css link --- elements/pfe-cta/demo/pfe-cta.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/elements/pfe-cta/demo/pfe-cta.html b/elements/pfe-cta/demo/pfe-cta.html index 522c0efda2..204c9716cb 100644 --- a/elements/pfe-cta/demo/pfe-cta.html +++ b/elements/pfe-cta/demo/pfe-cta.html @@ -3,7 +3,7 @@ - +