From 483325a656e9884d4322d0ed38a9a518b9ea1f8f Mon Sep 17 00:00:00 2001
From: Valentin Palkovic
Date: Wed, 4 Sep 2024 10:20:57 +0200
Subject: [PATCH 01/13] Portable Stories: Improve Handling of React Updates and
Errors
Co-authored-by: Yann Braga
Co-authored-by: Jeppe Reinhold
---
.../modules/store/csf/portable-stories.ts | 4 +
.../react-dom-shim/src/preventActChecks.tsx | 17 --
code/lib/react-dom-shim/src/react-16.tsx | 6 +-
code/lib/react-dom-shim/src/react-18.tsx | 23 +-
code/renderers/react/package.json | 4 +
.../react/src/__test__/Button.stories.tsx | 9 +-
.../__test__/ComponentWithError.stories.tsx | 13 +
.../react/src/__test__/ComponentWithError.tsx | 4 +
.../portable-stories-legacy.test.tsx.snap | 34 +++
.../__test__/portable-stories-legacy.test.tsx | 6 +-
.../src/__test__/portable-stories.test.tsx | 90 +++---
code/renderers/react/src/act-compat.ts | 65 +++++
code/renderers/react/src/portable-stories.tsx | 80 +++++-
code/renderers/react/src/renderToCanvas.tsx | 7 +-
code/vitest-setup.ts | 1 +
code/yarn.lock | 269 +++++++++++++++++-
16 files changed, 557 insertions(+), 75 deletions(-)
delete mode 100644 code/lib/react-dom-shim/src/preventActChecks.tsx
create mode 100644 code/renderers/react/src/__test__/ComponentWithError.stories.tsx
create mode 100644 code/renderers/react/src/__test__/ComponentWithError.tsx
create mode 100644 code/renderers/react/src/act-compat.ts
diff --git a/code/core/src/preview-api/modules/store/csf/portable-stories.ts b/code/core/src/preview-api/modules/store/csf/portable-stories.ts
index 1525b6e3e6d8..7adc83196eb1 100644
--- a/code/core/src/preview-api/modules/store/csf/portable-stories.ts
+++ b/code/core/src/preview-api/modules/store/csf/portable-stories.ts
@@ -74,6 +74,10 @@ export function setProjectAnnotations(
| NamedOrDefaultProjectAnnotations[]
): NormalizedProjectAnnotations {
const annotations = Array.isArray(projectAnnotations) ? projectAnnotations : [projectAnnotations];
+ if (globalThis.defaultProjectAnnotations) {
+ annotations.push(globalThis.defaultProjectAnnotations);
+ }
+
globalThis.globalProjectAnnotations = composeConfigs(annotations.map(extractAnnotation));
return globalThis.globalProjectAnnotations;
diff --git a/code/lib/react-dom-shim/src/preventActChecks.tsx b/code/lib/react-dom-shim/src/preventActChecks.tsx
deleted file mode 100644
index f35e2fb25dc5..000000000000
--- a/code/lib/react-dom-shim/src/preventActChecks.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-export {};
-
-declare const globalThis: {
- IS_REACT_ACT_ENVIRONMENT?: boolean;
-};
-
-// TODO(9.0): We should actually wrap all those lines in `act`, but that might be a breaking change.
-// We should make that breaking change for SB 9.0
-export function preventActChecks(callback: () => void): void {
- const originalActEnvironment = globalThis.IS_REACT_ACT_ENVIRONMENT;
- globalThis.IS_REACT_ACT_ENVIRONMENT = false;
- try {
- callback();
- } finally {
- globalThis.IS_REACT_ACT_ENVIRONMENT = originalActEnvironment;
- }
-}
diff --git a/code/lib/react-dom-shim/src/react-16.tsx b/code/lib/react-dom-shim/src/react-16.tsx
index a1e7b1e97009..8c7b2c8f5a67 100644
--- a/code/lib/react-dom-shim/src/react-16.tsx
+++ b/code/lib/react-dom-shim/src/react-16.tsx
@@ -2,14 +2,12 @@
import type { ReactElement } from 'react';
import * as ReactDOM from 'react-dom';
-import { preventActChecks } from './preventActChecks';
-
export const renderElement = async (node: ReactElement, el: Element) => {
return new Promise((resolve) => {
- preventActChecks(() => void ReactDOM.render(node, el, () => resolve(null)));
+ ReactDOM.render(node, el, () => resolve(null));
});
};
export const unmountElement = (el: Element) => {
- preventActChecks(() => void ReactDOM.unmountComponentAtNode(el));
+ ReactDOM.unmountComponentAtNode(el);
};
diff --git a/code/lib/react-dom-shim/src/react-18.tsx b/code/lib/react-dom-shim/src/react-18.tsx
index 5eb72b20eb17..f3398fc65ff0 100644
--- a/code/lib/react-dom-shim/src/react-18.tsx
+++ b/code/lib/react-dom-shim/src/react-18.tsx
@@ -1,15 +1,21 @@
/* eslint-disable @typescript-eslint/no-unnecessary-type-constraint */
-import type { FC, ReactElement } from 'react';
+import type { ReactElement } from 'react';
import * as React from 'react';
import type { Root as ReactRoot, RootOptions } from 'react-dom/client';
import * as ReactDOM from 'react-dom/client';
-import { preventActChecks } from './preventActChecks';
-
// A map of all rendered React 18 nodes
const nodes = new Map();
-const WithCallback: FC<{ callback: () => void; children: ReactElement }> = ({
+declare const globalThis: {
+ IS_REACT_ACT_ENVIRONMENT: boolean;
+};
+
+function getIsReactActEnvironment() {
+ return globalThis.IS_REACT_ACT_ENVIRONMENT;
+}
+
+const WithCallback: React.FC<{ callback: () => void; children: ReactElement }> = ({
callback,
children,
}) => {
@@ -43,8 +49,13 @@ export const renderElement = async (node: ReactElement, el: Element, rootOptions
// Create Root Element conditionally for new React 18 Root Api
const root = await getReactRoot(el, rootOptions);
+ if (getIsReactActEnvironment()) {
+ root.render(node);
+ return;
+ }
+
const { promise, resolve } = Promise.withResolvers();
- preventActChecks(() => root.render({node}));
+ root.render({node});
return promise;
};
@@ -52,7 +63,7 @@ export const unmountElement = (el: Element, shouldUseNewRootApi?: boolean) => {
const root = nodes.get(el);
if (root) {
- preventActChecks(() => root.unmount());
+ root.unmount();
nodes.delete(el);
}
};
diff --git a/code/renderers/react/package.json b/code/renderers/react/package.json
index 4d370bbb8a1e..003466f1b182 100644
--- a/code/renderers/react/package.json
+++ b/code/renderers/react/package.json
@@ -94,12 +94,16 @@
"require-from-string": "^2.0.2"
},
"peerDependencies": {
+ "@storybook/test": "workspace:*",
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta",
"storybook": "workspace:^",
"typescript": ">= 4.2.x"
},
"peerDependenciesMeta": {
+ "@storybook/test": {
+ "optional": true
+ },
"typescript": {
"optional": true
}
diff --git a/code/renderers/react/src/__test__/Button.stories.tsx b/code/renderers/react/src/__test__/Button.stories.tsx
index bde220fdf469..0e6e0d6e8c67 100644
--- a/code/renderers/react/src/__test__/Button.stories.tsx
+++ b/code/renderers/react/src/__test__/Button.stories.tsx
@@ -103,7 +103,6 @@ export const HooksStory: CSF3Story = {
);
},
play: async ({ canvasElement, step }) => {
- console.log('start of play function');
const canvas = within(canvasElement);
await step('Step label', async () => {
const inputEl = canvas.getByTestId('input');
@@ -112,8 +111,8 @@ export const HooksStory: CSF3Story = {
await userEvent.type(inputEl, 'Hello world!');
await expect(inputEl).toHaveValue('Hello world!');
+ await expect(buttonEl).toHaveTextContent('I am clicked');
});
- console.log('end of play function');
},
};
@@ -182,6 +181,12 @@ export const MountInPlayFunction: CSF3Story<{ mockFn: (val: string) => string }>
},
};
+export const MountInPlayFunctionThrow: CSF3Story<{ mockFn: (val: string) => string }> = {
+ play: async () => {
+ throw new Error('Error thrown in play');
+ },
+};
+
export const WithActionArg: CSF3Story<{ someActionArg: HandlerFunction }> = {
args: {
someActionArg: action('some-action-arg'),
diff --git a/code/renderers/react/src/__test__/ComponentWithError.stories.tsx b/code/renderers/react/src/__test__/ComponentWithError.stories.tsx
new file mode 100644
index 000000000000..627055e2d965
--- /dev/null
+++ b/code/renderers/react/src/__test__/ComponentWithError.stories.tsx
@@ -0,0 +1,13 @@
+import type { Meta, StoryObj } from '..';
+import { ComponentWithError } from './ComponentWithError';
+
+const meta = {
+ title: 'Example/ComponentWithError',
+ component: ComponentWithError as any,
+} satisfies Meta;
+
+export default meta;
+
+type Story = StoryObj;
+
+export const ThrowsError: Story = {};
diff --git a/code/renderers/react/src/__test__/ComponentWithError.tsx b/code/renderers/react/src/__test__/ComponentWithError.tsx
new file mode 100644
index 000000000000..37f667cb4f2c
--- /dev/null
+++ b/code/renderers/react/src/__test__/ComponentWithError.tsx
@@ -0,0 +1,4 @@
+export function ComponentWithError() {
+ // eslint-disable-next-line local-rules/no-uncategorized-errors
+ throw new Error('Error in render');
+}
diff --git a/code/renderers/react/src/__test__/__snapshots__/portable-stories-legacy.test.tsx.snap b/code/renderers/react/src/__test__/__snapshots__/portable-stories-legacy.test.tsx.snap
index b4753327aaf1..b690349bed8d 100644
--- a/code/renderers/react/src/__test__/__snapshots__/portable-stories-legacy.test.tsx.snap
+++ b/code/renderers/react/src/__test__/__snapshots__/portable-stories-legacy.test.tsx.snap
@@ -147,6 +147,40 @@ exports[`Legacy Portable Stories API > Renders Modal story 1`] = `
+
diff --git a/code/renderers/react/src/__test__/portable-stories-legacy.test.tsx b/code/renderers/react/src/__test__/portable-stories-legacy.test.tsx
index 3c7321cdfe63..5567b1fd9fbc 100644
--- a/code/renderers/react/src/__test__/portable-stories-legacy.test.tsx
+++ b/code/renderers/react/src/__test__/portable-stories-legacy.test.tsx
@@ -200,7 +200,11 @@ describe('Legacy Portable Stories API', () => {
it.each(testCases)('Renders %s story', async (_storyName, Story) => {
cleanup();
- if (_storyName === 'CSF2StoryWithLocale' || _storyName === 'MountInPlayFunction') {
+ if (
+ _storyName === 'CSF2StoryWithLocale' ||
+ _storyName === 'MountInPlayFunction' ||
+ _storyName === 'MountInPlayFunctionThrow'
+ ) {
return;
}
diff --git a/code/renderers/react/src/__test__/portable-stories.test.tsx b/code/renderers/react/src/__test__/portable-stories.test.tsx
index 90346edff991..94de89e093a5 100644
--- a/code/renderers/react/src/__test__/portable-stories.test.tsx
+++ b/code/renderers/react/src/__test__/portable-stories.test.tsx
@@ -2,7 +2,7 @@
/* eslint-disable import/namespace */
import { cleanup, render, screen } from '@testing-library/react';
-import { afterEach, describe, expect, it, vi } from 'vitest';
+import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
import React from 'react';
@@ -16,23 +16,28 @@ import { expectTypeOf } from 'expect-type';
import { composeStories, composeStory, setProjectAnnotations } from '..';
import type { Button } from './Button';
-import * as stories from './Button.stories';
+import * as ButtonStories from './Button.stories';
+import * as ComponentWithErrorStories from './ComponentWithError.stories';
-setProjectAnnotations([]);
+const HooksStory = composeStory(ButtonStories.HooksStory, ButtonStories.default);
+
+const projectAnnotations = setProjectAnnotations([]);
// example with composeStories, returns an object with all stories composed with args/decorators
-const { CSF3Primary, LoaderStory, MountInPlayFunction } = composeStories(stories);
+const { CSF3Primary, LoaderStory, MountInPlayFunction, MountInPlayFunctionThrow } =
+ composeStories(ButtonStories);
+const { ThrowsError } = composeStories(ComponentWithErrorStories);
+
+beforeAll(async () => {
+ await projectAnnotations.beforeAll?.();
+});
afterEach(() => {
cleanup();
});
-declare const globalThis: {
- IS_REACT_ACT_ENVIRONMENT?: boolean;
-};
-
// example with composeStory, returns a single story composed with args/decorators
-const Secondary = composeStory(stories.CSF2Secondary, stories.default);
+const Secondary = composeStory(ButtonStories.CSF2Secondary, ButtonStories.default);
describe('renders', () => {
it('renders primary button', () => {
render(
Hello world);
@@ -60,6 +65,10 @@ describe('renders', () => {
expect(buttonElement).not.toBeNull();
});
+ it('should throw error when rendering a component with a render error', async () => {
+ await expect(() => ThrowsError.run()).rejects.toThrowError('Error in render');
+ });
+
it('should render component mounted in play function', async () => {
await MountInPlayFunction.run();
@@ -67,6 +76,10 @@ describe('renders', () => {
expect(screen.getByTestId('loaded-data').textContent).toEqual('loaded data');
});
+ it('should throw an error in play function', () => {
+ expect(() => MountInPlayFunctionThrow.run()).rejects.toThrowError('Error thrown in play');
+ });
+
it('should call and compose loaders data', async () => {
await LoaderStory.load();
const { getByTestId } = render(
);
@@ -78,10 +91,6 @@ describe('renders', () => {
});
describe('projectAnnotations', () => {
- afterEach(() => {
- cleanup();
- });
-
it('renders with default projectAnnotations', () => {
setProjectAnnotations([
{
@@ -91,7 +100,7 @@ describe('projectAnnotations', () => {
},
},
]);
- const WithEnglishText = composeStory(stories.CSF2StoryWithLocale, stories.default);
+ const WithEnglishText = composeStory(ButtonStories.CSF2StoryWithLocale, ButtonStories.default);
const { getByText } = render(
);
const buttonElement = getByText('Hello!');
expect(buttonElement).not.toBeNull();
@@ -99,24 +108,31 @@ describe('projectAnnotations', () => {
});
it('renders with custom projectAnnotations via composeStory params', () => {
- const WithPortugueseText = composeStory(stories.CSF2StoryWithLocale, stories.default, {
- initialGlobals: { locale: 'pt' },
- });
+ const WithPortugueseText = composeStory(
+ ButtonStories.CSF2StoryWithLocale,
+ ButtonStories.default,
+ {
+ initialGlobals: { locale: 'pt' },
+ }
+ );
const { getByText } = render(
);
const buttonElement = getByText('Olá!');
expect(buttonElement).not.toBeNull();
});
it('has action arg from argTypes when addon-actions annotations are added', () => {
- //@ts-expect-error our tsconfig.jsn#moduleResulution is set to 'node', which doesn't support this import
- const Story = composeStory(stories.WithActionArgType, stories.default, addonActionsPreview);
+ const Story = composeStory(
+ ButtonStories.WithActionArgType,
+ ButtonStories.default,
+ addonActionsPreview
+ );
expect(Story.args.someActionArg).toHaveProperty('isAction', true);
});
});
describe('CSF3', () => {
it('renders with inferred globalRender', () => {
- const Primary = composeStory(stories.CSF3Button, stories.default);
+ const Primary = composeStory(ButtonStories.CSF3Button, ButtonStories.default);
render(
Hello world);
const buttonElement = screen.getByText(/Hello world/i);
@@ -124,14 +140,17 @@ describe('CSF3', () => {
});
it('renders with custom render function', () => {
- const Primary = composeStory(stories.CSF3ButtonWithRender, stories.default);
+ const Primary = composeStory(ButtonStories.CSF3ButtonWithRender, ButtonStories.default);
render(
);
expect(screen.getByTestId('custom-render')).not.toBeNull();
});
it('renders with play function without canvas element', async () => {
- const CSF3InputFieldFilled = composeStory(stories.CSF3InputFieldFilled, stories.default);
+ const CSF3InputFieldFilled = composeStory(
+ ButtonStories.CSF3InputFieldFilled,
+ ButtonStories.default
+ );
await CSF3InputFieldFilled.run();
const input = screen.getByTestId('input') as HTMLInputElement;
@@ -139,7 +158,10 @@ describe('CSF3', () => {
});
it('renders with play function with canvas element', async () => {
- const CSF3InputFieldFilled = composeStory(stories.CSF3InputFieldFilled, stories.default);
+ const CSF3InputFieldFilled = composeStory(
+ ButtonStories.CSF3InputFieldFilled,
+ ButtonStories.default
+ );
const div = document.createElement('div');
document.body.appendChild(div);
@@ -153,21 +175,16 @@ describe('CSF3', () => {
});
it('renders with hooks', async () => {
- // TODO find out why act is not working here
- globalThis.IS_REACT_ACT_ENVIRONMENT = false;
- const HooksStory = composeStory(stories.HooksStory, stories.default);
-
await HooksStory.run();
const input = screen.getByTestId('input') as HTMLInputElement;
expect(input.value).toEqual('Hello world!');
- globalThis.IS_REACT_ACT_ENVIRONMENT = true;
});
});
// common in addons that need to communicate between manager and preview
it('should pass with decorators that need addons channel', () => {
- const PrimaryWithChannels = composeStory(stories.CSF3Primary, stories.default, {
+ const PrimaryWithChannels = composeStory(ButtonStories.CSF3Primary, ButtonStories.default, {
decorators: [
(StoryFn: any) => {
addons.getChannel();
@@ -186,27 +203,24 @@ describe('ComposeStories types', () => {
type ComposeStoriesParam = Parameters
[0];
expectTypeOf({
- ...stories,
- default: stories.default as Meta,
+ ...ButtonStories,
+ default: ButtonStories.default as Meta,
}).toMatchTypeOf();
expectTypeOf({
- ...stories,
- default: stories.default satisfies Meta,
+ ...ButtonStories,
+ default: ButtonStories.default satisfies Meta,
}).toMatchTypeOf();
});
});
-// Batch snapshot testing
-const testCases = Object.values(composeStories(stories)).map(
+const testCases = Object.values(composeStories(ButtonStories)).map(
(Story) => [Story.storyName, Story] as [string, typeof Story]
);
it.each(testCases)('Renders %s story', async (_storyName, Story) => {
- if (_storyName === 'CSF2StoryWithLocale') {
+ if (_storyName === 'CSF2StoryWithLocale' || _storyName === 'MountInPlayFunctionThrow') {
return;
}
- globalThis.IS_REACT_ACT_ENVIRONMENT = false;
await Story.run();
- globalThis.IS_REACT_ACT_ENVIRONMENT = true;
expect(document.body).toMatchSnapshot();
});
diff --git a/code/renderers/react/src/act-compat.ts b/code/renderers/react/src/act-compat.ts
new file mode 100644
index 000000000000..afe1cc902316
--- /dev/null
+++ b/code/renderers/react/src/act-compat.ts
@@ -0,0 +1,65 @@
+// Copied from
+// https://github.com/testing-library/react-testing-library/blob/3dcd8a9649e25054c0e650d95fca2317b7008576/src/act-compat.js
+import * as React from 'react';
+
+import * as DeprecatedReactTestUtils from 'react-dom/test-utils';
+
+declare const globalThis: {
+ IS_REACT_ACT_ENVIRONMENT: boolean;
+};
+
+// @ts-expect-error act might not be available in some versions of React
+const reactAct = typeof React.act === 'function' ? React.act : DeprecatedReactTestUtils.act;
+
+export function setReactActEnvironment(isReactActEnvironment: boolean) {
+ globalThis.IS_REACT_ACT_ENVIRONMENT = isReactActEnvironment;
+}
+
+export function getReactActEnvironment() {
+ return globalThis.IS_REACT_ACT_ENVIRONMENT;
+}
+
+function withGlobalActEnvironment(actImplementation: (callback: () => void) => Promise) {
+ return (callback: () => any) => {
+ const previousActEnvironment = getReactActEnvironment();
+ setReactActEnvironment(true);
+ try {
+ // The return value of `act` is always a thenable.
+ let callbackNeedsToBeAwaited = false;
+ const actResult = actImplementation(() => {
+ const result = callback();
+ if (result !== null && typeof result === 'object' && typeof result.then === 'function') {
+ callbackNeedsToBeAwaited = true;
+ }
+ return result;
+ });
+ if (callbackNeedsToBeAwaited) {
+ const thenable: Promise = actResult;
+ return {
+ then: (resolve: (param: any) => void, reject: (param: any) => void) => {
+ thenable.then(
+ (returnValue) => {
+ setReactActEnvironment(previousActEnvironment);
+ resolve(returnValue);
+ },
+ (error) => {
+ setReactActEnvironment(previousActEnvironment);
+ reject(error);
+ }
+ );
+ },
+ };
+ } else {
+ setReactActEnvironment(previousActEnvironment);
+ return actResult;
+ }
+ } catch (error) {
+ // Can't be a `finally {}` block since we don't know if we have to immediately restore IS_REACT_ACT_ENVIRONMENT
+ // or if we have to await the callback first.
+ setReactActEnvironment(previousActEnvironment);
+ throw error;
+ }
+ };
+}
+
+export const act = withGlobalActEnvironment(reactAct);
diff --git a/code/renderers/react/src/portable-stories.tsx b/code/renderers/react/src/portable-stories.tsx
index 2ea196e85b4b..ced0bbd289e3 100644
--- a/code/renderers/react/src/portable-stories.tsx
+++ b/code/renderers/react/src/portable-stories.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import * as React from 'react';
import {
composeStories as originalComposeStories,
@@ -17,6 +17,7 @@ import type {
StoryAnnotationsOrFn,
} from 'storybook/internal/types';
+import { act, getReactActEnvironment, setReactActEnvironment } from './act-compat';
import * as reactProjectAnnotations from './entry-preview';
import type { Meta } from './public-types';
import type { ReactRenderer } from './types';
@@ -54,9 +55,66 @@ export function setProjectAnnotations(
// This will not be necessary once we have auto preset loading
export const INTERNAL_DEFAULT_PROJECT_ANNOTATIONS: ProjectAnnotations = {
...reactProjectAnnotations,
- renderToCanvas: (renderContext, canvasElement) => {
+ beforeAll: async function reactBeforeAll() {
+ try {
+ // copied from
+ // https://github.com/testing-library/react-testing-library/blob/3dcd8a9649e25054c0e650d95fca2317b7008576/src/pure.js
+ const { configure } = await import('@storybook/test');
+
+ configure({
+ unstable_advanceTimersWrapper: (cb) => {
+ return act(cb);
+ },
+ asyncWrapper: async (cb) => {
+ const previousActEnvironment = getReactActEnvironment();
+ setReactActEnvironment(false);
+ try {
+ const result = await cb();
+ // Drain microtask queue.
+ // Otherwise we'll restore the previous act() environment, before we resolve the `waitFor` call.
+ // The caller would have no chance to wrap the in-flight Promises in `act()`
+ await new Promise((resolve) => {
+ setTimeout(() => {
+ resolve();
+ }, 0);
+
+ if (jestFakeTimersAreEnabled()) {
+ // @ts-expect-error global jest
+ jest.advanceTimersByTime(0);
+ }
+ });
+
+ return result;
+ } finally {
+ setReactActEnvironment(previousActEnvironment);
+ }
+ },
+ eventWrapper: (cb) => {
+ let result;
+ act(() => {
+ result = cb();
+ });
+ return result;
+ },
+ });
+ } catch (e) {
+ console.log(e);
+ // @storybook/test might not be available
+ }
+ },
+ renderToCanvas: async (renderContext, canvasElement) => {
if (renderContext.storyContext.testingLibraryRender == null) {
- return reactProjectAnnotations.renderToCanvas(renderContext, canvasElement);
+ let unmount: () => void;
+
+ await act(async () => {
+ unmount = await reactProjectAnnotations.renderToCanvas(renderContext, canvasElement);
+ });
+
+ return async () => {
+ await act(() => {
+ unmount();
+ });
+ };
}
const {
storyContext: { context, unboundStoryFn: Story, testingLibraryRender: render },
@@ -149,3 +207,19 @@ export function composeStories;
}
+
+/** The function is used to configure jest's fake timers in environments where React's act is enabled */
+function jestFakeTimersAreEnabled() {
+ // @ts-expect-error global jest
+ if (typeof jest !== 'undefined' && jest !== null) {
+ return (
+ // legacy timers
+
+ // eslint-disable-next-line no-underscore-dangle
+ (setTimeout as any)._isMockFunction === true || // modern timers
+ Object.prototype.hasOwnProperty.call(setTimeout, 'clock')
+ );
+ }
+
+ return false;
+}
diff --git a/code/renderers/react/src/renderToCanvas.tsx b/code/renderers/react/src/renderToCanvas.tsx
index f3a4231d078c..3ae6136f9582 100644
--- a/code/renderers/react/src/renderToCanvas.tsx
+++ b/code/renderers/react/src/renderToCanvas.tsx
@@ -5,6 +5,7 @@ import type { RenderContext } from 'storybook/internal/types';
import { global } from '@storybook/global';
+import { getReactActEnvironment } from './act-compat';
import type { ReactRenderer, StoryContext } from './types';
const { FRAMEWORK_OPTIONS } = global;
@@ -57,7 +58,11 @@ export async function renderToCanvas(
const { renderElement, unmountElement } = await import('@storybook/react-dom-shim');
const Story = unboundStoryFn as FC>;
- const content = (
+ const isActEnabled = getReactActEnvironment();
+
+ const content = isActEnabled ? (
+
+ ) : (
diff --git a/code/vitest-setup.ts b/code/vitest-setup.ts
index 8edd64c36314..5eba16740d1d 100644
--- a/code/vitest-setup.ts
+++ b/code/vitest-setup.ts
@@ -7,6 +7,7 @@ const ignoreList = [
(error: any) => error.message.includes('":nth-child" is potentially unsafe'),
(error: any) => error.message.includes('":first-child" is potentially unsafe'),
(error: any) => error.message.match(/Browserslist: .* is outdated. Please run:/),
+ (error: any) => error.message.includes('Consider adding an error boundary'),
(error: any) =>
error.message.includes('react-async-component-lifecycle-hooks') &&
error.stack.includes('addons/knobs/src/components/__tests__/Options.js'),
diff --git a/code/yarn.lock b/code/yarn.lock
index 9b8a426f6016..397b6cd54ff5 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -2490,7 +2490,7 @@ __metadata:
languageName: node
linkType: hard
-"@emnapi/runtime@npm:^1.1.1":
+"@emnapi/runtime@npm:^1.1.1, @emnapi/runtime@npm:^1.2.0":
version: 1.2.0
resolution: "@emnapi/runtime@npm:1.2.0"
dependencies:
@@ -3424,6 +3424,18 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-darwin-arm64@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-darwin-arm64@npm:0.33.5"
+ dependencies:
+ "@img/sharp-libvips-darwin-arm64": "npm:1.0.4"
+ dependenciesMeta:
+ "@img/sharp-libvips-darwin-arm64":
+ optional: true
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@img/sharp-darwin-x64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-darwin-x64@npm:0.33.4"
@@ -3436,6 +3448,18 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-darwin-x64@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-darwin-x64@npm:0.33.5"
+ dependencies:
+ "@img/sharp-libvips-darwin-x64": "npm:1.0.4"
+ dependenciesMeta:
+ "@img/sharp-libvips-darwin-x64":
+ optional: true
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
"@img/sharp-libvips-darwin-arm64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-darwin-arm64@npm:1.0.2"
@@ -3443,6 +3467,13 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-libvips-darwin-arm64@npm:1.0.4":
+ version: 1.0.4
+ resolution: "@img/sharp-libvips-darwin-arm64@npm:1.0.4"
+ conditions: os=darwin & cpu=arm64
+ languageName: node
+ linkType: hard
+
"@img/sharp-libvips-darwin-x64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-darwin-x64@npm:1.0.2"
@@ -3450,6 +3481,13 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-libvips-darwin-x64@npm:1.0.4":
+ version: 1.0.4
+ resolution: "@img/sharp-libvips-darwin-x64@npm:1.0.4"
+ conditions: os=darwin & cpu=x64
+ languageName: node
+ linkType: hard
+
"@img/sharp-libvips-linux-arm64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linux-arm64@npm:1.0.2"
@@ -3457,6 +3495,13 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-libvips-linux-arm64@npm:1.0.4":
+ version: 1.0.4
+ resolution: "@img/sharp-libvips-linux-arm64@npm:1.0.4"
+ conditions: os=linux & cpu=arm64 & libc=glibc
+ languageName: node
+ linkType: hard
+
"@img/sharp-libvips-linux-arm@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linux-arm@npm:1.0.2"
@@ -3464,6 +3509,13 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-libvips-linux-arm@npm:1.0.5":
+ version: 1.0.5
+ resolution: "@img/sharp-libvips-linux-arm@npm:1.0.5"
+ conditions: os=linux & cpu=arm & libc=glibc
+ languageName: node
+ linkType: hard
+
"@img/sharp-libvips-linux-s390x@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linux-s390x@npm:1.0.2"
@@ -3471,6 +3523,13 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-libvips-linux-s390x@npm:1.0.4":
+ version: 1.0.4
+ resolution: "@img/sharp-libvips-linux-s390x@npm:1.0.4"
+ conditions: os=linux & cpu=s390x & libc=glibc
+ languageName: node
+ linkType: hard
+
"@img/sharp-libvips-linux-x64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linux-x64@npm:1.0.2"
@@ -3478,6 +3537,13 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-libvips-linux-x64@npm:1.0.4":
+ version: 1.0.4
+ resolution: "@img/sharp-libvips-linux-x64@npm:1.0.4"
+ conditions: os=linux & cpu=x64 & libc=glibc
+ languageName: node
+ linkType: hard
+
"@img/sharp-libvips-linuxmusl-arm64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.0.2"
@@ -3485,6 +3551,13 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-libvips-linuxmusl-arm64@npm:1.0.4":
+ version: 1.0.4
+ resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.0.4"
+ conditions: os=linux & cpu=arm64 & libc=musl
+ languageName: node
+ linkType: hard
+
"@img/sharp-libvips-linuxmusl-x64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.0.2"
@@ -3492,6 +3565,13 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-libvips-linuxmusl-x64@npm:1.0.4":
+ version: 1.0.4
+ resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.0.4"
+ conditions: os=linux & cpu=x64 & libc=musl
+ languageName: node
+ linkType: hard
+
"@img/sharp-linux-arm64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linux-arm64@npm:0.33.4"
@@ -3504,6 +3584,18 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-linux-arm64@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-linux-arm64@npm:0.33.5"
+ dependencies:
+ "@img/sharp-libvips-linux-arm64": "npm:1.0.4"
+ dependenciesMeta:
+ "@img/sharp-libvips-linux-arm64":
+ optional: true
+ conditions: os=linux & cpu=arm64 & libc=glibc
+ languageName: node
+ linkType: hard
+
"@img/sharp-linux-arm@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linux-arm@npm:0.33.4"
@@ -3516,6 +3608,18 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-linux-arm@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-linux-arm@npm:0.33.5"
+ dependencies:
+ "@img/sharp-libvips-linux-arm": "npm:1.0.5"
+ dependenciesMeta:
+ "@img/sharp-libvips-linux-arm":
+ optional: true
+ conditions: os=linux & cpu=arm & libc=glibc
+ languageName: node
+ linkType: hard
+
"@img/sharp-linux-s390x@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linux-s390x@npm:0.33.4"
@@ -3528,6 +3632,18 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-linux-s390x@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-linux-s390x@npm:0.33.5"
+ dependencies:
+ "@img/sharp-libvips-linux-s390x": "npm:1.0.4"
+ dependenciesMeta:
+ "@img/sharp-libvips-linux-s390x":
+ optional: true
+ conditions: os=linux & cpu=s390x & libc=glibc
+ languageName: node
+ linkType: hard
+
"@img/sharp-linux-x64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linux-x64@npm:0.33.4"
@@ -3540,6 +3656,18 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-linux-x64@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-linux-x64@npm:0.33.5"
+ dependencies:
+ "@img/sharp-libvips-linux-x64": "npm:1.0.4"
+ dependenciesMeta:
+ "@img/sharp-libvips-linux-x64":
+ optional: true
+ conditions: os=linux & cpu=x64 & libc=glibc
+ languageName: node
+ linkType: hard
+
"@img/sharp-linuxmusl-arm64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linuxmusl-arm64@npm:0.33.4"
@@ -3552,6 +3680,18 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-linuxmusl-arm64@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-linuxmusl-arm64@npm:0.33.5"
+ dependencies:
+ "@img/sharp-libvips-linuxmusl-arm64": "npm:1.0.4"
+ dependenciesMeta:
+ "@img/sharp-libvips-linuxmusl-arm64":
+ optional: true
+ conditions: os=linux & cpu=arm64 & libc=musl
+ languageName: node
+ linkType: hard
+
"@img/sharp-linuxmusl-x64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linuxmusl-x64@npm:0.33.4"
@@ -3564,6 +3704,18 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-linuxmusl-x64@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-linuxmusl-x64@npm:0.33.5"
+ dependencies:
+ "@img/sharp-libvips-linuxmusl-x64": "npm:1.0.4"
+ dependenciesMeta:
+ "@img/sharp-libvips-linuxmusl-x64":
+ optional: true
+ conditions: os=linux & cpu=x64 & libc=musl
+ languageName: node
+ linkType: hard
+
"@img/sharp-wasm32@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-wasm32@npm:0.33.4"
@@ -3573,6 +3725,15 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-wasm32@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-wasm32@npm:0.33.5"
+ dependencies:
+ "@emnapi/runtime": "npm:^1.2.0"
+ conditions: cpu=wasm32
+ languageName: node
+ linkType: hard
+
"@img/sharp-win32-ia32@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-win32-ia32@npm:0.33.4"
@@ -3580,6 +3741,13 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-win32-ia32@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-win32-ia32@npm:0.33.5"
+ conditions: os=win32 & cpu=ia32
+ languageName: node
+ linkType: hard
+
"@img/sharp-win32-x64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-win32-x64@npm:0.33.4"
@@ -3587,6 +3755,13 @@ __metadata:
languageName: node
linkType: hard
+"@img/sharp-win32-x64@npm:0.33.5":
+ version: 0.33.5
+ resolution: "@img/sharp-win32-x64@npm:0.33.5"
+ conditions: os=win32 & cpu=x64
+ languageName: node
+ linkType: hard
+
"@inquirer/confirm@npm:^3.0.0":
version: 3.1.20
resolution: "@inquirer/confirm@npm:3.1.20"
@@ -3903,13 +4078,20 @@ __metadata:
languageName: node
linkType: hard
-"@next/env@npm:14.2.5, @next/env@npm:^14.2.5":
+"@next/env@npm:14.2.5":
version: 14.2.5
resolution: "@next/env@npm:14.2.5"
checksum: 10c0/63d8b88ac450b3c37940a9e2119a63a1074aca89908574ade6157a8aa295275dcb3ac5f69e00883fc55d0f12963b73b74e87ba32a5768a489f9609c6be57b699
languageName: node
linkType: hard
+"@next/env@npm:^14.2.5":
+ version: 14.2.7
+ resolution: "@next/env@npm:14.2.7"
+ checksum: 10c0/1cda023007acda4d47036a25fba0e039d9b2df9c3770651dc289207e0537506675546c02b5b574fe92bb1adc1c887d948d5cb630673aa572754278b82d150b7e
+ languageName: node
+ linkType: hard
+
"@next/swc-darwin-arm64@npm:14.2.5":
version: 14.2.5
resolution: "@next/swc-darwin-arm64@npm:14.2.5"
@@ -6668,11 +6850,14 @@ __metadata:
type-fest: "npm:~2.19"
util-deprecate: "npm:^1.0.2"
peerDependencies:
+ "@storybook/test": "workspace:*"
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta
storybook: "workspace:^"
typescript: ">= 4.2.x"
peerDependenciesMeta:
+ "@storybook/test":
+ optional: true
typescript:
optional: true
languageName: unknown
@@ -25319,6 +25504,15 @@ __metadata:
languageName: node
linkType: hard
+"semver@npm:^7.6.3":
+ version: 7.6.3
+ resolution: "semver@npm:7.6.3"
+ bin:
+ semver: bin/semver.js
+ checksum: 10c0/88f33e148b210c153873cb08cfe1e281d518aaa9a666d4d148add6560db5cd3c582f3a08ccb91f38d5f379ead256da9931234ed122057f40bb5766e65e58adaf
+ languageName: node
+ linkType: hard
+
"send@npm:0.18.0":
version: 0.18.0
resolution: "send@npm:0.18.0"
@@ -25470,7 +25664,7 @@ __metadata:
languageName: node
linkType: hard
-"sharp@npm:^0.33.3, sharp@npm:^0.33.4":
+"sharp@npm:^0.33.3":
version: 0.33.4
resolution: "sharp@npm:0.33.4"
dependencies:
@@ -25539,6 +25733,75 @@ __metadata:
languageName: node
linkType: hard
+"sharp@npm:^0.33.4":
+ version: 0.33.5
+ resolution: "sharp@npm:0.33.5"
+ dependencies:
+ "@img/sharp-darwin-arm64": "npm:0.33.5"
+ "@img/sharp-darwin-x64": "npm:0.33.5"
+ "@img/sharp-libvips-darwin-arm64": "npm:1.0.4"
+ "@img/sharp-libvips-darwin-x64": "npm:1.0.4"
+ "@img/sharp-libvips-linux-arm": "npm:1.0.5"
+ "@img/sharp-libvips-linux-arm64": "npm:1.0.4"
+ "@img/sharp-libvips-linux-s390x": "npm:1.0.4"
+ "@img/sharp-libvips-linux-x64": "npm:1.0.4"
+ "@img/sharp-libvips-linuxmusl-arm64": "npm:1.0.4"
+ "@img/sharp-libvips-linuxmusl-x64": "npm:1.0.4"
+ "@img/sharp-linux-arm": "npm:0.33.5"
+ "@img/sharp-linux-arm64": "npm:0.33.5"
+ "@img/sharp-linux-s390x": "npm:0.33.5"
+ "@img/sharp-linux-x64": "npm:0.33.5"
+ "@img/sharp-linuxmusl-arm64": "npm:0.33.5"
+ "@img/sharp-linuxmusl-x64": "npm:0.33.5"
+ "@img/sharp-wasm32": "npm:0.33.5"
+ "@img/sharp-win32-ia32": "npm:0.33.5"
+ "@img/sharp-win32-x64": "npm:0.33.5"
+ color: "npm:^4.2.3"
+ detect-libc: "npm:^2.0.3"
+ semver: "npm:^7.6.3"
+ dependenciesMeta:
+ "@img/sharp-darwin-arm64":
+ optional: true
+ "@img/sharp-darwin-x64":
+ optional: true
+ "@img/sharp-libvips-darwin-arm64":
+ optional: true
+ "@img/sharp-libvips-darwin-x64":
+ optional: true
+ "@img/sharp-libvips-linux-arm":
+ optional: true
+ "@img/sharp-libvips-linux-arm64":
+ optional: true
+ "@img/sharp-libvips-linux-s390x":
+ optional: true
+ "@img/sharp-libvips-linux-x64":
+ optional: true
+ "@img/sharp-libvips-linuxmusl-arm64":
+ optional: true
+ "@img/sharp-libvips-linuxmusl-x64":
+ optional: true
+ "@img/sharp-linux-arm":
+ optional: true
+ "@img/sharp-linux-arm64":
+ optional: true
+ "@img/sharp-linux-s390x":
+ optional: true
+ "@img/sharp-linux-x64":
+ optional: true
+ "@img/sharp-linuxmusl-arm64":
+ optional: true
+ "@img/sharp-linuxmusl-x64":
+ optional: true
+ "@img/sharp-wasm32":
+ optional: true
+ "@img/sharp-win32-ia32":
+ optional: true
+ "@img/sharp-win32-x64":
+ optional: true
+ checksum: 10c0/6b81421ddfe6ee524d8d77e325c5e147fef22884e1c7b1656dfd89a88d7025894115da02d5f984261bf2e6daa16f98cadd1721c4ba408b4212b1d2a60f233484
+ languageName: node
+ linkType: hard
+
"shebang-command@npm:^1.2.0":
version: 1.2.0
resolution: "shebang-command@npm:1.2.0"
From da21bfecb75a64abeb40a23fa01ec32daccbb40d Mon Sep 17 00:00:00 2001
From: Valentin Palkovic
Date: Wed, 4 Sep 2024 13:00:05 +0200
Subject: [PATCH 02/13] Next.js: Update webpack configuration to support
react-dom/test-utils
---
code/frameworks/nextjs/src/config/webpack.ts | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/code/frameworks/nextjs/src/config/webpack.ts b/code/frameworks/nextjs/src/config/webpack.ts
index 3860207e124e..3e0d758c1514 100644
--- a/code/frameworks/nextjs/src/config/webpack.ts
+++ b/code/frameworks/nextjs/src/config/webpack.ts
@@ -25,9 +25,17 @@ export const configureConfig = async ({
if (tryResolve('next/dist/compiled/react')) {
addScopedAlias(baseConfig, 'react', 'next/dist/compiled/react');
}
+ if (tryResolve('next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js')) {
+ addScopedAlias(
+ baseConfig,
+ 'react-dom/test-utils',
+ 'next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js'
+ );
+ }
if (tryResolve('next/dist/compiled/react-dom')) {
- addScopedAlias(baseConfig, 'react-dom', 'next/dist/compiled/react-dom');
+ addScopedAlias(baseConfig, 'react-dom$', 'next/dist/compiled/react-dom');
}
+
setupRuntimeConfig(baseConfig, nextConfig);
return nextConfig;
From da2aee4928b7cc5ba104df99ec9a62873a4b3b3b Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Thu, 5 Sep 2024 09:12:51 +0200
Subject: [PATCH 03/13] name decorators for easier debugging
---
code/core/template/stories/preview.ts | 16 +-
code/frameworks/sveltekit/src/preview.ts | 222 +++++++++++------------
2 files changed, 119 insertions(+), 119 deletions(-)
diff --git a/code/core/template/stories/preview.ts b/code/core/template/stories/preview.ts
index 4cd4c64abff5..bba2716864bc 100644
--- a/code/core/template/stories/preview.ts
+++ b/code/core/template/stories/preview.ts
@@ -30,14 +30,14 @@ export const parameters = {
export const loaders = [async () => ({ projectValue: 2 })];
-export const decorators = [
- (storyFn: PartialStoryFn, context: StoryContext) => {
- if (context.parameters.useProjectDecorator) {
- return storyFn({ args: { ...context.args, text: `project ${context.args.text}` } });
- }
- return storyFn();
- },
-];
+const testProjectDecorator = (storyFn: PartialStoryFn, context: StoryContext) => {
+ if (context.parameters.useProjectDecorator) {
+ return storyFn({ args: { ...context.args, text: `project ${context.args.text}` } });
+ }
+ return storyFn();
+};
+
+export const decorators = [testProjectDecorator];
export const initialGlobals = {
foo: 'fooValue',
diff --git a/code/frameworks/sveltekit/src/preview.ts b/code/frameworks/sveltekit/src/preview.ts
index f93c06862c29..c93cbde37a55 100644
--- a/code/frameworks/sveltekit/src/preview.ts
+++ b/code/frameworks/sveltekit/src/preview.ts
@@ -15,125 +15,125 @@ const normalizeHrefConfig = (hrefConfig: HrefConfig): NormalizedHrefConfig => {
return hrefConfig;
};
-export const decorators: Decorator[] = [
- (Story, ctx) => {
- const svelteKitParameters: SvelteKitParameters = ctx.parameters?.sveltekit_experimental ?? {};
- setPage(svelteKitParameters?.stores?.page);
- setNavigating(svelteKitParameters?.stores?.navigating);
- setUpdated(svelteKitParameters?.stores?.updated);
- setAfterNavigateArgument(svelteKitParameters?.navigation?.afterNavigate);
+const svelteKitMocksDecorator = (Story, ctx) => {
+ const svelteKitParameters: SvelteKitParameters = ctx.parameters?.sveltekit_experimental ?? {};
+ setPage(svelteKitParameters?.stores?.page);
+ setNavigating(svelteKitParameters?.stores?.navigating);
+ setUpdated(svelteKitParameters?.stores?.updated);
+ setAfterNavigateArgument(svelteKitParameters?.navigation?.afterNavigate);
- onMount(() => {
- const globalClickListener = (e: MouseEvent) => {
- // we add a global click event listener and we check if there's a link in the composedPath
- const path = e.composedPath();
- const element = path.findLast((el) => el instanceof HTMLElement && el.tagName === 'A');
- if (element && element instanceof HTMLAnchorElement) {
- // if the element is an a-tag we get the href of the element
- // and compare it to the hrefs-parameter set by the user
- const to = element.getAttribute('href');
- if (!to) {
- return;
- }
- e.preventDefault();
- const defaultActionCallback = () => action('navigate')(to, e);
- if (!svelteKitParameters.hrefs) {
- defaultActionCallback();
- return;
- }
-
- let callDefaultCallback = true;
- // we loop over every href set by the user and check if the href matches
- // if it does we call the callback provided by the user and disable the default callback
- Object.entries(svelteKitParameters.hrefs).forEach(([href, hrefConfig]) => {
- const { callback, asRegex } = normalizeHrefConfig(hrefConfig);
- const isMatch = asRegex ? new RegExp(href).test(to) : to === href;
- if (isMatch) {
- callDefaultCallback = false;
- callback?.(to, e);
- }
- });
- if (callDefaultCallback) {
- defaultActionCallback();
- }
+ onMount(() => {
+ const globalClickListener = (e: MouseEvent) => {
+ // we add a global click event listener and we check if there's a link in the composedPath
+ const path = e.composedPath();
+ const element = path.findLast((el) => el instanceof HTMLElement && el.tagName === 'A');
+ if (element && element instanceof HTMLAnchorElement) {
+ // if the element is an a-tag we get the href of the element
+ // and compare it to the hrefs-parameter set by the user
+ const to = element.getAttribute('href');
+ if (!to) {
+ return;
+ }
+ e.preventDefault();
+ const defaultActionCallback = () => action('navigate')(to, e);
+ if (!svelteKitParameters.hrefs) {
+ defaultActionCallback();
+ return;
}
- };
-
- /**
- * Function that create and add listeners for the event that are emitted by the mocked
- * functions. The event name is based on the function name
- *
- * Eg. storybook:goto, storybook:invalidateAll
- *
- * @param baseModule The base module where the function lives (navigation|forms)
- * @param functions The list of functions in that module that emit events
- * @param {boolean} [defaultToAction] The list of functions in that module that emit events
- * @returns A function to remove all the listener added
- */
- function createListeners(
- baseModule: keyof SvelteKitParameters,
- functions: string[],
- defaultToAction?: boolean
- ) {
- // the array of every added listener, we can use this in the return function
- // to clean them
- const toRemove: Array<{
- eventType: string;
- listener: (event: { detail: any[] }) => void;
- }> = [];
- functions.forEach((func) => {
- // we loop over every function and check if the user actually passed
- // a function in sveltekit_experimental[baseModule][func] eg. sveltekit_experimental.navigation.goto
- const hasFunction =
- (svelteKitParameters as any)[baseModule]?.[func] &&
- (svelteKitParameters as any)[baseModule][func] instanceof Function;
- // if we default to an action we still add the listener (this will be the case for goto, invalidate, invalidateAll)
- if (hasFunction || defaultToAction) {
- // we create the listener that will just get the detail array from the custom element
- // and call the user provided function spreading this args in...this will basically call
- // the function that the user provide with the same arguments the function is invoked to
- // eg. if it calls goto("/my-route") inside the component the function sveltekit_experimental.navigation.goto
- // it provided to storybook will be called with "/my-route"
- const listener = ({ detail = [] as any[] }) => {
- const args = Array.isArray(detail) ? detail : [];
- // if it has a function in the parameters we call that function
- // otherwise we invoke the action
- const fnToCall = hasFunction
- ? (svelteKitParameters as any)[baseModule][func]
- : action(func);
- fnToCall(...args);
- };
- const eventType = `storybook:${func}`;
- toRemove.push({ eventType, listener });
- // add the listener to window
- (window.addEventListener as any)(eventType, listener);
+ let callDefaultCallback = true;
+ // we loop over every href set by the user and check if the href matches
+ // if it does we call the callback provided by the user and disable the default callback
+ Object.entries(svelteKitParameters.hrefs).forEach(([href, hrefConfig]) => {
+ const { callback, asRegex } = normalizeHrefConfig(hrefConfig);
+ const isMatch = asRegex ? new RegExp(href).test(to) : to === href;
+ if (isMatch) {
+ callDefaultCallback = false;
+ callback?.(to, e);
}
});
- return () => {
- // loop over every listener added and remove them
- toRemove.forEach(({ eventType, listener }) => {
- // @ts-expect-error apparently you can't remove a custom listener to the window with TS
- window.removeEventListener(eventType, listener);
- });
- };
+ if (callDefaultCallback) {
+ defaultActionCallback();
+ }
}
+ };
- const removeNavigationListeners = createListeners(
- 'navigation',
- ['goto', 'invalidate', 'invalidateAll', 'pushState', 'replaceState'],
- true
- );
- const removeFormsListeners = createListeners('forms', ['enhance']);
- window.addEventListener('click', globalClickListener);
+ /**
+ * Function that create and add listeners for the event that are emitted by the mocked
+ * functions. The event name is based on the function name
+ *
+ * Eg. storybook:goto, storybook:invalidateAll
+ *
+ * @param baseModule The base module where the function lives (navigation|forms)
+ * @param functions The list of functions in that module that emit events
+ * @param {boolean} [defaultToAction] The list of functions in that module that emit events
+ * @returns A function to remove all the listener added
+ */
+ function createListeners(
+ baseModule: keyof SvelteKitParameters,
+ functions: string[],
+ defaultToAction?: boolean
+ ) {
+ // the array of every added listener, we can use this in the return function
+ // to clean them
+ const toRemove: Array<{
+ eventType: string;
+ listener: (event: { detail: any[] }) => void;
+ }> = [];
+ functions.forEach((func) => {
+ // we loop over every function and check if the user actually passed
+ // a function in sveltekit_experimental[baseModule][func] eg. sveltekit_experimental.navigation.goto
+ const hasFunction =
+ (svelteKitParameters as any)[baseModule]?.[func] &&
+ (svelteKitParameters as any)[baseModule][func] instanceof Function;
+ // if we default to an action we still add the listener (this will be the case for goto, invalidate, invalidateAll)
+ if (hasFunction || defaultToAction) {
+ // we create the listener that will just get the detail array from the custom element
+ // and call the user provided function spreading this args in...this will basically call
+ // the function that the user provide with the same arguments the function is invoked to
+ // eg. if it calls goto("/my-route") inside the component the function sveltekit_experimental.navigation.goto
+ // it provided to storybook will be called with "/my-route"
+ const listener = ({ detail = [] as any[] }) => {
+ const args = Array.isArray(detail) ? detail : [];
+ // if it has a function in the parameters we call that function
+ // otherwise we invoke the action
+ const fnToCall = hasFunction
+ ? (svelteKitParameters as any)[baseModule][func]
+ : action(func);
+ fnToCall(...args);
+ };
+ const eventType = `storybook:${func}`;
+ toRemove.push({ eventType, listener });
+ // add the listener to window
+ (window.addEventListener as any)(eventType, listener);
+ }
+ });
return () => {
- window.removeEventListener('click', globalClickListener);
- removeNavigationListeners();
- removeFormsListeners();
+ // loop over every listener added and remove them
+ toRemove.forEach(({ eventType, listener }) => {
+ // @ts-expect-error apparently you can't remove a custom listener to the window with TS
+ window.removeEventListener(eventType, listener);
+ });
};
- });
+ }
+
+ const removeNavigationListeners = createListeners(
+ 'navigation',
+ ['goto', 'invalidate', 'invalidateAll', 'pushState', 'replaceState'],
+ true
+ );
+ const removeFormsListeners = createListeners('forms', ['enhance']);
+ window.addEventListener('click', globalClickListener);
+
+ return () => {
+ window.removeEventListener('click', globalClickListener);
+ removeNavigationListeners();
+ removeFormsListeners();
+ };
+ });
+
+ return Story();
+};
- return Story();
- },
-];
+export const decorators: Decorator[] = [svelteKitMocksDecorator];
From 4f1b73d8ff7c5c49323912b9e420385cce45946c Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Thu, 5 Sep 2024 09:13:27 +0200
Subject: [PATCH 04/13] fix duplicate default annotations
---
.../modules/store/csf/portable-stories.ts | 16 +++++++++++-----
1 file changed, 11 insertions(+), 5 deletions(-)
diff --git a/code/core/src/preview-api/modules/store/csf/portable-stories.ts b/code/core/src/preview-api/modules/store/csf/portable-stories.ts
index 7adc83196eb1..1133e2e87a84 100644
--- a/code/core/src/preview-api/modules/store/csf/portable-stories.ts
+++ b/code/core/src/preview-api/modules/store/csf/portable-stories.ts
@@ -74,13 +74,19 @@ export function setProjectAnnotations(
| NamedOrDefaultProjectAnnotations[]
): NormalizedProjectAnnotations {
const annotations = Array.isArray(projectAnnotations) ? projectAnnotations : [projectAnnotations];
- if (globalThis.defaultProjectAnnotations) {
- annotations.push(globalThis.defaultProjectAnnotations);
- }
-
globalThis.globalProjectAnnotations = composeConfigs(annotations.map(extractAnnotation));
- return globalThis.globalProjectAnnotations;
+ /*
+ We must return the composition of default and global annotations here
+ To ensure that the user has the full project annotations, eg. when running
+
+ const projectAnnotations = setProjectAnnotations(...);
+ beforeAll(projectAnnotations.beforeAll)
+ */
+ return composeConfigs([
+ globalThis.defaultProjectAnnotations,
+ globalThis.globalProjectAnnotations,
+ ]);
}
const cleanups: CleanupCallback[] = [];
From 8a7d8eee7ce16030111e009779b9fb15a70099c0 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Thu, 5 Sep 2024 10:53:11 +0200
Subject: [PATCH 05/13] fix decorator type
---
code/frameworks/sveltekit/src/preview.ts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/frameworks/sveltekit/src/preview.ts b/code/frameworks/sveltekit/src/preview.ts
index c93cbde37a55..6eb8a816dd12 100644
--- a/code/frameworks/sveltekit/src/preview.ts
+++ b/code/frameworks/sveltekit/src/preview.ts
@@ -15,7 +15,7 @@ const normalizeHrefConfig = (hrefConfig: HrefConfig): NormalizedHrefConfig => {
return hrefConfig;
};
-const svelteKitMocksDecorator = (Story, ctx) => {
+const svelteKitMocksDecorator: Decorator = (Story, ctx) => {
const svelteKitParameters: SvelteKitParameters = ctx.parameters?.sveltekit_experimental ?? {};
setPage(svelteKitParameters?.stores?.page);
setNavigating(svelteKitParameters?.stores?.navigating);
From e4697f9d782700879ed2b4706965fcba8ff7a988 Mon Sep 17 00:00:00 2001
From: Jeppe Reinhold
Date: Thu, 5 Sep 2024 13:25:27 +0200
Subject: [PATCH 06/13] fix composing undefined defaultProjectAnnotations
---
.../src/preview-api/modules/store/csf/portable-stories.ts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/code/core/src/preview-api/modules/store/csf/portable-stories.ts b/code/core/src/preview-api/modules/store/csf/portable-stories.ts
index 1133e2e87a84..fd50bd57c2dd 100644
--- a/code/core/src/preview-api/modules/store/csf/portable-stories.ts
+++ b/code/core/src/preview-api/modules/store/csf/portable-stories.ts
@@ -84,8 +84,8 @@ export function setProjectAnnotations(
beforeAll(projectAnnotations.beforeAll)
*/
return composeConfigs([
- globalThis.defaultProjectAnnotations,
- globalThis.globalProjectAnnotations,
+ globalThis.defaultProjectAnnotations ?? {},
+ globalThis.globalProjectAnnotations ?? {},
]);
}
From 758aaa1497d8ded23bc2f1c2e3662cf597ced881 Mon Sep 17 00:00:00 2001
From: Valentin Palkovic
Date: Thu, 5 Sep 2024 13:29:09 +0200
Subject: [PATCH 07/13] Next.js-Vite: Update vite-plugin-storybook-nextjs
---
code/frameworks/experimental-nextjs-vite/package.json | 2 +-
code/yarn.lock | 10 +++++-----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/code/frameworks/experimental-nextjs-vite/package.json b/code/frameworks/experimental-nextjs-vite/package.json
index c0a70eaecffd..83e8bfc35993 100644
--- a/code/frameworks/experimental-nextjs-vite/package.json
+++ b/code/frameworks/experimental-nextjs-vite/package.json
@@ -99,7 +99,7 @@
"@storybook/react": "workspace:*",
"@storybook/test": "workspace:*",
"styled-jsx": "5.1.6",
- "vite-plugin-storybook-nextjs": "^1.0.10"
+ "vite-plugin-storybook-nextjs": "^1.0.11"
},
"devDependencies": {
"@types/node": "^18.0.0",
diff --git a/code/yarn.lock b/code/yarn.lock
index c3b25a45ce54..b7dcbf2eaa77 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -6340,7 +6340,7 @@ __metadata:
sharp: "npm:^0.33.3"
styled-jsx: "npm:5.1.6"
typescript: "npm:^5.3.2"
- vite-plugin-storybook-nextjs: "npm:^1.0.10"
+ vite-plugin-storybook-nextjs: "npm:^1.0.11"
peerDependencies:
"@storybook/test": "workspace:*"
next: ^14.1.0
@@ -28697,9 +28697,9 @@ __metadata:
languageName: node
linkType: hard
-"vite-plugin-storybook-nextjs@npm:^1.0.10":
- version: 1.0.10
- resolution: "vite-plugin-storybook-nextjs@npm:1.0.10"
+"vite-plugin-storybook-nextjs@npm:^1.0.11":
+ version: 1.0.11
+ resolution: "vite-plugin-storybook-nextjs@npm:1.0.11"
dependencies:
"@next/env": "npm:^14.2.5"
image-size: "npm:^1.1.1"
@@ -28715,7 +28715,7 @@ __metadata:
dependenciesMeta:
sharp:
optional: true
- checksum: 10c0/e0e373ef94e1761b871b2cc846c205a846901d93c7e61f9d9ee3c69740681e42e6403a7d61109c59f2d98d5829476c3e6d4e9d5a329c4bd51e758b763fa8ea9e
+ checksum: 10c0/9652b76c13a682b688d9a4f617b1a66263f25f395a99af8e258bedef4f3b3ce1c856ec1ff66cc0359d6aedc96adee9750fd6b0432514dd575ad7896cd1de70df
languageName: node
linkType: hard
From 714913d6443c524666ef81de37d4e59709712b33 Mon Sep 17 00:00:00 2001
From: Valentin Palkovic
Date: Thu, 5 Sep 2024 15:33:36 +0200
Subject: [PATCH 08/13] Next.js: Fix react-dom/test-utils aliasing
---
code/frameworks/nextjs/src/config/webpack.ts | 15 ++++++++++++++-
code/renderers/react/src/act-compat.ts | 8 ++++++--
2 files changed, 20 insertions(+), 3 deletions(-)
diff --git a/code/frameworks/nextjs/src/config/webpack.ts b/code/frameworks/nextjs/src/config/webpack.ts
index 3e0d758c1514..57e7caa47bbe 100644
--- a/code/frameworks/nextjs/src/config/webpack.ts
+++ b/code/frameworks/nextjs/src/config/webpack.ts
@@ -20,6 +20,9 @@ export const configureConfig = async ({
nextConfigPath?: string;
}): Promise => {
const nextConfig = await resolveNextConfig({ nextConfigPath });
+ baseConfig.resolve ??= {};
+ baseConfig.resolve.alias ??= {};
+ const aliasConfig = baseConfig.resolve.alias;
addScopedAlias(baseConfig, 'next/config');
if (tryResolve('next/dist/compiled/react')) {
@@ -31,9 +34,19 @@ export const configureConfig = async ({
'react-dom/test-utils',
'next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js'
);
+ } else {
+ const name = 'react-dom/test-utils';
+ if (Array.isArray(aliasConfig)) {
+ aliasConfig.push({
+ name,
+ alias: name,
+ });
+ } else {
+ aliasConfig[name] = name;
+ }
}
if (tryResolve('next/dist/compiled/react-dom')) {
- addScopedAlias(baseConfig, 'react-dom$', 'next/dist/compiled/react-dom');
+ addScopedAlias(baseConfig, 'react-dom', 'next/dist/compiled/react-dom');
}
setupRuntimeConfig(baseConfig, nextConfig);
diff --git a/code/renderers/react/src/act-compat.ts b/code/renderers/react/src/act-compat.ts
index afe1cc902316..31b4fb72e54f 100644
--- a/code/renderers/react/src/act-compat.ts
+++ b/code/renderers/react/src/act-compat.ts
@@ -8,8 +8,12 @@ declare const globalThis: {
IS_REACT_ACT_ENVIRONMENT: boolean;
};
-// @ts-expect-error act might not be available in some versions of React
-const reactAct = typeof React.act === 'function' ? React.act : DeprecatedReactTestUtils.act;
+const reactAct =
+ // @ts-expect-error act might not be available in some versions of React
+ typeof React.act === 'function'
+ ? // @ts-expect-error act might not be available in some versions of React
+ React.act
+ : DeprecatedReactTestUtils.act ?? (async (cb: () => Promise | void) => cb());
export function setReactActEnvironment(isReactActEnvironment: boolean) {
globalThis.IS_REACT_ACT_ENVIRONMENT = isReactActEnvironment;
From ea1a533a4b4e0017fb1a93baaf76c51be25fe66b Mon Sep 17 00:00:00 2001
From: Valentin Palkovic
Date: Fri, 6 Sep 2024 11:46:32 +0200
Subject: [PATCH 09/13] Fix Webpack aliasing
---
code/frameworks/nextjs/src/config/webpack.ts | 31 ++++++++++----------
code/frameworks/nextjs/src/utils.ts | 20 ++++++++-----
code/renderers/react/src/act-compat.ts | 5 +---
3 files changed, 28 insertions(+), 28 deletions(-)
diff --git a/code/frameworks/nextjs/src/config/webpack.ts b/code/frameworks/nextjs/src/config/webpack.ts
index 57e7caa47bbe..a0ea2d47bded 100644
--- a/code/frameworks/nextjs/src/config/webpack.ts
+++ b/code/frameworks/nextjs/src/config/webpack.ts
@@ -2,7 +2,7 @@ import type { NextConfig } from 'next';
import type { Configuration as WebpackConfig } from 'webpack';
import { DefinePlugin } from 'webpack';
-import { addScopedAlias, resolveNextConfig } from '../utils';
+import { addScopedAlias, resolveNextConfig, setAlias } from '../utils';
const tryResolve = (path: string) => {
try {
@@ -20,33 +20,32 @@ export const configureConfig = async ({
nextConfigPath?: string;
}): Promise => {
const nextConfig = await resolveNextConfig({ nextConfigPath });
- baseConfig.resolve ??= {};
- baseConfig.resolve.alias ??= {};
- const aliasConfig = baseConfig.resolve.alias;
addScopedAlias(baseConfig, 'next/config');
+
+ // @ts-expect-error We know that alias is an object
+ if (baseConfig.resolve?.alias?.['react-dom']) {
+ // Removing the alias to react-dom to avoid conflicts with the alias we are setting
+ // because the react-dom alias is an exact match and we need to alias separate parts of react-dom
+ // in different places
+ // @ts-expect-error We know that alias is an object
+ delete baseConfig.resolve.alias?.['react-dom'];
+ }
+
if (tryResolve('next/dist/compiled/react')) {
addScopedAlias(baseConfig, 'react', 'next/dist/compiled/react');
}
if (tryResolve('next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js')) {
- addScopedAlias(
+ setAlias(
baseConfig,
'react-dom/test-utils',
'next/dist/compiled/react-dom/cjs/react-dom-test-utils.production.js'
);
- } else {
- const name = 'react-dom/test-utils';
- if (Array.isArray(aliasConfig)) {
- aliasConfig.push({
- name,
- alias: name,
- });
- } else {
- aliasConfig[name] = name;
- }
}
if (tryResolve('next/dist/compiled/react-dom')) {
- addScopedAlias(baseConfig, 'react-dom', 'next/dist/compiled/react-dom');
+ setAlias(baseConfig, 'react-dom$', 'next/dist/compiled/react-dom');
+ setAlias(baseConfig, 'react-dom/client', 'next/dist/compiled/react-dom/client');
+ setAlias(baseConfig, 'react-dom/server', 'next/dist/compiled/react-dom/server');
}
setupRuntimeConfig(baseConfig, nextConfig);
diff --git a/code/frameworks/nextjs/src/utils.ts b/code/frameworks/nextjs/src/utils.ts
index 9c8abc6c88c8..198917513166 100644
--- a/code/frameworks/nextjs/src/utils.ts
+++ b/code/frameworks/nextjs/src/utils.ts
@@ -27,23 +27,27 @@ export const resolveNextConfig = async ({
return loadConfig(PHASE_DEVELOPMENT_SERVER, dir, undefined);
};
-// This is to help the addon in development
-// Without it, webpack resolves packages in its node_modules instead of the example's node_modules
-export const addScopedAlias = (baseConfig: WebpackConfig, name: string, alias?: string): void => {
+export function setAlias(baseConfig: WebpackConfig, name: string, alias: string) {
baseConfig.resolve ??= {};
baseConfig.resolve.alias ??= {};
const aliasConfig = baseConfig.resolve.alias;
- const scopedAlias = scopedResolve(`${alias ?? name}`);
-
if (Array.isArray(aliasConfig)) {
aliasConfig.push({
name,
- alias: scopedAlias,
+ alias,
});
} else {
- aliasConfig[name] = scopedAlias;
+ aliasConfig[name] = alias;
}
+}
+
+// This is to help the addon in development
+// Without it, webpack resolves packages in its node_modules instead of the example's node_modules
+export const addScopedAlias = (baseConfig: WebpackConfig, name: string, alias?: string): void => {
+ const scopedAlias = scopedResolve(`${alias ?? name}`);
+
+ setAlias(baseConfig, name, scopedAlias);
};
/**
@@ -64,7 +68,7 @@ export const scopedResolve = (id: string): string => {
let scopedModulePath;
try {
- // TODO: Remove in next major release (SB 8.0) and use the statement in the catch block per default instead
+ // TODO: Remove in next major release (SB 9.0) and use the statement in the catch block per default instead
scopedModulePath = require.resolve(id, { paths: [resolve()] });
} catch (e) {
scopedModulePath = require.resolve(id);
diff --git a/code/renderers/react/src/act-compat.ts b/code/renderers/react/src/act-compat.ts
index 31b4fb72e54f..3eab722d3bb1 100644
--- a/code/renderers/react/src/act-compat.ts
+++ b/code/renderers/react/src/act-compat.ts
@@ -10,10 +10,7 @@ declare const globalThis: {
const reactAct =
// @ts-expect-error act might not be available in some versions of React
- typeof React.act === 'function'
- ? // @ts-expect-error act might not be available in some versions of React
- React.act
- : DeprecatedReactTestUtils.act ?? (async (cb: () => Promise | void) => cb());
+ typeof React.act === 'function' ? React.act : DeprecatedReactTestUtils.act;
export function setReactActEnvironment(isReactActEnvironment: boolean) {
globalThis.IS_REACT_ACT_ENVIRONMENT = isReactActEnvironment;
From c24f22182edb47bf7da8fa4230b0b37cc054db62 Mon Sep 17 00:00:00 2001
From: Valentin Palkovic
Date: Fri, 6 Sep 2024 15:21:04 +0200
Subject: [PATCH 10/13] Remove console.log
---
code/renderers/react/src/portable-stories.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/code/renderers/react/src/portable-stories.tsx b/code/renderers/react/src/portable-stories.tsx
index ced0bbd289e3..bc031a04717e 100644
--- a/code/renderers/react/src/portable-stories.tsx
+++ b/code/renderers/react/src/portable-stories.tsx
@@ -98,7 +98,7 @@ export const INTERNAL_DEFAULT_PROJECT_ANNOTATIONS: ProjectAnnotations
Date: Mon, 9 Sep 2024 10:10:56 +0200
Subject: [PATCH 11/13] fix lock-file changes
---
code/yarn.lock | 257 +------------------------------------------------
1 file changed, 3 insertions(+), 254 deletions(-)
diff --git a/code/yarn.lock b/code/yarn.lock
index 2e596fc708a3..52f251440bb3 100644
--- a/code/yarn.lock
+++ b/code/yarn.lock
@@ -2490,7 +2490,7 @@ __metadata:
languageName: node
linkType: hard
-"@emnapi/runtime@npm:^1.1.1, @emnapi/runtime@npm:^1.2.0":
+"@emnapi/runtime@npm:^1.1.1":
version: 1.2.0
resolution: "@emnapi/runtime@npm:1.2.0"
dependencies:
@@ -3424,18 +3424,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-darwin-arm64@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-darwin-arm64@npm:0.33.5"
- dependencies:
- "@img/sharp-libvips-darwin-arm64": "npm:1.0.4"
- dependenciesMeta:
- "@img/sharp-libvips-darwin-arm64":
- optional: true
- conditions: os=darwin & cpu=arm64
- languageName: node
- linkType: hard
-
"@img/sharp-darwin-x64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-darwin-x64@npm:0.33.4"
@@ -3448,18 +3436,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-darwin-x64@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-darwin-x64@npm:0.33.5"
- dependencies:
- "@img/sharp-libvips-darwin-x64": "npm:1.0.4"
- dependenciesMeta:
- "@img/sharp-libvips-darwin-x64":
- optional: true
- conditions: os=darwin & cpu=x64
- languageName: node
- linkType: hard
-
"@img/sharp-libvips-darwin-arm64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-darwin-arm64@npm:1.0.2"
@@ -3467,13 +3443,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-libvips-darwin-arm64@npm:1.0.4":
- version: 1.0.4
- resolution: "@img/sharp-libvips-darwin-arm64@npm:1.0.4"
- conditions: os=darwin & cpu=arm64
- languageName: node
- linkType: hard
-
"@img/sharp-libvips-darwin-x64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-darwin-x64@npm:1.0.2"
@@ -3481,13 +3450,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-libvips-darwin-x64@npm:1.0.4":
- version: 1.0.4
- resolution: "@img/sharp-libvips-darwin-x64@npm:1.0.4"
- conditions: os=darwin & cpu=x64
- languageName: node
- linkType: hard
-
"@img/sharp-libvips-linux-arm64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linux-arm64@npm:1.0.2"
@@ -3495,13 +3457,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-libvips-linux-arm64@npm:1.0.4":
- version: 1.0.4
- resolution: "@img/sharp-libvips-linux-arm64@npm:1.0.4"
- conditions: os=linux & cpu=arm64 & libc=glibc
- languageName: node
- linkType: hard
-
"@img/sharp-libvips-linux-arm@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linux-arm@npm:1.0.2"
@@ -3509,13 +3464,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-libvips-linux-arm@npm:1.0.5":
- version: 1.0.5
- resolution: "@img/sharp-libvips-linux-arm@npm:1.0.5"
- conditions: os=linux & cpu=arm & libc=glibc
- languageName: node
- linkType: hard
-
"@img/sharp-libvips-linux-s390x@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linux-s390x@npm:1.0.2"
@@ -3523,13 +3471,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-libvips-linux-s390x@npm:1.0.4":
- version: 1.0.4
- resolution: "@img/sharp-libvips-linux-s390x@npm:1.0.4"
- conditions: os=linux & cpu=s390x & libc=glibc
- languageName: node
- linkType: hard
-
"@img/sharp-libvips-linux-x64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linux-x64@npm:1.0.2"
@@ -3537,13 +3478,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-libvips-linux-x64@npm:1.0.4":
- version: 1.0.4
- resolution: "@img/sharp-libvips-linux-x64@npm:1.0.4"
- conditions: os=linux & cpu=x64 & libc=glibc
- languageName: node
- linkType: hard
-
"@img/sharp-libvips-linuxmusl-arm64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.0.2"
@@ -3551,13 +3485,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-libvips-linuxmusl-arm64@npm:1.0.4":
- version: 1.0.4
- resolution: "@img/sharp-libvips-linuxmusl-arm64@npm:1.0.4"
- conditions: os=linux & cpu=arm64 & libc=musl
- languageName: node
- linkType: hard
-
"@img/sharp-libvips-linuxmusl-x64@npm:1.0.2":
version: 1.0.2
resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.0.2"
@@ -3565,13 +3492,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-libvips-linuxmusl-x64@npm:1.0.4":
- version: 1.0.4
- resolution: "@img/sharp-libvips-linuxmusl-x64@npm:1.0.4"
- conditions: os=linux & cpu=x64 & libc=musl
- languageName: node
- linkType: hard
-
"@img/sharp-linux-arm64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linux-arm64@npm:0.33.4"
@@ -3584,18 +3504,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-linux-arm64@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-linux-arm64@npm:0.33.5"
- dependencies:
- "@img/sharp-libvips-linux-arm64": "npm:1.0.4"
- dependenciesMeta:
- "@img/sharp-libvips-linux-arm64":
- optional: true
- conditions: os=linux & cpu=arm64 & libc=glibc
- languageName: node
- linkType: hard
-
"@img/sharp-linux-arm@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linux-arm@npm:0.33.4"
@@ -3608,18 +3516,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-linux-arm@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-linux-arm@npm:0.33.5"
- dependencies:
- "@img/sharp-libvips-linux-arm": "npm:1.0.5"
- dependenciesMeta:
- "@img/sharp-libvips-linux-arm":
- optional: true
- conditions: os=linux & cpu=arm & libc=glibc
- languageName: node
- linkType: hard
-
"@img/sharp-linux-s390x@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linux-s390x@npm:0.33.4"
@@ -3632,18 +3528,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-linux-s390x@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-linux-s390x@npm:0.33.5"
- dependencies:
- "@img/sharp-libvips-linux-s390x": "npm:1.0.4"
- dependenciesMeta:
- "@img/sharp-libvips-linux-s390x":
- optional: true
- conditions: os=linux & cpu=s390x & libc=glibc
- languageName: node
- linkType: hard
-
"@img/sharp-linux-x64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linux-x64@npm:0.33.4"
@@ -3656,18 +3540,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-linux-x64@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-linux-x64@npm:0.33.5"
- dependencies:
- "@img/sharp-libvips-linux-x64": "npm:1.0.4"
- dependenciesMeta:
- "@img/sharp-libvips-linux-x64":
- optional: true
- conditions: os=linux & cpu=x64 & libc=glibc
- languageName: node
- linkType: hard
-
"@img/sharp-linuxmusl-arm64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linuxmusl-arm64@npm:0.33.4"
@@ -3680,18 +3552,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-linuxmusl-arm64@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-linuxmusl-arm64@npm:0.33.5"
- dependencies:
- "@img/sharp-libvips-linuxmusl-arm64": "npm:1.0.4"
- dependenciesMeta:
- "@img/sharp-libvips-linuxmusl-arm64":
- optional: true
- conditions: os=linux & cpu=arm64 & libc=musl
- languageName: node
- linkType: hard
-
"@img/sharp-linuxmusl-x64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-linuxmusl-x64@npm:0.33.4"
@@ -3704,18 +3564,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-linuxmusl-x64@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-linuxmusl-x64@npm:0.33.5"
- dependencies:
- "@img/sharp-libvips-linuxmusl-x64": "npm:1.0.4"
- dependenciesMeta:
- "@img/sharp-libvips-linuxmusl-x64":
- optional: true
- conditions: os=linux & cpu=x64 & libc=musl
- languageName: node
- linkType: hard
-
"@img/sharp-wasm32@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-wasm32@npm:0.33.4"
@@ -3725,15 +3573,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-wasm32@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-wasm32@npm:0.33.5"
- dependencies:
- "@emnapi/runtime": "npm:^1.2.0"
- conditions: cpu=wasm32
- languageName: node
- linkType: hard
-
"@img/sharp-win32-ia32@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-win32-ia32@npm:0.33.4"
@@ -3741,13 +3580,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-win32-ia32@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-win32-ia32@npm:0.33.5"
- conditions: os=win32 & cpu=ia32
- languageName: node
- linkType: hard
-
"@img/sharp-win32-x64@npm:0.33.4":
version: 0.33.4
resolution: "@img/sharp-win32-x64@npm:0.33.4"
@@ -3755,13 +3587,6 @@ __metadata:
languageName: node
linkType: hard
-"@img/sharp-win32-x64@npm:0.33.5":
- version: 0.33.5
- resolution: "@img/sharp-win32-x64@npm:0.33.5"
- conditions: os=win32 & cpu=x64
- languageName: node
- linkType: hard
-
"@inquirer/confirm@npm:^3.0.0":
version: 3.1.20
resolution: "@inquirer/confirm@npm:3.1.20"
@@ -4078,20 +3903,13 @@ __metadata:
languageName: node
linkType: hard
-"@next/env@npm:14.2.5":
+"@next/env@npm:14.2.5, @next/env@npm:^14.2.5":
version: 14.2.5
resolution: "@next/env@npm:14.2.5"
checksum: 10c0/63d8b88ac450b3c37940a9e2119a63a1074aca89908574ade6157a8aa295275dcb3ac5f69e00883fc55d0f12963b73b74e87ba32a5768a489f9609c6be57b699
languageName: node
linkType: hard
-"@next/env@npm:^14.2.5":
- version: 14.2.7
- resolution: "@next/env@npm:14.2.7"
- checksum: 10c0/1cda023007acda4d47036a25fba0e039d9b2df9c3770651dc289207e0537506675546c02b5b574fe92bb1adc1c887d948d5cb630673aa572754278b82d150b7e
- languageName: node
- linkType: hard
-
"@next/swc-darwin-arm64@npm:14.2.5":
version: 14.2.5
resolution: "@next/swc-darwin-arm64@npm:14.2.5"
@@ -25822,7 +25640,7 @@ __metadata:
languageName: node
linkType: hard
-"sharp@npm:^0.33.3":
+"sharp@npm:^0.33.3, sharp@npm:^0.33.4":
version: 0.33.4
resolution: "sharp@npm:0.33.4"
dependencies:
@@ -25891,75 +25709,6 @@ __metadata:
languageName: node
linkType: hard
-"sharp@npm:^0.33.4":
- version: 0.33.5
- resolution: "sharp@npm:0.33.5"
- dependencies:
- "@img/sharp-darwin-arm64": "npm:0.33.5"
- "@img/sharp-darwin-x64": "npm:0.33.5"
- "@img/sharp-libvips-darwin-arm64": "npm:1.0.4"
- "@img/sharp-libvips-darwin-x64": "npm:1.0.4"
- "@img/sharp-libvips-linux-arm": "npm:1.0.5"
- "@img/sharp-libvips-linux-arm64": "npm:1.0.4"
- "@img/sharp-libvips-linux-s390x": "npm:1.0.4"
- "@img/sharp-libvips-linux-x64": "npm:1.0.4"
- "@img/sharp-libvips-linuxmusl-arm64": "npm:1.0.4"
- "@img/sharp-libvips-linuxmusl-x64": "npm:1.0.4"
- "@img/sharp-linux-arm": "npm:0.33.5"
- "@img/sharp-linux-arm64": "npm:0.33.5"
- "@img/sharp-linux-s390x": "npm:0.33.5"
- "@img/sharp-linux-x64": "npm:0.33.5"
- "@img/sharp-linuxmusl-arm64": "npm:0.33.5"
- "@img/sharp-linuxmusl-x64": "npm:0.33.5"
- "@img/sharp-wasm32": "npm:0.33.5"
- "@img/sharp-win32-ia32": "npm:0.33.5"
- "@img/sharp-win32-x64": "npm:0.33.5"
- color: "npm:^4.2.3"
- detect-libc: "npm:^2.0.3"
- semver: "npm:^7.6.3"
- dependenciesMeta:
- "@img/sharp-darwin-arm64":
- optional: true
- "@img/sharp-darwin-x64":
- optional: true
- "@img/sharp-libvips-darwin-arm64":
- optional: true
- "@img/sharp-libvips-darwin-x64":
- optional: true
- "@img/sharp-libvips-linux-arm":
- optional: true
- "@img/sharp-libvips-linux-arm64":
- optional: true
- "@img/sharp-libvips-linux-s390x":
- optional: true
- "@img/sharp-libvips-linux-x64":
- optional: true
- "@img/sharp-libvips-linuxmusl-arm64":
- optional: true
- "@img/sharp-libvips-linuxmusl-x64":
- optional: true
- "@img/sharp-linux-arm":
- optional: true
- "@img/sharp-linux-arm64":
- optional: true
- "@img/sharp-linux-s390x":
- optional: true
- "@img/sharp-linux-x64":
- optional: true
- "@img/sharp-linuxmusl-arm64":
- optional: true
- "@img/sharp-linuxmusl-x64":
- optional: true
- "@img/sharp-wasm32":
- optional: true
- "@img/sharp-win32-ia32":
- optional: true
- "@img/sharp-win32-x64":
- optional: true
- checksum: 10c0/6b81421ddfe6ee524d8d77e325c5e147fef22884e1c7b1656dfd89a88d7025894115da02d5f984261bf2e6daa16f98cadd1721c4ba408b4212b1d2a60f233484
- languageName: node
- linkType: hard
-
"shebang-command@npm:^1.2.0":
version: 1.2.0
resolution: "shebang-command@npm:1.2.0"
From ceb8387e24f9b401bd3fc693dbb0f5b220a389d0 Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Wed, 11 Sep 2024 11:25:00 +0200
Subject: [PATCH 12/13] Add comment explaining asyncWrapper
---
code/renderers/react/src/portable-stories.tsx | 2 ++
1 file changed, 2 insertions(+)
diff --git a/code/renderers/react/src/portable-stories.tsx b/code/renderers/react/src/portable-stories.tsx
index bc031a04717e..7b906c9f4bde 100644
--- a/code/renderers/react/src/portable-stories.tsx
+++ b/code/renderers/react/src/portable-stories.tsx
@@ -65,6 +65,8 @@ export const INTERNAL_DEFAULT_PROJECT_ANNOTATIONS: ProjectAnnotations {
return act(cb);
},
+ // For more context about why we need disable act warnings in waitFor:
+ // https://github.com/reactwg/react-18/discussions/102
asyncWrapper: async (cb) => {
const previousActEnvironment = getReactActEnvironment();
setReactActEnvironment(false);
From 46aa6e01ff4a4421a9899cc6f18edf2ff8fdee97 Mon Sep 17 00:00:00 2001
From: Kasper Peulen
Date: Wed, 11 Sep 2024 11:26:34 +0200
Subject: [PATCH 13/13] Fix bug where @storybook/test is not imported but
canvas is used directly from context
---
code/addons/interactions/src/preview.ts | 3 +++
1 file changed, 3 insertions(+)
diff --git a/code/addons/interactions/src/preview.ts b/code/addons/interactions/src/preview.ts
index 6abc30f63b93..482b6933279f 100644
--- a/code/addons/interactions/src/preview.ts
+++ b/code/addons/interactions/src/preview.ts
@@ -1,6 +1,9 @@
import type { PlayFunction, StepLabel, StoryContext } from 'storybook/internal/types';
import { instrument } from '@storybook/instrumenter';
+// This makes sure that storybook test loaders are always loaded when addon-interactions is used
+// For 9.0 we want to merge storybook/test and addon-interactions into one addon.
+import '@storybook/test';
export const { step: runStep } = instrument(
{