Skip to content

Commit

Permalink
chore(storybook): fix code loading on docs pages [CSS-1070] (#3440)
Browse files Browse the repository at this point in the history
Fix an issue where "show code" was blocking loading in Storybook docs pages [CSS-1070]
  • Loading branch information
castastrophe authored Jan 15, 2025
1 parent 5be513a commit 6808c85
Show file tree
Hide file tree
Showing 33 changed files with 2,736 additions and 473 deletions.
5 changes: 5 additions & 0 deletions .changeset/famous-comics-run.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@spectrum-css/preview": patch
---

Fix an issue where "show code" was blocking loading in Storybook docs pages [CSS-1070]
33 changes: 20 additions & 13 deletions .storybook/decorators/context.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ export const withContextWrapper = makeDecorator({
color = "light",
context = "legacy",
scale = "medium",
testingPreview = false,
} = {},
parameters: {
showTestingGrid = false,
} = {},
id,
viewMode,
Expand All @@ -45,7 +47,7 @@ export const withContextWrapper = makeDecorator({

useEffect(() => {
const isDocs = viewMode === "docs";
const isTesting = testingPreview;
const isTesting = showTestingGrid;
const isRaw = Boolean(context === "raw");
const isModern = Boolean(context === "spectrum");
const isExpress = Boolean(context === "express");
Expand All @@ -59,16 +61,18 @@ export const withContextWrapper = makeDecorator({
}

for (const container of fetchContainers(id, isDocs, isTesting)) {
// Check if the container is a testing wrapper to prevent applying colors around the testing grid
const isTestingWrapper = isTesting ? container.matches("body:has([data-testing-preview]),[data-testing-preview]") : false;
// Check if the container has a static color element
let hasStaticElement = container.matches(`:has([data-html-preview])`) ? container.matches(`:has([data-html-preview] .${rootClass}--staticWhite, [data-html-preview] .${rootClass}--staticBlack)`) : container.matches(`:has(.${rootClass}--staticWhite, .${rootClass}--staticBlack)`);

// Reset the context to the original values
color = original.color;
context = original.context;
scale = original.scale;

// Check if the container has a static color element
let hasStaticElement = container.matches(`:has([data-html-preview])`) ? container.matches(`:has([data-html-preview] .${rootClass}--staticWhite, [data-html-preview] .${rootClass}--staticBlack)`) : container.matches(`:has(.${rootClass}--staticWhite, .${rootClass}--staticBlack)`);

let staticKey = staticColor;
if (!staticKey && hasStaticElement) {
if (!isTestingWrapper && !staticKey && hasStaticElement) {
staticKey = (
container.querySelector(`.${rootClass}--staticWhite`) && "white" ||
container.querySelector(`.${rootClass}--staticBlack`) && "black"
Expand All @@ -95,13 +99,13 @@ export const withContextWrapper = makeDecorator({
}

// Let the static color override the color if it's set
if (hasStaticElement && staticColorSettings[staticKey]?.color) {
if (!isTestingWrapper && hasStaticElement && staticColorSettings[staticKey]?.color) {
color = staticColorSettings[staticKey].color;
}

// Force a light theme for the body wrapper in testing preview mode
// because the individual containers will bring in the correct theme
if (isTesting && container.matches("body:has([data-testing-preview]")) {
if (isTestingWrapper) {
color = "light";
}

Expand All @@ -113,14 +117,17 @@ export const withContextWrapper = makeDecorator({
container.classList.toggle(`spectrum--${s}`, s === scale && !isRaw);
}

// Start by removing the background color from the container and then add it back if needed
container.style.removeProperty("background");
if (hasStaticElement && staticKey && staticColorSettings[staticKey]) {
container.style.background = staticColorSettings[staticKey].background;
if (!isTestingWrapper) {
if (hasStaticElement && staticKey && staticColorSettings[staticKey]) {
container.style.background = staticColorSettings[staticKey].background;
}
} else {
// Testing background is stark white to highlight the gray-ish background color on light theme
container.style.background = "Canvas";
}
}

}, [context, viewMode, original, staticColor, color, scale, rootClass, tokens, staticColorSettings, testingPreview]);
}, [context, viewMode, original, staticColor, color, scale, rootClass, tokens, staticColorSettings, showTestingGrid]);

return StoryFn(data);
},
Expand Down
2 changes: 1 addition & 1 deletion .storybook/decorators/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function fetchContainers(id, isDocs = false, isTesting = false) {
}
else if (isTesting) {
// Only capture the top-level container for testing previews
containers.push(...document.querySelectorAll("[data-inner-container]"));
containers.push(...document.querySelectorAll("body,[data-testing-preview],[data-testing-preview] [data-inner-container]"));
}

if (containers.length === 0) containers = [document.body];
Expand Down
74 changes: 38 additions & 36 deletions .storybook/decorators/language.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { makeDecorator, useEffect } from "@storybook/preview-api";
import { fetchContainers } from "./helpers.js";
/* global Typekit */

/**
* @type import('@storybook/csf').DecoratorFunction<import('@storybook/web-components').WebComponentsFramework>
Expand All @@ -17,48 +16,51 @@ export const withLanguageWrapper = makeDecorator({
viewMode,
} = context;

const currentKitId = window.currentKitId;

useEffect(() => {
const isNotEnglish = lang && lang !== "en-US";
// Set the language on all containers and track if it has changed
let hasChanged = false;
for (const container of fetchContainers(id, viewMode === "docs")) {
if (container.lang !== lang) {
container.lang = lang;
hasChanged = true;
}
}

// If the fonts are actively loading, do not re-trigger the load
if (window.FontsLoading === true) return;
// If the language has not changed, do not re-trigger the load
if (!hasChanged) return;

// If it is US-language or unset use the rok6rmo Adobe font web project id (smaller size),
// otherwise use the mge7bvf kit with all the language settings (larger size)
const kitId = isNotEnglish ? "mge7bvf" : "rok6rmo";
const config = {
kitId,
async: true,
scriptTimeout: 3000,
// https://github.com/typekit/webfontloader?tab=readme-ov-file#configuration
loading: function() {},
fontactive: function(familyName) {
console.log(`Font ${familyName} active`);
},
fontinactive: function(familyName) {
console.log(`Font ${familyName} inactive`);
},
active: function() {
console.log(`Font loaded [id: ${kitId}]`);
const kitId = lang && lang !== "en-US" ? "mge7bvf" : "rok6rmo";

// Fire a custom event to indicate the Adobe Fonts have loaded
document.dispatchEvent(new CustomEvent("typekit-loaded", { detail: { kitId } }));
},
}
// If the current kit is the same as the new kit, do not re-trigger the load
if (currentKitId === kitId) return;

if (typeof window.Typekit !== "undefined") {
// If the kitId is the same as the one already loaded, do nothing
if (window.Typekit.config?.kitId !== kitId) {
window.Typekit.load(config);
}
}
else {
try {
window.Typekit = Typekit.load(config);
} catch (e) {/* empty */}
}
try {
window.Typekit.load({
kitId,
async: true,
scriptTimeout: 3000,
// https://github.com/typekit/webfontloader?tab=readme-ov-file#configuration
loading: function() {
window.FontsLoading = true;
},
active: function() {
window.FontsLoading = false;
window.currentKitId = this.kitId;
console.log(`Font loaded [id: ${this.kitId}]`);

for (const container of fetchContainers(id, viewMode === "docs")) {
container.lang = lang;
}
}, [lang]);
// Fire a custom event to indicate the Adobe Fonts have loaded
document.dispatchEvent(new CustomEvent("typekit-loaded", { detail: { kitId: this.kitId } }));

},
});
} catch (e) {/* empty */}
}, [lang, currentKitId, window]);

return StoryFn(context);
},
Expand Down
39 changes: 22 additions & 17 deletions .storybook/decorators/testing-preview.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { FORCE_RE_RENDER } from '@storybook/core-events';
import { addons, makeDecorator, useCallback } from "@storybook/preview-api";
import { makeDecorator } from "@storybook/preview-api";
import isChromatic from "chromatic/isChromatic";

/**
Expand All @@ -11,27 +10,33 @@ export const withTestingPreviewWrapper = makeDecorator({
parameterName: "testingPreview",
wrapper: (StoryFn, context) => {
const {
// Testing preview reflects the state of the user-selected global value
globals: {
testingPreview = false,
} = {},
viewMode,
// Show testing grid reflects whether the testing grid is currently visible in the UI
parameters: {
showTestingGrid,
} = {},
viewMode
} = context;

const init = () => {
window.isChromatic = typeof isChromatic === "function" && isChromatic() === true ? isChromatic : () => testingPreview && viewMode !== "docs";
};

// Function that will update the global value and trigger a UI refresh.
const refreshAndUpdateGlobal = () => {
init();

// Invokes Storybook's addon API method (with the FORCE_RE_RENDER) event to trigger a UI refresh
addons.getChannel().emit(FORCE_RE_RENDER);
};

init();
// Below we're setting a new parameter "showTestingGrid" to reflect whether the testing grid should be visible
// This is done to ensure that the testing grid is always visible in the Chromatic testing view
// and that the user-selected global value is respected in the Storybook UI

useCallback(() => refreshAndUpdateGlobal(), [testingPreview]);
// If isChromatic() is true, we should update the global value to always show the testing grid
if (typeof isChromatic === "function" && isChromatic() === true) {
context.parameters.showTestingGrid = true;
} else if (viewMode === "docs") {
// If we're in the docs view, we should disable the testing grid
context.parameters.showTestingGrid = false;
} else if (typeof showTestingGrid === "undefined") {
// If the global value is undefined, we should set it to the testing preview value
context.parameters.showTestingGrid = testingPreview;
} else if (showTestingGrid !== testingPreview) {
context.parameters.showTestingGrid = testingPreview;
}

return StoryFn(context);
},
Expand Down
16 changes: 4 additions & 12 deletions .storybook/decorators/underlay.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import { Template as Underlay } from "@spectrum-css/underlay/stories/template.js";
import { makeDecorator } from "@storybook/preview-api";
import isChromatic from "chromatic/isChromatic";
import { html } from "lit";
import { when } from "lit/directives/when.js";
import { getRandomId } from "./utilities.js";
Expand All @@ -14,22 +13,15 @@ export const withUnderlayWrapper = makeDecorator({
parameterName: "withUnderlay",
wrapper: (StoryFn, context) => {
const {
globals: {
testingPreview = false,
} = {},
args: {
isOpen = false,
} = {},
parameters: {
withUnderlay = true,
} = {},
args: { isOpen = false } = {},
parameters: { withUnderlay = true, showTestingGrid = false } = {},
} = context;

const id = getRandomId("underlay");

// In the chromatic testing view, the underlay should be forced
// to the height of the #storybook-root element to ensure it is visible
if (testingPreview || isChromatic()) {
if (showTestingGrid) {
document.addEventListener("DOMContentLoaded", () => {
setTimeout(() => {
const container = document.getElementById("storybook-root");
Expand All @@ -47,6 +39,6 @@ export const withUnderlayWrapper = makeDecorator({
return html`
${when(withUnderlay, () => Underlay({ isOpen, id }, context))}
${StoryFn(context)}
`;
`;
},
});
Loading

0 comments on commit 6808c85

Please sign in to comment.