Skip to content

Commit

Permalink
fix: add support for foundation components to SSR DOM shim (#6031)
Browse files Browse the repository at this point in the history
* add files and foundation dev dependency

* fixing issue where getter and static property "attributes" was causing static to clobber the getter

* removing style utilities

* adding uninstall dom shim test util

* adding foundation DOM shim

* Revert "removing style utilities"

This reverts commit bfa5816.

* deprecate styling utilities and hard-code focus-visible

* fixing errors in foundation element rendering

* cleans up foundation-dom-shim spec file

* adding un-install dom shim

* adding exports field for foundation shim and install-foundation-shim

* Change files

* remove foundation DOM shim and integrate it into minimal DOM shim

* fixing deprecation notice

* remove uninstall-dom-shim because it is no longer needed

* removing foundation DOM shim

Co-authored-by: nicholasrice <[email protected]>
  • Loading branch information
nicholasrice and nicholasrice authored May 26, 2022
1 parent f9daa8a commit b2d111f
Show file tree
Hide file tree
Showing 14 changed files with 143 additions and 29 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Deprecates style utilities and hard-codes focus-visible value",
"packageName": "@microsoft/fast-foundation",
"email": "[email protected]",
"dependentChangeType": "patch"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"type": "patch",
"comment": "Adds DOM shim support for APIs in @microsoft/fast-foundation",
"packageName": "@microsoft/fast-ssr",
"email": "[email protected]",
"dependentChangeType": "patch"
}
12 changes: 6 additions & 6 deletions packages/web-components/fast-foundation/docs/api-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ export interface CSSDesignToken<T extends string | number | boolean | BigInteger
readonly cssCustomProperty: string;
}

// @public
// @public @deprecated
export type CSSDisplayPropertyValue = "block" | "contents" | "flex" | "grid" | "inherit" | "initial" | "inline" | "inline-block" | "inline-flex" | "inline-grid" | "inline-table" | "list-item" | "none" | "run-in" | "table" | "table-caption" | "table-cell" | "table-column" | "table-column-group" | "table-footer-group" | "table-header-group" | "table-row" | "table-row-group";

// @public
Expand Down Expand Up @@ -1092,7 +1092,7 @@ export class Dialog extends FoundationElement {
// @public
export const dialogTemplate: FoundationElementTemplate<ViewTemplate<Dialog>>;

// @public
// @public @deprecated
export const disabledCursor = "not-allowed";

// @public
Expand All @@ -1115,7 +1115,7 @@ export class Disclosure extends FoundationElement {
// @public
export const disclosureTemplate: FoundationElementTemplate<ViewTemplate<Disclosure>>;

// @public
// @public @deprecated
export function display(displayValue: CSSDisplayPropertyValue): string;

// @public
Expand Down Expand Up @@ -1241,8 +1241,8 @@ export const FlyoutPosTop: AnchoredRegionConfig;
// @public
export const FlyoutPosTopFill: AnchoredRegionConfig;

// @public
export const focusVisible: string;
// @public @deprecated
export const focusVisible = "focus-visible";

// @public
export const forcedColorsStylesheetBehavior: (styles: ElementStyles) => MatchMediaStyleSheetBehavior;
Expand Down Expand Up @@ -1373,7 +1373,7 @@ export type GenerateHeaderOptions = typeof GenerateHeaderOptions[keyof typeof Ge
// @public
export const getDirection: (rootNode: HTMLElement) => Direction;

// @public
// @public @deprecated
export const hidden = ":host([hidden]){display:none}";

// @public
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/**
* The CSS value for disabled cursors.
* @public
* @deprecated - fast-foundation is removing styling utilities
*/
export const disabledCursor = "not-allowed";
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/**
* Define all possible CSS display values.
* @public
* @deprecated - fast-foundation is removing styling utilities
*/
export type CSSDisplayPropertyValue =
| "block"
Expand Down Expand Up @@ -30,6 +31,7 @@ export type CSSDisplayPropertyValue =
/**
* A CSS fragment to set `display: none;` when the host is hidden using the [hidden] attribute.
* @public
* @deprecated - fast-foundation is removing styling utilities
*/
export const hidden = `:host([hidden]){display:none}`;

Expand All @@ -38,6 +40,7 @@ export const hidden = `:host([hidden]){display:none}`;
* Also adds CSS rules to not display the element when the [hidden] attribute is applied to the element.
* @param display - The CSS display property value
* @public
* @deprecated - fast-foundation is removing styling utilities
*/
export function display(displayValue: CSSDisplayPropertyValue): string {
return `${hidden}:host{display:${displayValue}}`;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,6 @@
import { canUseFocusVisible } from "@microsoft/fast-web-utilities";

/**
* The string representing the focus selector to be used. Value
* will be "focus-visible" when https://drafts.csswg.org/selectors-4/#the-focus-visible-pseudo
* is supported and "focus" when it is not.
*
* This will always evaluate to 'focus-visible' due to modern browser support.
* @public
* @deprecated - fast-foundation is removing styling utilities
*/
export const focusVisible = canUseFocusVisible() ? "focus-visible" : "focus";
export const focusVisible = "focus-visible";
4 changes: 3 additions & 1 deletion packages/web-components/fast-ssr/.npmignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
test
test-utilities
server
temp
src
Expand All @@ -6,4 +8,4 @@ api-extractor.json
.eslintrc.cjs
.prettierignore
playwright.config.cjs
tsconfig.json
tsconfig.json
1 change: 1 addition & 0 deletions packages/web-components/fast-ssr/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
},
"devDependencies": {
"@microsoft/fast-element": "^1.10.1",
"@microsoft/fast-foundation": "^2.46.2",
"@microsoft/api-extractor": "7.24.2",
"@playwright/test": "^1.20.1",
"@types/express": "^4.17.13",
Expand Down
72 changes: 72 additions & 0 deletions packages/web-components/fast-ssr/src/dom-shim.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import "./install-dom-shim.js";
import { expect, test } from "@playwright/test";
import { createWindow } from "./dom-shim.js";
import * as Foundation from "@microsoft/fast-foundation";
import { ViewTemplate } from "@microsoft/fast-element";
import fastSSR from "./exports.js";

test.describe("createWindow", () => {
test("should create a window with a document property that is an instance of the window's Document constructor", () => {
Expand All @@ -21,3 +25,71 @@ test.describe("createWindow", () => {
expect(windowOverride.customElements instanceof MyRegistry).toBe(true);
});
})

const designSystem = Foundation.DesignSystem.getOrCreate();

const componentsAndTemplates: [typeof Foundation.FoundationElement, ViewTemplate | Foundation.FoundationElementTemplate<any>][] = [
[Foundation.Accordion, Foundation.accordionTemplate],
[Foundation.AccordionItem, Foundation.accordionItemTemplate],
[Foundation.Anchor, Foundation.anchorTemplate],
[Foundation.AnchoredRegion, Foundation.anchoredRegionTemplate],
[Foundation.Avatar, Foundation.avatarTemplate],
[Foundation.Badge, Foundation.badgeTemplate],
[Foundation.Breadcrumb, Foundation.breadcrumbTemplate],
[Foundation.BreadcrumbItem, Foundation.breadcrumbItemTemplate],
[Foundation.Button, Foundation.buttonTemplate],
[Foundation.Calendar, Foundation.calendarTemplate],
[Foundation.Card, Foundation.cardTemplate],
[Foundation.Checkbox, Foundation.checkboxTemplate],
[Foundation.Combobox, Foundation.comboboxTemplate],
[Foundation.DataGrid, Foundation.dataGridTemplate],
[Foundation.DataGridCell, Foundation.dataGridCellTemplate],
[Foundation.DataGridRow, Foundation.dataGridRowTemplate],
[Foundation.Dialog, Foundation.dialogTemplate],
[Foundation.Disclosure, Foundation.disclosureTemplate],
[Foundation.Divider, Foundation.dividerTemplate],
[Foundation.Flipper, Foundation.flipperTemplate],
[Foundation.HorizontalScroll, Foundation.horizontalScrollTemplate],
[Foundation.Listbox as unknown as typeof Foundation.FoundationElement, Foundation.listboxTemplate],
[Foundation.ListboxOption, Foundation.listboxOptionTemplate],
[Foundation.Menu, Foundation.menuTemplate],
[Foundation.MenuItem, Foundation.menuItemTemplate],
[Foundation.NumberField, Foundation.numberFieldTemplate],
[Foundation.Picker, Foundation.pickerTemplate],
[Foundation.PickerList, Foundation.pickerListTemplate],
[Foundation.PickerListItem, Foundation.pickerListItemTemplate],
[Foundation.PickerMenu, Foundation.pickerMenuTemplate],
[Foundation.PickerMenuOption, Foundation.pickerMenuOptionTemplate],
[Foundation.BaseProgress, Foundation.progressTemplate],
[Foundation.Radio, Foundation.radioTemplate],
[Foundation.RadioGroup, Foundation.radioGroupTemplate],
[Foundation.Search, Foundation.searchTemplate],
[Foundation.Skeleton, Foundation.skeletonTemplate],
[Foundation.Slider, Foundation.sliderTemplate],
[Foundation.SliderLabel, Foundation.sliderLabelTemplate],
[Foundation.Switch, Foundation.switchTemplate],
[Foundation.Tab, Foundation.tabTemplate],
[Foundation.TabPanel, Foundation.tabPanelTemplate],
[Foundation.Tabs, Foundation.tabsTemplate],
[Foundation.TextArea, Foundation.textAreaTemplate],
[Foundation.TextField, Foundation.textFieldTemplate],
[Foundation.Toolbar, Foundation.toolbarTemplate],
[Foundation.TreeItem, Foundation.treeItemTemplate],
[Foundation.TreeView, Foundation.treeViewTemplate]
];
test.describe("The foundation DOM shim", () => {
componentsAndTemplates.forEach(([ctor, template]) => {
const baseName = ctor.name.toLowerCase();
const prefix = "fast";
designSystem.withPrefix(prefix).register(ctor.compose({
baseName,
template
}));

test(`should support construction and connection of the ${ctor.name} component and template during SSR rendering`, () => {
const { templateRenderer, defaultRenderInfo } = fastSSR();
const templateString = `<${prefix}-${baseName}></${prefix}-${baseName}>`;
expect(() => templateRenderer.render(templateString, defaultRenderInfo)).not.toThrow();
});
});
})
47 changes: 36 additions & 11 deletions packages/web-components/fast-ssr/src/dom-shim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,25 @@
/**
* @beta
*/
export class Element {}
export class Node {}

/**
* @beta
*/
export class Element extends Node {}

/**
* @beta
*/
export abstract class HTMLElement extends Element {
private static attributes: WeakMap<HTMLElement, Map<string, string>> = new WeakMap();
private static elementAttributes: WeakMap<
HTMLElement,
Map<string, string>
> = new WeakMap();
private static getOrCreateAttributesForElement(element: HTMLElement) {
let attrs = HTMLElement.attributes.get(element);
let attrs = HTMLElement.elementAttributes.get(element);
if (!attrs) {
HTMLElement.attributes.set(element, (attrs = new Map()));
HTMLElement.elementAttributes.set(element, (attrs = new Map()));
}
return attrs;
}
Expand Down Expand Up @@ -76,9 +84,7 @@ export class ShadowRoot {}
* @beta
*/
export class Document {
get adoptedStyleSheets() {
return [];
}
public adoptedStyleSheets: ReadonlyArray<CSSStyleSheet> = [];
createTreeWalker() {
return {};
}
Expand All @@ -95,6 +101,12 @@ export class Document {
*/
export class CSSStyleSheet {
replace() {}
public readonly cssRules: CSSRule[] = [];
insertRule(rule: CSSRule, index: number = 0) {
this.cssRules.splice(index, 0, rule);

return index;
}
}

/**
Expand Down Expand Up @@ -131,6 +143,17 @@ export class MutationObserver {
observe() {}
}

/**
* Shim of MediaQueryList.
* {@link https://developer.mozilla.org/en-US/docs/Web/API/MediaQueryList} */
export class MediaQueryList {
/** No-op */
addListener() {}

/** Always false */
matches = false;
}

/**
* Creates a window object.
* @param props - Additional properties to expose on the window.
Expand All @@ -141,13 +164,16 @@ export function createWindow(
props: { [key: string]: unknown } = {}
): { [key: string]: unknown } {
const window = {
Node,
Element,
HTMLElement,
Document,
CSSStyleSheet,
ShadowRoot,
CustomElementRegistry,
MutationObserver,
MediaQueryList,
matchMedia: () => new MediaQueryList(),

// Set below
window: undefined as unknown,
Expand All @@ -165,14 +191,13 @@ export function createWindow(
}

/**
* Constructs a window object and installs it on the global, exposing properties directly on globalThis and globalthis.window
* @param props - Additional properties to expose on the window.
* Installs a window object on the global, exposing properties directly on globalThis and globalthis.window
* @param window - The Window object to install
*
* @beta
*/
export function installWindowOnGlobal(props: { [key: string]: unknown } = {}) {
export function installWindowOnGlobal(window: { [key: string]: unknown }) {
if (globalThis.window === undefined) {
const window = createWindow(props);
Object.assign(globalThis, window);
globalThis.window = globalThis as typeof globalThis & Window;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FASTElement, customElement, css, html, attr } from "@microsoft/fast-ele
import { expect, test } from '@playwright/test';
import { FASTElementRenderer } from "./fast-element-renderer.js";
import fastSSR from "../exports.js";
import { consolidate } from "../test-utils.js";
import { consolidate } from "../test-utilities/consolidate.js";


@customElement({
Expand Down
4 changes: 2 additions & 2 deletions packages/web-components/fast-ssr/src/install-dom-shim.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import { installWindowOnGlobal } from "./dom-shim.js";
import { createWindow, installWindowOnGlobal } from "./dom-shim.js";

installWindowOnGlobal();
installWindowOnGlobal(createWindow());
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import "../install-dom-shim.js";
import { child, children, customElement, ExecutionContext, FASTElement, html, item, ref, repeat, slotted, when } from "@microsoft/fast-element";
import { expect, test } from "@playwright/test";
import fastSSR from "../exports.js";
import { consolidate } from "../test-utils.js";
import { consolidate } from "../test-utilities/consolidate.js";
import { TemplateRenderer } from "./template-renderer.js";

@customElement("hello-world")
Expand Down

0 comments on commit b2d111f

Please sign in to comment.