Skip to content

Commit

Permalink
Merge pull request #26316 from storybookjs/feature/portable-stories-a…
Browse files Browse the repository at this point in the history
…nnotations-improvement

Portable stories: Make setProjectAnnotations accept multiple types of imports
  • Loading branch information
yannbf authored Mar 14, 2024
2 parents 8dbff49 + 8edd503 commit 84b39c0
Show file tree
Hide file tree
Showing 11 changed files with 68 additions and 15 deletions.
2 changes: 1 addition & 1 deletion code/frameworks/react-vite/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Storybook for React & Vite

See [documentation](https://storybook.js.org/docs/8.0/get-started/react-vite?renderer=react) for installation instructions, usage examples, APIs, and more.
See [documentation](https://storybook.js.org/docs/8.0/get-started/react-vite?renderer=react) for installation instructions, usage examples, APIs, and more.
2 changes: 1 addition & 1 deletion code/frameworks/react-webpack5/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Storybook for React & Webpack

See [documentation](https://storybook.js.org/docs/8.0/get-started/react-webpack5?renderer=react) for installation instructions, usage examples, APIs, and more.
See [documentation](https://storybook.js.org/docs/8.0/get-started/react-webpack5?renderer=react) for installation instructions, usage examples, APIs, and more.
2 changes: 1 addition & 1 deletion code/frameworks/vue3-webpack5/README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Storybook for Vue 3 and Webpack

See [documentation](https://storybook.js.org/docs/8.0/get-started/vue3-webpack5?renderer=vue) for installation instructions, usage examples, APIs, and more.
See [documentation](https://storybook.js.org/docs/8.0/get-started/vue3-webpack5?renderer=vue) for installation instructions, usage examples, APIs, and more.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export default {
parameters: {
fromAnnotations: {
asDefaultImport: true,
},
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const parameters = {
fromAnnotations: {
asObjectImport: true,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import type {
} from '@storybook/types';

import { composeStory, composeStories, setProjectAnnotations } from './portable-stories';
import * as defaultExportAnnotations from './__mocks__/defaultExportAnnotations.mockfile';
import * as namedExportAnnotations from './__mocks__/namedExportAnnotations.mockfile';

type StoriesModule = Store_CSFExports & Record<string, any>;

Expand All @@ -23,6 +25,18 @@ describe('composeStory', () => {
},
};

it('should compose project annotations in all module formats', () => {
setProjectAnnotations([defaultExportAnnotations, namedExportAnnotations]);

const Story: Story = {
render: () => {},
};

const composedStory = composeStory(Story, meta);
expect(composedStory.parameters.fromAnnotations.asObjectImport).toEqual(true);
expect(composedStory.parameters.fromAnnotations.asDefaultImport).toEqual(true);
});

it('should return story with composed annotations from story, meta and project', () => {
const decoratorFromProjectAnnotations = vi.fn((StoryFn) => StoryFn());
const decoratorFromStoryAnnotations = vi.fn((StoryFn) => StoryFn());
Expand Down
18 changes: 15 additions & 3 deletions code/lib/preview-api/src/modules/store/csf/portable-stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import type {
Args,
ComponentAnnotations,
LegacyStoryAnnotationsOrFn,
ProjectAnnotations,
NamedOrDefaultProjectAnnotations,
ComposedStoryPlayFn,
ComposeStoryFn,
Store_CSFExports,
Expand All @@ -14,6 +14,7 @@ import type {
ComposedStoryFn,
StrictArgTypes,
PlayFunctionContext,
ProjectAnnotations,
} from '@storybook/types';

import { HooksContext } from '../../../addons';
Expand All @@ -26,11 +27,22 @@ import { normalizeProjectAnnotations } from './normalizeProjectAnnotations';

let globalProjectAnnotations: ProjectAnnotations<any> = {};

function extractAnnotation<TRenderer extends Renderer = Renderer>(
annotation: NamedOrDefaultProjectAnnotations<TRenderer>
) {
// support imports such as
// import * as annotations from '.storybook/preview'
// in both cases: 1 - the file has a default export; 2 - named exports only
return 'default' in annotation ? annotation.default : annotation;
}

export function setProjectAnnotations<TRenderer extends Renderer = Renderer>(
projectAnnotations: ProjectAnnotations<TRenderer> | ProjectAnnotations<TRenderer>[]
projectAnnotations:
| NamedOrDefaultProjectAnnotations<TRenderer>
| NamedOrDefaultProjectAnnotations<TRenderer>[]
) {
const annotations = Array.isArray(projectAnnotations) ? projectAnnotations : [projectAnnotations];
globalProjectAnnotations = composeConfigs(annotations);
globalProjectAnnotations = composeConfigs(annotations.map(extractAnnotation));
}

export function composeStory<TRenderer extends Renderer = Renderer, TArgs extends Args = Args>(
Expand Down
10 changes: 7 additions & 3 deletions code/lib/types/src/modules/composedStory.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
/* eslint-disable @typescript-eslint/naming-convention */

import type { PlayFunction, Renderer, StoryId, StrictArgTypes } from '@storybook/csf';
import type {
PlayFunction,
ProjectAnnotations,
Renderer,
StoryId,
StrictArgTypes,
} from '@storybook/csf';

import type {
AnnotatedStoryFn,
Expand All @@ -11,8 +17,6 @@ import type {
StoryAnnotationsOrFn,
} from './csf';

import type { ProjectAnnotations } from './story';

// TODO -- I think the name "CSFExports" overlaps here a bit with the types in csfFile.ts
// we might want to reconcile @yannbf
export type Store_CSFExports<TRenderer extends Renderer = Renderer, TArgs extends Args = Args> = {
Expand Down
5 changes: 5 additions & 0 deletions code/lib/types/src/modules/story.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ export type ProjectAnnotations<TRenderer extends Renderer> = CsfProjectAnnotatio
renderToDOM?: RenderToCanvas<TRenderer>;
};

type NamedExportsOrDefault<TExport> = TExport | { default: TExport };

export type NamedOrDefaultProjectAnnotations<TRenderer extends Renderer = Renderer> =
NamedExportsOrDefault<ProjectAnnotations<TRenderer>>;

export type NormalizedProjectAnnotations<TRenderer extends Renderer = Renderer> = Omit<
ProjectAnnotations<TRenderer>,
'decorators' | 'loaders'
Expand Down
13 changes: 8 additions & 5 deletions code/renderers/react/src/portable-stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import {
} from '@storybook/preview-api';
import type {
Args,
ProjectAnnotations,
NamedOrDefaultProjectAnnotations,
StoryAnnotationsOrFn,
Store_CSFExports,
StoriesWithPartialProps,
ProjectAnnotations,
} from '@storybook/types';

import * as defaultProjectAnnotations from './entry-preview';
Expand All @@ -28,17 +29,19 @@ import type { ReactRenderer } from './types';
* setProjectAnnotations(projectAnnotations);
*```
*
* @param projectAnnotations - e.g. (import projectAnnotations from '../.storybook/preview')
* @param projectAnnotations - e.g. (import * as projectAnnotations from '../.storybook/preview')
*/
export function setProjectAnnotations(
projectAnnotations: ProjectAnnotations<ReactRenderer> | ProjectAnnotations<ReactRenderer>[]
projectAnnotations:
| NamedOrDefaultProjectAnnotations<ReactRenderer>
| NamedOrDefaultProjectAnnotations<ReactRenderer>[]
) {
originalSetProjectAnnotations<ReactRenderer>(projectAnnotations);
}

/**
* 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)
* and optionally projectAnnotations e.g. (import * as projectAnnotations from '../.storybook/preview)
* and will return a composed component that has all args/parameters/decorators/etc combined and applied to it.
*
*
Expand Down Expand Up @@ -80,7 +83,7 @@ export function composeStory<TArgs extends Args = Args>(

/**
* Function that will receive a stories import (e.g. `import * as stories from './Button.stories'`)
* and optionally projectAnnotations (e.g. `import * from '../.storybook/preview`)
* and optionally projectAnnotations (e.g. `import * as projectAnnotations from '../.storybook/preview`)
* and will return an object containing all the stories passed, but now as a composed component that has all args/parameters/decorators/etc combined and applied to it.
*
*
Expand Down
5 changes: 4 additions & 1 deletion code/renderers/vue3/src/portable-stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from '@storybook/preview-api';
import type {
Args,
NamedOrDefaultProjectAnnotations,
ProjectAnnotations,
StoryAnnotationsOrFn,
Store_CSFExports,
Expand Down Expand Up @@ -32,7 +33,9 @@ import type { VueRenderer } from './types';
* @param projectAnnotations - e.g. (import projectAnnotations from '../.storybook/preview')
*/
export function setProjectAnnotations(
projectAnnotations: ProjectAnnotations<VueRenderer> | ProjectAnnotations<VueRenderer>[]
projectAnnotations:
| NamedOrDefaultProjectAnnotations<VueRenderer>
| NamedOrDefaultProjectAnnotations<VueRenderer>[]
) {
originalSetProjectAnnotations<VueRenderer>(projectAnnotations);
}
Expand Down

0 comments on commit 84b39c0

Please sign in to comment.