mirror of
https://github.com/storybookjs/storybook.git
synced 2025-04-09 00:19:13 +08:00
use document.body as default canvasElement instead of injecting a root element
This commit is contained in:
parent
fc222b9b97
commit
93b7ed44cb
31
MIGRATION.md
31
MIGRATION.md
@ -4,7 +4,6 @@
|
||||
- [Portable stories](#portable-stories)
|
||||
- [Project annotations are now merged instead of overwritten in composeStory](#project-annotations-are-now-merged-instead-of-overwritten-in-composestory)
|
||||
- [Type change in `composeStories` API](#type-change-in-composestories-api)
|
||||
- [DOM structure changed in portable stories](#dom-structure-changed-in-portable-stories)
|
||||
- [Composed Vue stories are now components instead of functions](#composed-vue-stories-are-now-components-instead-of-functions)
|
||||
- [Tab addons are now routed to a query parameter](#tab-addons-are-now-routed-to-a-query-parameter)
|
||||
- [Default keyboard shortcuts changed](#default-keyboard-shortcuts-changed)
|
||||
@ -440,35 +439,6 @@ await Primary.play!(...) // if you want a runtime error when the play function d
|
||||
|
||||
There are plans to make the type of the play function be inferred based on your imported story's play function in a near future, so the types will be 100% accurate.
|
||||
|
||||
#### DOM structure changed in portable stories
|
||||
|
||||
The portable stories API now adds a wrapper to your stories with a unique id based on your story id, such as:
|
||||
|
||||
```html
|
||||
<div data-story="true" id="#storybook-story-button--primary">
|
||||
<!-- your story here -->
|
||||
</div>
|
||||
```
|
||||
|
||||
This means that if you take DOM snapshots of your stories, they will be affected and you will have to update them.
|
||||
|
||||
The id calculation is based on different heuristics based on your Meta title and Story name. When using `composeStories`, the id can be inferred automatically. However, when using `composeStory` and your story does not explicitly have a `storyName` property, the story name can't be inferred automatically. As a result, its name will be "Unnamed Story", resulting in a wrapper id like `"#storybook-story-button--unnamed-story"`. If the id matters to you and you want to fix it, you have to specify the `exportsName` property like so:
|
||||
|
||||
```ts
|
||||
test("snapshots the story with custom id", () => {
|
||||
const Primary = composeStory(
|
||||
stories.Primary,
|
||||
stories.default,
|
||||
undefined,
|
||||
// If you do not want the `unnamed-story` id, you have to pass the name of the story as a parameter
|
||||
"Primary"
|
||||
);
|
||||
|
||||
const { baseElement } = render(<Primary />);
|
||||
expect(baseElement).toMatchSnapshot();
|
||||
});
|
||||
```
|
||||
|
||||
#### Composed Vue stories are now components instead of functions
|
||||
|
||||
`composeStory` (and `composeStories`) from `@storybook/vue3` now return Vue components rather than story functions that return components. This means that when rendering these composed stories you just pass the composed story _without_ first calling it.
|
||||
@ -1105,6 +1075,7 @@ const preview: Preview = {
|
||||
|
||||
export default preview;
|
||||
```
|
||||
|
||||
To learn more about the available icons and their names, see the [Storybook documentation](https://storybook.js.org/docs/8.0/faq#what-icons-are-available-for-my-toolbar-or-my-addon).
|
||||
|
||||
#### Removals in @storybook/types
|
||||
|
@ -56,7 +56,6 @@ export {
|
||||
filterArgTypes,
|
||||
sanitizeStoryContextUpdate,
|
||||
setProjectAnnotations,
|
||||
getPortableStoryWrapperId,
|
||||
inferControls,
|
||||
userOrAutoTitleFromSpecifier,
|
||||
userOrAutoTitle,
|
||||
|
@ -26,10 +26,6 @@ import { normalizeProjectAnnotations } from './normalizeProjectAnnotations';
|
||||
|
||||
let globalProjectAnnotations: ProjectAnnotations<any> = {};
|
||||
|
||||
export function getPortableStoryWrapperId(storyId: string) {
|
||||
return `storybook-story-${storyId}`;
|
||||
}
|
||||
|
||||
export function setProjectAnnotations<TRenderer extends Renderer = Renderer>(
|
||||
projectAnnotations: ProjectAnnotations<TRenderer> | ProjectAnnotations<TRenderer>[]
|
||||
) {
|
||||
@ -99,11 +95,7 @@ export function composeStory<TRenderer extends Renderer = Renderer, TArgs extend
|
||||
story.playFunction!({
|
||||
...context,
|
||||
...extraContext,
|
||||
// if canvasElement is not provided, we default to the root element, which comes from a decorator
|
||||
// the decorator has to be implemented in the defaultAnnotations of each integrator of portable stories
|
||||
canvasElement:
|
||||
extraContext?.canvasElement ??
|
||||
globalThis.document?.getElementById(getPortableStoryWrapperId(context.id)),
|
||||
canvasElement: extraContext?.canvasElement ?? globalThis.document?.body,
|
||||
})
|
||||
: undefined;
|
||||
|
||||
|
@ -3,17 +3,12 @@
|
||||
exports[`Renders CSF2Secondary story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-2-secondary"
|
||||
<button
|
||||
class="storybook-button storybook-button--medium storybook-button--secondary"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="storybook-button storybook-button--medium storybook-button--secondary"
|
||||
type="button"
|
||||
>
|
||||
Children coming from story args!
|
||||
</button>
|
||||
</div>
|
||||
Children coming from story args!
|
||||
</button>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@ -21,17 +16,12 @@ exports[`Renders CSF2Secondary story 1`] = `
|
||||
exports[`Renders CSF2StoryWithParamsAndDecorator story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-2-story-with-params-and-decorator"
|
||||
<button
|
||||
class="storybook-button storybook-button--medium storybook-button--secondary"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="storybook-button storybook-button--medium storybook-button--secondary"
|
||||
type="button"
|
||||
>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@ -39,17 +29,12 @@ exports[`Renders CSF2StoryWithParamsAndDecorator story 1`] = `
|
||||
exports[`Renders CSF3Button story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-3-button"
|
||||
<button
|
||||
class="storybook-button storybook-button--medium storybook-button--secondary"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="storybook-button storybook-button--medium storybook-button--secondary"
|
||||
type="button"
|
||||
>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@ -57,23 +42,18 @@ exports[`Renders CSF3Button story 1`] = `
|
||||
exports[`Renders CSF3ButtonWithRender story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-3-button-with-render"
|
||||
>
|
||||
<div>
|
||||
<p
|
||||
data-testid="custom-render"
|
||||
>
|
||||
I am a custom render function
|
||||
</p>
|
||||
<button
|
||||
class="storybook-button storybook-button--medium storybook-button--secondary"
|
||||
type="button"
|
||||
>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
<div>
|
||||
<p
|
||||
data-testid="custom-render"
|
||||
>
|
||||
I am a custom render function
|
||||
</p>
|
||||
<button
|
||||
class="storybook-button storybook-button--medium storybook-button--secondary"
|
||||
type="button"
|
||||
>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
@ -82,14 +62,9 @@ exports[`Renders CSF3ButtonWithRender story 1`] = `
|
||||
exports[`Renders CSF3InputFieldFilled story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-3-input-field-filled"
|
||||
>
|
||||
<input
|
||||
data-testid="input"
|
||||
/>
|
||||
</div>
|
||||
<input
|
||||
data-testid="input"
|
||||
/>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@ -97,17 +72,12 @@ exports[`Renders CSF3InputFieldFilled story 1`] = `
|
||||
exports[`Renders CSF3Primary story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-3-primary"
|
||||
<button
|
||||
class="storybook-button storybook-button--large storybook-button--primary"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="storybook-button storybook-button--large storybook-button--primary"
|
||||
type="button"
|
||||
>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@ -115,21 +85,16 @@ exports[`Renders CSF3Primary story 1`] = `
|
||||
exports[`Renders LoaderStory story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--loader-story"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
data-testid="loaded-data"
|
||||
>
|
||||
loaded data
|
||||
</div>
|
||||
<div
|
||||
data-testid="spy-data"
|
||||
>
|
||||
mockFn return value
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
data-testid="loaded-data"
|
||||
>
|
||||
loaded data
|
||||
</div>
|
||||
<div
|
||||
data-testid="spy-data"
|
||||
>
|
||||
mockFn return value
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,9 +1,7 @@
|
||||
import React from 'react';
|
||||
import {
|
||||
composeStory as originalComposeStory,
|
||||
composeStories as originalComposeStories,
|
||||
setProjectAnnotations as originalSetProjectAnnotations,
|
||||
getPortableStoryWrapperId,
|
||||
} from '@storybook/preview-api';
|
||||
import type {
|
||||
Args,
|
||||
@ -13,7 +11,7 @@ import type {
|
||||
StoriesWithPartialProps,
|
||||
} from '@storybook/types';
|
||||
|
||||
import * as reactProjectAnnotations from './entry-preview';
|
||||
import * as defaultProjectAnnotations from './entry-preview';
|
||||
import type { Meta } from './public-types';
|
||||
import type { ReactRenderer } from './types';
|
||||
|
||||
@ -38,20 +36,6 @@ export function setProjectAnnotations(
|
||||
originalSetProjectAnnotations<ReactRenderer>(projectAnnotations);
|
||||
}
|
||||
|
||||
// This will not be necessary once we have auto preset loading
|
||||
const defaultProjectAnnotations: ProjectAnnotations<ReactRenderer> = {
|
||||
...reactProjectAnnotations,
|
||||
decorators: [
|
||||
function addStorybookId(StoryFn, { id }) {
|
||||
return (
|
||||
<div data-story id={getPortableStoryWrapperId(id)}>
|
||||
<StoryFn />
|
||||
</div>
|
||||
);
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* Function that will receive a story along with meta (e.g. a default export from a .stories file)
|
||||
* and optionally projectAnnotations e.g. (import * from '../.storybook/preview)
|
@ -3,17 +3,12 @@
|
||||
exports[`Renders CSF2Secondary story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-2-secondary"
|
||||
<button
|
||||
class="storybook-button storybook-button--secondary storybook-button--medium"
|
||||
type="button"
|
||||
>
|
||||
<button
|
||||
class="storybook-button storybook-button--secondary storybook-button--medium"
|
||||
type="button"
|
||||
>
|
||||
label coming from story args!
|
||||
</button>
|
||||
</div>
|
||||
label coming from story args!
|
||||
</button>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
@ -22,30 +17,7 @@ exports[`Renders CSF2StoryWithParamsAndDecorator story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-2-story-with-params-and-decorator"
|
||||
>
|
||||
<div
|
||||
style="margin: 3em;"
|
||||
>
|
||||
<button
|
||||
class="storybook-button storybook-button--secondary storybook-button--medium"
|
||||
type="button"
|
||||
>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`Renders CSF3Button story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-3-button"
|
||||
style="margin: 3em;"
|
||||
>
|
||||
<button
|
||||
class="storybook-button storybook-button--secondary storybook-button--medium"
|
||||
@ -58,55 +30,30 @@ exports[`Renders CSF3Button story 1`] = `
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`Renders CSF3Button story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<button
|
||||
class="storybook-button storybook-button--secondary storybook-button--medium"
|
||||
type="button"
|
||||
>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`Renders CSF3ButtonWithRender story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-3-button-with-render"
|
||||
>
|
||||
<div>
|
||||
<p
|
||||
data-testid="custom-render"
|
||||
>
|
||||
I am a custom render function
|
||||
</p>
|
||||
<button
|
||||
class="storybook-button storybook-button--secondary storybook-button--medium"
|
||||
type="button"
|
||||
>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`Renders CSF3InputFieldFilled story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-3-input-field-filled"
|
||||
>
|
||||
<input
|
||||
data-testid="input"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`Renders CSF3Primary story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--csf-3-primary"
|
||||
>
|
||||
<div>
|
||||
<p
|
||||
data-testid="custom-render"
|
||||
>
|
||||
I am a custom render function
|
||||
</p>
|
||||
<button
|
||||
class="storybook-button storybook-button--primary storybook-button--large"
|
||||
class="storybook-button storybook-button--secondary storybook-button--medium"
|
||||
type="button"
|
||||
>
|
||||
foo
|
||||
@ -116,24 +63,42 @@ exports[`Renders CSF3Primary story 1`] = `
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`Renders CSF3InputFieldFilled story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<input
|
||||
data-testid="input"
|
||||
/>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`Renders CSF3Primary story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<button
|
||||
class="storybook-button storybook-button--primary storybook-button--large"
|
||||
type="button"
|
||||
>
|
||||
foo
|
||||
</button>
|
||||
</div>
|
||||
</body>
|
||||
`;
|
||||
|
||||
exports[`Renders LoaderStory story 1`] = `
|
||||
<body>
|
||||
<div>
|
||||
<div
|
||||
data-story="true"
|
||||
id="storybook-story-example-button--loader-story"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
data-testid="loaded-data"
|
||||
>
|
||||
loaded data
|
||||
</div>
|
||||
<div
|
||||
data-testid="spy-data"
|
||||
>
|
||||
mockFn return value
|
||||
</div>
|
||||
<div>
|
||||
<div
|
||||
data-testid="loaded-data"
|
||||
>
|
||||
loaded data
|
||||
</div>
|
||||
<div
|
||||
data-testid="spy-data"
|
||||
>
|
||||
mockFn return value
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -2,7 +2,6 @@ import {
|
||||
composeStory as originalComposeStory,
|
||||
composeStories as originalComposeStories,
|
||||
setProjectAnnotations as originalSetProjectAnnotations,
|
||||
getPortableStoryWrapperId,
|
||||
} from '@storybook/preview-api';
|
||||
import type {
|
||||
Args,
|
||||
@ -13,20 +12,10 @@ import type {
|
||||
} from '@storybook/types';
|
||||
import { h } from 'vue';
|
||||
|
||||
import * as vueProjectAnnotations from './entry-preview';
|
||||
import * as defaultProjectAnnotations from './entry-preview';
|
||||
import type { Meta } from './public-types';
|
||||
import type { VueRenderer } from './types';
|
||||
|
||||
const defaultProjectAnnotations: ProjectAnnotations<VueRenderer> = {
|
||||
...vueProjectAnnotations,
|
||||
decorators: [
|
||||
function (story, { id }) {
|
||||
const wrapperProps = { 'data-story': true, id: getPortableStoryWrapperId(id) };
|
||||
return h('div', wrapperProps, h(story()));
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
/** Function that sets the globalConfig of your Storybook. The global config is the preview module of your .storybook folder.
|
||||
*
|
||||
* It should be run a single time, so that your global config (e.g. decorators) is applied to your stories when using `composeStories` or `composeStory`.
|
||||
|
Loading…
x
Reference in New Issue
Block a user