Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(core): ssr events #2891

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/clear-pugs-make.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@patternfly/pfe-core": patch
"@patternfly/elements": patch
---
Enable context protocol in SSR scenarios.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v20.10.0
v22.13.0
14 changes: 9 additions & 5 deletions core/pfe-core/controllers/light-dom-controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { ReactiveController, ReactiveElement } from 'lit';
import { isServer, type ReactiveController, type ReactiveElement } from 'lit';

import { Logger } from './logger.js';

Expand Down Expand Up @@ -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
);
}
}
}
4 changes: 2 additions & 2 deletions core/pfe-core/controllers/scroll-spy-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[] {
Expand Down Expand Up @@ -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'));
}

Expand Down
8 changes: 6 additions & 2 deletions core/pfe-core/controllers/slot-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,12 @@ export class SlotController implements ReactiveController {
#getChildrenForSlot<T extends Element = Element>(
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) => {
Expand Down
5 changes: 5 additions & 0 deletions core/pfe-core/functions/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
4 changes: 2 additions & 2 deletions core/pfe-core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@
},
"dependencies": {
"@floating-ui/dom": "^1.6.10",
"@lit/context": "^1.1.2",
"lit": "^3.2.0"
"@lit/context": "^1.1.3",
"lit": "^3.2.1"
},
"repository": {
"type": "git",
Expand Down
54 changes: 25 additions & 29 deletions core/pfe-core/ssr-shims.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { installWindowOnGlobal } from '@lit-labs/ssr/lib/dom-shim.js';

class ObserverShim {
observe(): void {
void 0;
Expand All @@ -17,33 +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: 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 '';
Expand All @@ -52,7 +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;
}
};
2 changes: 1 addition & 1 deletion elements/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
}
4 changes: 3 additions & 1 deletion elements/pf-back-to-top/pf-back-to-top.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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}`);
Expand Down
8 changes: 5 additions & 3 deletions elements/pf-clipboard-copy/pf-clipboard-copy.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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();
}
}

/**
Expand Down Expand Up @@ -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(''));
Expand Down
5 changes: 5 additions & 0 deletions elements/pf-table/context.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { createContextWithRoot } from '@patternfly/pfe-core/functions/context.js';

export const thRoleContext: {
__context__: unknown;
} = createContextWithRoot<'rowheader' | 'colheader'>('pf-th-role');
9 changes: 7 additions & 2 deletions elements/pf-table/pf-table.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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',
Expand Down Expand Up @@ -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');
Expand Down Expand Up @@ -708,7 +713,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();
}

Expand Down
13 changes: 8 additions & 5 deletions elements/pf-table/pf-th.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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> {
Expand Down
5 changes: 5 additions & 0 deletions elements/pf-table/pf-thead.ts
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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');
Expand Down
7 changes: 4 additions & 3 deletions elements/pf-tabs/pf-tab.ts
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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`
<div id="button"
part="button"
class="${classMap({ active, box: !!box, dark, light, fill, vertical })}">
<slot name="icon"
part="icon"
?hidden="${!this.icons.length}"
?hidden="${!icons?.length}"
@slotchange="${() => this.requestUpdate()}"></slot>
<slot part="text"></slot>
</div>
Expand Down
4 changes: 2 additions & 2 deletions elements/pf-tooltip/pf-tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading
Loading