Skip to content

Commit

Permalink
fix: ensure stories get refreshed on update (#2468)
Browse files Browse the repository at this point in the history
  • Loading branch information
fwouts committed Feb 21, 2024
1 parent 180e84d commit 8174516
Show file tree
Hide file tree
Showing 10 changed files with 446 additions and 52 deletions.
22 changes: 22 additions & 0 deletions core/src/vite/plugins/invalidate-stories-modules-on-update.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type * as vite from "vite";

export function invalidateStoriesModulesOnUpdate(): vite.Plugin {
return {
name: "previewjs:invalidate-stories-modules-on-update",
enforce: "post",
transform: (code) => {
if (!code.includes("import.meta.hot.accept(")) {
return null;
}
return `${code}
if (import.meta?.hot) {
import.meta.hot.accept(newModule => {
if (newModule?.default?.component) {
import.meta.hot.invalidate();
}
});
}
`;
},
};
}
12 changes: 11 additions & 1 deletion core/src/vite/plugins/preview-script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ let refresh = () => {};
window.__PREVIEWJS_IFRAME__.refresh = (options) => {
refresh(options);
if (options.refetchPreviewableModule) {
loadFreshPreviewableModule().then(previewableModule => refresh({
...options,
previewableModule,
}));
}
};
import.meta.hot.accept(["/${previewablePath}"], ([previewableModule]) => {
Expand Down Expand Up @@ -82,7 +88,7 @@ const wrapperModulePromise = Promise.all([${detectedGlobalCssFilePaths
// Important: the wrapper must be loaded first as it may monkey-patch
// modules imported by the component module.
wrapperModulePromise.then(wrapperModule => {
import(/* @vite-ignore */ "/${previewablePath}?t=" + Date.now()).then(previewableModule => {
loadFreshPreviewableModule().then(previewableModule => {
refresh = initPreview({
previewableModule,
previewableName: ${JSON.stringify(previewableName)},
Expand All @@ -91,5 +97,9 @@ wrapperModulePromise.then(wrapperModule => {
});
});
});
function loadFreshPreviewableModule() {
return import(/* @vite-ignore */ "/${previewablePath}?t=" + Date.now());
}
`;
}
2 changes: 2 additions & 0 deletions core/src/vite/vite-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { findFiles } from "../find-files.js";
import { generateHtmlError } from "../html-error.js";
import type { FrameworkPlugin } from "../plugins/framework.js";
import { cssModulesWithoutSuffixPlugin } from "./plugins/css-modules-without-suffix-plugin.js";
import { invalidateStoriesModulesOnUpdate } from "./plugins/invalidate-stories-modules-on-update.js";
import { localEval } from "./plugins/local-eval.js";
import {
previewScriptPlugin,
Expand Down Expand Up @@ -343,6 +344,7 @@ export class ViteManager {
esbuildOptions: frameworkPluginViteConfig.esbuild || {},
}),
localEval(),
invalidateStoriesModulesOnUpdate(),
fakeExportedTypesPlugin({
readFile: (absoluteFilePath) =>
this.options.reader.read(absoluteFilePath).then((entry) => {
Expand Down
90 changes: 80 additions & 10 deletions framework-plugins/preact/tests/storybook.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ test.describe.parallel("preact/storybook", () => {
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hello, World!')]"
);
await preview.fileManager.update("src/Button.tsx", {
replace: "Hello, World!",
with: "Hi, World!",
});
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hi, World!')]"
);
});

test("renders CSF2 story with explicit args", async (preview) => {
Expand All @@ -45,6 +52,13 @@ test.describe.parallel("preact/storybook", () => {
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'explicit')]"
);
await preview.fileManager.update("src/Button.tsx", {
replace: "explicit",
with: "Hi, World!",
});
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hi, World!')]"
);
});

test("renders CSF2 story with assignment source referring local variable", async (preview) => {
Expand Down Expand Up @@ -74,37 +88,51 @@ test.describe.parallel("preact/storybook", () => {
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'local value')]"
);
await preview.fileManager.update("src/Button.tsx", {
replace: "local value",
with: "Hi, World!",
});
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hi, World!')]"
);
});

test("renders CSF2 story with default args", async (preview) => {
test("renders CSF2 story with default export args", async (preview) => {
await preview.fileManager.update(
"src/Button.tsx",
`const Button = ({ label }) => <button>{label}</button>;
export default {
component: Button,
args: {
label: "default"
label: "default export"
}
};
export const ButtonStory = Button.bind({});`
);
await preview.show("src/Button.tsx:ButtonStory");
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'default')]"
"xpath=//button[contains(., 'default export')]"
);
await preview.fileManager.update("src/Button.tsx", {
replace: "default export",
with: "Hi, World!",
});
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hi, World!')]"
);
});

test("renders CSF2 story with explicit args over default args", async (preview) => {
test("renders CSF2 story with explicit args over default export args", async (preview) => {
await preview.fileManager.update(
"src/Button.tsx",
`const Button = ({ label }) => <button>{label}</button>;
export default {
component: Button,
args: {
label: "default"
label: "default export"
}
};
Expand All @@ -117,6 +145,13 @@ test.describe.parallel("preact/storybook", () => {
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'explicit')]"
);
await preview.fileManager.update("src/Button.tsx", {
replace: "explicit",
with: "Hi, World!",
});
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hi, World!')]"
);
});

test("renders CSF3 story with explicit args", async (preview) => {
Expand All @@ -138,6 +173,13 @@ test.describe.parallel("preact/storybook", () => {
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'explicit')]"
);
await preview.fileManager.update("src/Button.tsx", {
replace: "explicit",
with: "Hi, World!",
});
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hi, World!')]"
);
});

test("renders CSF3 story with assignment source referring local variable", async (preview) => {
Expand Down Expand Up @@ -168,37 +210,51 @@ test.describe.parallel("preact/storybook", () => {
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'local value')]"
);
await preview.fileManager.update("src/Button.tsx", {
replace: "local value",
with: "Hi, World!",
});
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hi, World!')]"
);
});

test("renders CSF3 story with default args", async (preview) => {
test("renders CSF3 story with default export args", async (preview) => {
await preview.fileManager.update(
"src/Button.tsx",
`const Button = ({ label }) => <button>{label}</button>;
export default {
component: Button,
args: {
label: "default"
label: "default export"
}
}
export const ButtonStory = {};`
);
await preview.show("src/Button.tsx:ButtonStory");
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'default')]"
"xpath=//button[contains(., 'default export')]"
);
await preview.fileManager.update("src/Button.tsx", {
replace: "default export",
with: "Hi, World!",
});
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hi, World!')]"
);
});

test("renders CSF3 story with explicit args over default args", async (preview) => {
test("renders CSF3 story with explicit args over default export args", async (preview) => {
await preview.fileManager.update(
"src/Button.tsx",
`const Button = ({ label }) => <button>{label}</button>;
export default {
component: Button,
args: {
label: "default"
label: "default export"
}
};
Expand All @@ -212,6 +268,13 @@ test.describe.parallel("preact/storybook", () => {
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'explicit')]"
);
await preview.fileManager.update("src/Button.tsx", {
replace: "explicit",
with: "Hi, World!",
});
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hi, World!')]"
);
});

test("renders CSF3 story with render function", async (preview) => {
Expand All @@ -234,5 +297,12 @@ test.describe.parallel("preact/storybook", () => {
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hello, World!')]"
);
await preview.fileManager.update("src/Button.tsx", {
replace: "Hello, World!",
with: "Hi, World!",
});
await preview.iframe.waitForSelector(
"xpath=//button[contains(., 'Hi, World!')]"
);
});
});
Loading

0 comments on commit 8174516

Please sign in to comment.