Skip to content

Commit

Permalink
make setProjectAnnotations accept multiple types of imports
Browse files Browse the repository at this point in the history
  • Loading branch information
yannbf committed Mar 5, 2024
1 parent 02eec68 commit 4ba9e6f
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,37 @@ describe('composeStory', () => {
},
};

it('should compose project annotations in all module formats', () => {
setProjectAnnotations([
{
// import annotations from '.storybook/preview'
parameters: {
fromAnnotations: {
asObjectImport: true,
},
},
},
{
// import * as annotations from '.storybook/preview'
default: {
parameters: {
fromAnnotations: {
asDefaultImport: true,
},
},
},
},
]);

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
28 changes: 22 additions & 6 deletions code/lib/preview-api/src/modules/store/csf/portable-stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type {
ComponentAnnotations,
LegacyStoryAnnotationsOrFn,
ProjectAnnotations,
FlexibleProjectAnnotations,
ComposedStoryPlayFn,
ComposeStoryFn,
Store_CSFExports,
Expand All @@ -24,23 +25,34 @@ import { normalizeComponentAnnotations } from './normalizeComponentAnnotations';
import { getValuesFromArgTypes } from './getValuesFromArgTypes';
import { normalizeProjectAnnotations } from './normalizeProjectAnnotations';

let globalProjectAnnotations: ProjectAnnotations<any> = {};
let globalProjectAnnotations: FlexibleProjectAnnotations<any> = {};

export function getPortableStoryWrapperId(storyId: string) {
return `storybook-story-${storyId}`;
}

function extractAnnotation<TRenderer extends Renderer = Renderer>(
annotation: FlexibleProjectAnnotations<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:
| FlexibleProjectAnnotations<TRenderer>
| FlexibleProjectAnnotations<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>(
storyAnnotations: LegacyStoryAnnotationsOrFn<TRenderer>,
componentAnnotations: ComponentAnnotations<TRenderer, TArgs>,
projectAnnotations?: ProjectAnnotations<TRenderer>,
projectAnnotations?: FlexibleProjectAnnotations<TRenderer>,
defaultConfig?: ProjectAnnotations<TRenderer>,
exportsName?: string
): ComposedStoryFn<TRenderer, Partial<TArgs>> {
Expand Down Expand Up @@ -69,7 +81,11 @@ export function composeStory<TRenderer extends Renderer = Renderer, TArgs extend
);

const normalizedProjectAnnotations = normalizeProjectAnnotations<TRenderer>(
composeConfigs([defaultConfig ?? {}, globalProjectAnnotations, projectAnnotations ?? {}])
composeConfigs(
[defaultConfig ?? {}, globalProjectAnnotations, projectAnnotations ?? {}].map(
extractAnnotation
)
)
);

const story = prepareStory<TRenderer>(
Expand Down Expand Up @@ -135,7 +151,7 @@ export function composeStory<TRenderer extends Renderer = Renderer, TArgs extend

export function composeStories<TModule extends Store_CSFExports>(
storiesImport: TModule,
globalConfig: ProjectAnnotations<Renderer>,
globalConfig: FlexibleProjectAnnotations<Renderer>,
composeStoryFn: ComposeStoryFn
) {
const { default: meta, __esModule, __namedExportsOrder, ...stories } = storiesImport;
Expand Down
4 changes: 2 additions & 2 deletions code/lib/types/src/modules/composedStory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import type {
StoryAnnotationsOrFn,
} from './csf';

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

// TODO -- I think the name "CSFExports" overlaps here a bit with the types in csfFile.ts
// we might want to reconcile @yannbf
Expand Down Expand Up @@ -76,7 +76,7 @@ export interface ComposeStoryFn<TRenderer extends Renderer = Renderer, TArgs ext
(
storyAnnotations: AnnotatedStoryFn<TRenderer, TArgs> | StoryAnnotations<TRenderer, TArgs>,
componentAnnotations: ComponentAnnotations<TRenderer, TArgs>,
projectAnnotations: ProjectAnnotations<TRenderer>,
projectAnnotations: FlexibleProjectAnnotations<TRenderer>,
exportsName?: string
): ComposedStoryFn;
}
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>;
};

// TODO: find a better name for this, of course. Please help
export type FlexibleProjectAnnotations<TRenderer extends Renderer = Renderer> =
| ProjectAnnotations<TRenderer>
| { default: ProjectAnnotations<TRenderer> };

export type NormalizedProjectAnnotations<TRenderer extends Renderer = Renderer> = Omit<
ProjectAnnotations<TRenderer>,
'decorators' | 'loaders'
Expand Down
7 changes: 6 additions & 1 deletion code/renderers/react/src/portable-stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,21 @@ import {
} from '@storybook/preview-api';
import type {
Args,
ProjectAnnotations,
ProjectAnnotations as OriginalProjectAnnotations,
StoryAnnotationsOrFn,
Store_CSFExports,
StoriesWithPartialProps,
Renderer,
} from '@storybook/types';

import * as reactProjectAnnotations from './entry-preview';
import type { Meta } from './public-types';
import type { ReactRenderer } from './types';

type ProjectAnnotations<TRenderer extends Renderer = Renderer> =
| OriginalProjectAnnotations<TRenderer>
| { default: OriginalProjectAnnotations<TRenderer> };

/** 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`.
Expand Down

0 comments on commit 4ba9e6f

Please sign in to comment.