Skip to content

Commit

Permalink
Model loaders as before each and restore mocks properly
Browse files Browse the repository at this point in the history
  • Loading branch information
kasperpeulen committed Nov 22, 2023
1 parent c344ce3 commit 8d612f3
Show file tree
Hide file tree
Showing 27 changed files with 206 additions and 93 deletions.
2 changes: 1 addition & 1 deletion code/addons/links/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/addon-bundle.ts"
},
"dependencies": {
"@storybook/csf": "^0.1.0",
"@storybook/csf": "0.1.2--canary.71.cad875c.0",
"@storybook/global": "^5.0.0",
"ts-dedent": "^2.0.0"
},
Expand Down
2 changes: 1 addition & 1 deletion code/addons/storyshots-puppeteer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
},
"dependencies": {
"@axe-core/puppeteer": "^4.2.0",
"@storybook/csf": "^0.1.0",
"@storybook/csf": "0.1.2--canary.71.cad875c.0",
"@storybook/node-logger": "workspace:*",
"@storybook/types": "workspace:*",
"@types/jest-image-snapshot": "^6.0.0",
Expand Down
2 changes: 1 addition & 1 deletion code/lib/codemod/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@
"@babel/core": "^7.23.2",
"@babel/preset-env": "^7.23.2",
"@babel/types": "^7.23.0",
"@storybook/csf": "^0.1.0",
"@storybook/csf": "0.1.2--canary.71.cad875c.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/node-logger": "workspace:*",
"@storybook/types": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion code/lib/core-server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@
"@storybook/channels": "workspace:*",
"@storybook/core-common": "workspace:*",
"@storybook/core-events": "workspace:*",
"@storybook/csf": "^0.1.0",
"@storybook/csf": "0.1.2--canary.71.cad875c.0",
"@storybook/csf-tools": "workspace:*",
"@storybook/docs-mdx": "^0.1.0",
"@storybook/global": "^5.0.0",
Expand Down
2 changes: 1 addition & 1 deletion code/lib/csf-tools/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@babel/parser": "^7.23.0",
"@babel/traverse": "^7.23.2",
"@babel/types": "^7.23.0",
"@storybook/csf": "^0.1.0",
"@storybook/csf": "0.1.2--canary.71.cad875c.0",
"@storybook/types": "workspace:*",
"fs-extra": "^11.1.0",
"recast": "^0.23.1",
Expand Down
2 changes: 1 addition & 1 deletion code/lib/manager-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-events": "workspace:*",
"@storybook/csf": "^0.1.0",
"@storybook/csf": "0.1.2--canary.71.cad875c.0",
"@storybook/global": "^5.0.0",
"@storybook/router": "workspace:*",
"@storybook/theming": "workspace:*",
Expand Down
2 changes: 1 addition & 1 deletion code/lib/preview-api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
"@storybook/channels": "workspace:*",
"@storybook/client-logger": "workspace:*",
"@storybook/core-events": "workspace:*",
"@storybook/csf": "^0.1.0",
"@storybook/csf": "0.1.2--canary.71.cad875c.0",
"@storybook/global": "^5.0.0",
"@storybook/types": "workspace:*",
"@types/qs": "^6.9.5",
Expand Down
8 changes: 5 additions & 3 deletions code/lib/preview-api/src/modules/client-api/ClientApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,11 @@ export class ClientApi<TRenderer extends Renderer> {
}
};

addStepRunner = (stepRunner: StepRunner) => {
addStepRunner = (stepRunner: StepRunner<TRenderer>) => {
this.facade.projectAnnotations.runStep = composeStepRunners(
[this.facade.projectAnnotations.runStep, stepRunner].filter(Boolean) as StepRunner[]
[this.facade.projectAnnotations.runStep, stepRunner].filter(
Boolean
) as StepRunner<TRenderer>[]
);
};

Expand Down Expand Up @@ -297,7 +299,7 @@ export class ClientApi<TRenderer extends Renderer> {
this._addedExports[fileName] = { default: meta };

let counter = 0;
api.add = (storyName: string, storyFn: StoryFn<TRenderer>, parameters: Parameters = {}) => {
api.add = (storyName: string, storyFn: StoryFn, parameters: Parameters = {}) => {
hasAdded = true;

if (typeof storyName !== 'string') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export class DocsContext<TRenderer extends Renderer> implements DocsContextProps
constructor(
public channel: Channel,
protected store: StoryStore<TRenderer>,
public renderStoryToElement: DocsContextProps['renderStoryToElement'],
public renderStoryToElement: DocsContextProps<TRenderer>['renderStoryToElement'],
/** The CSF files known (via the index) to be refererenced by this docs file */
csfFiles: CSFFile<TRenderer>[]
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class CsfDocsRender<TRenderer extends Renderer> implements Render<TRender
);
}

docsContext(renderStoryToElement: DocsContextProps['renderStoryToElement']) {
docsContext(renderStoryToElement: DocsContextProps<TRenderer>['renderStoryToElement']) {
if (!this.csfFiles) throw new Error('Cannot render docs before preparing');
const docsContext = new DocsContext<TRenderer>(
this.channel,
Expand All @@ -112,7 +112,7 @@ export class CsfDocsRender<TRenderer extends Renderer> implements Render<TRender

async renderToElement(
canvasElement: TRenderer['canvasElement'],
renderStoryToElement: DocsContextProps['renderStoryToElement']
renderStoryToElement: DocsContextProps<TRenderer>['renderStoryToElement']
) {
if (!this.story || !this.csfFiles) throw new Error('Cannot render docs before preparing');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ export class MdxDocsRender<TRenderer extends Renderer> implements Render<TRender
);
}

docsContext(renderStoryToElement: DocsContextProps['renderStoryToElement']) {
docsContext(renderStoryToElement: DocsContextProps<TRenderer>['renderStoryToElement']) {
if (!this.csfFiles) throw new Error('Cannot render docs before preparing');

// NOTE we do *not* attach any CSF file yet. We wait for `referenceMeta(..., true)`
Expand All @@ -94,7 +94,7 @@ export class MdxDocsRender<TRenderer extends Renderer> implements Render<TRender

async renderToElement(
canvasElement: TRenderer['canvasElement'],
renderStoryToElement: DocsContextProps['renderStoryToElement']
renderStoryToElement: DocsContextProps<TRenderer>['renderStoryToElement']
) {
if (!this.exports || !this.csfFiles || !this.store.projectAnnotations)
throw new Error('Cannot render docs before preparing');
Expand Down
4 changes: 4 additions & 0 deletions code/lib/preview-api/src/modules/store/csf/normalizeArrays.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export const normalizeArrays = <T>(array: T[] | T | undefined): T[] => {
if (Array.isArray(array)) return array;
return typeof array === 'function' ? [array] : [];
};
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,21 @@ import type {
import { inferArgTypes } from '../inferArgTypes';
import { inferControls } from '../inferControls';
import { normalizeInputTypes } from './normalizeInputTypes';
import { normalizeArrays } from './normalizeArrays';

export function normalizeProjectAnnotations<TRenderer extends Renderer>({
argTypes,
globalTypes,
argTypesEnhancers,
decorators,
loaders,
...annotations
}: ProjectAnnotations<TRenderer>): NormalizedProjectAnnotations<TRenderer> {
return {
...(argTypes && { argTypes: normalizeInputTypes(argTypes as ArgTypes) }),
...(globalTypes && { globalTypes: normalizeInputTypes(globalTypes) }),
decorators: normalizeArrays(decorators),
loaders: normalizeArrays(loaders),
argTypesEnhancers: [
...(argTypesEnhancers || []),
inferArgTypes,
Expand Down
9 changes: 7 additions & 2 deletions code/lib/preview-api/src/modules/store/csf/normalizeStory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { dedent } from 'ts-dedent';
import { logger } from '@storybook/client-logger';
import deprecate from 'util-deprecate';
import { normalizeInputTypes } from './normalizeInputTypes';
import { normalizeArrays } from './normalizeArrays';

const deprecatedStoryAnnotation = dedent`
CSF .story annotations deprecated; annotate story functions directly:
Expand Down Expand Up @@ -44,11 +45,15 @@ export function normalizeStory<TRenderer extends Renderer>(
storyObject.storyName ||
story?.name ||
exportName;
const decorators = [...(storyObject.decorators || []), ...(story?.decorators || [])];

const decorators = [
...normalizeArrays(storyObject.decorators),
...normalizeArrays(story?.decorators),
];
const parameters = { ...story?.parameters, ...storyObject.parameters };
const args = { ...story?.args, ...storyObject.args };
const argTypes = { ...(story?.argTypes as ArgTypes), ...(storyObject.argTypes as ArgTypes) };
const loaders = [...(storyObject.loaders || []), ...(story?.loaders || [])];
const loaders = [...normalizeArrays(storyObject.loaders), ...normalizeArrays(story?.loaders)];
const { render, play, tags = [] } = storyObject;

// eslint-disable-next-line no-underscore-dangle
Expand Down
49 changes: 29 additions & 20 deletions code/lib/preview-api/src/modules/store/csf/prepareStory.ts
Original file line number Diff line number Diff line change
@@ -1,31 +1,33 @@
/* eslint-disable no-restricted-syntax,no-await-in-loop,@typescript-eslint/no-loop-func */
import { global } from '@storybook/global';

import type {
Renderer,
Args,
ArgsStoryFn,
LegacyStoryFn,
Parameters,
PlayFunction,
PlayFunctionContext,
StepLabel,
ModuleExport,
NormalizedComponentAnnotations,
NormalizedProjectAnnotations,
NormalizedStoryAnnotations,
Parameters,
PlayFunction,
PlayFunctionContext,
PreparedMeta,
PreparedStory,
Renderer,
StepLabel,
StoryContext,
StoryContextForEnhancers,
StoryContextForLoaders,
StrictArgTypes,
PreparedMeta,
ModuleExport,
} from '@storybook/types';
import { includeConditionalArg } from '@storybook/csf';

import { applyHooks } from '../../addons';
import { combineParameters } from '../parameters';
import { defaultDecorateStory } from '../decorators';
import { groupArgsByTarget, UNTARGETED } from '../args';
import { normalizeArrays } from './normalizeArrays';

// Combine all the metadata about a story (both direct and inherited from the component/global scope)
// into a "renderable" story function, with all decorators applied, parameters passed as context etc
Expand All @@ -48,15 +50,23 @@ export function prepareStory<TRenderer extends Renderer>(
projectAnnotations
);

const loaders = [
...(projectAnnotations.loaders || []),
...(componentAnnotations.loaders || []),
...(storyAnnotations?.loaders || []),
];
const applyLoaders = async (context: StoryContextForLoaders<TRenderer>) => {
const loadResults = await Promise.all(loaders.map((loader) => loader(context)));
const loaded = Object.assign({}, ...loadResults);
return { ...context, loaded };
const applyLoaders = async (
context: StoryContextForLoaders<TRenderer>
): Promise<StoryContextForLoaders<TRenderer> & { loaded: StoryContext<TRenderer>['loaded'] }> => {
let updatedContext = { ...context, loaded: {} };
for (const loaders of [
...('testPackageLoaders' in global && Array.isArray(global.testPackageLoaders)
? [global.testPackageLoaders]
: []),
normalizeArrays(projectAnnotations.loaders),
normalizeArrays(componentAnnotations.loaders),
normalizeArrays(storyAnnotations.loaders),
]) {
const loadResults = await Promise.all(loaders.map((loader) => loader(updatedContext)));
const loaded: Record<string, any> = Object.assign({}, ...loadResults);
updatedContext = { ...updatedContext, loaded: { ...updatedContext.loaded, ...loaded } };
}
return updatedContext;
};

const undecoratedStoryFn: LegacyStoryFn<TRenderer> = (context: StoryContext<TRenderer>) => {
Expand All @@ -70,9 +80,9 @@ export function prepareStory<TRenderer extends Renderer>(
const { applyDecorators = defaultDecorateStory, runStep } = projectAnnotations;

const decorators = [
...(storyAnnotations?.decorators || []),
...(componentAnnotations.decorators || []),
...(projectAnnotations.decorators || []),
...normalizeArrays(storyAnnotations?.decorators),
...normalizeArrays(componentAnnotations?.decorators),
...normalizeArrays(projectAnnotations?.decorators),
];

// The render function on annotations *has* to be an `ArgsStoryFn`, so when we normalize
Expand Down Expand Up @@ -115,7 +125,6 @@ export function prepareStory<TRenderer extends Renderer>(
playFunction,
};
}

export function prepareMeta<TRenderer extends Renderer>(
componentAnnotations: NormalizedComponentAnnotations<TRenderer>,
projectAnnotations: NormalizedProjectAnnotations<TRenderer>,
Expand Down
5 changes: 3 additions & 2 deletions code/lib/preview-api/src/modules/store/sortStories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
Parameters,
Path,
PreparedStory,
Renderer,
} from '@storybook/types';
import { storySort } from './storySort';

Expand Down Expand Up @@ -58,8 +59,8 @@ const toIndexEntry = (story: any): StoryIndexEntry => {
return { id, title, name, importPath: parameters.fileName, type };
};

export const sortStoriesV6 = (
stories: [string, PreparedStory, Parameters, Parameters][],
export const sortStoriesV6 = <TRenderer extends Renderer>(
stories: [string, PreparedStory<TRenderer>, Parameters, Parameters][],
storySortParameter: Addon_StorySortParameter,
fileNameOrder: Path[]
) => {
Expand Down
2 changes: 1 addition & 1 deletion code/lib/source-loader/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"prep": "node --loader ../../../scripts/node_modules/esbuild-register/loader.js -r ../../../scripts/node_modules/esbuild-register/register.js ../../../scripts/prepare/bundle.ts"
},
"dependencies": {
"@storybook/csf": "^0.1.0",
"@storybook/csf": "0.1.2--canary.71.cad875c.0",
"@storybook/types": "workspace:*",
"estraverse": "^5.2.0",
"lodash": "^4.17.21",
Expand Down
2 changes: 1 addition & 1 deletion code/lib/test/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"url": "https://opencollective.com/storybook"
},
"license": "MIT",
"sideEffects": false,
"sideEffects": true,
"exports": {
".": {
"types": "./dist/index.d.ts",
Expand Down
28 changes: 17 additions & 11 deletions code/lib/test/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,11 @@
import { instrument } from '@storybook/instrumenter';
import * as spy from '@vitest/spy';
import { type LoaderFunction } from '@storybook/csf';
import chai from 'chai';
import { FORCE_REMOUNT, STORY_RENDER_PHASE_CHANGED } from '@storybook/core-events';
import { addons } from '@storybook/preview-api';
import { global } from '@storybook/global';
import { expect as rawExpect } from './expect';
import { clearAllMocks, resetAllMocks, restoreAllMocks } from './spy';

export * from '@vitest/spy';

const channel = addons.getChannel();

channel.on(FORCE_REMOUNT, () => spy.spies.forEach((mock) => mock.mockClear()));
channel.on(STORY_RENDER_PHASE_CHANGED, ({ newPhase }) => {
if (newPhase === 'loading') spy.spies.forEach((mock) => mock.mockClear());
});
export * from './spy';

export const { expect } = instrument(
{ expect: rawExpect },
Expand All @@ -32,3 +25,16 @@ export const { expect } = instrument(
);

export * from './testing-library';

const resetAllMocksLoader: LoaderFunction = ({ parameters }) => {
if (parameters?.test?.mockReset === true) {
resetAllMocks();
} else if (parameters?.test?.clearMocks === true) {
clearAllMocks();
} else if (parameters?.test?.restoreMocks !== false) {
restoreAllMocks();
}
};

// @ts-expect-error Wwe are using this as a default loader, if the test package is used. But we don't want to get into optional peer dep shenanigans.
global.testPackageLoaders = [resetAllMocksLoader];
Loading

0 comments on commit 8d612f3

Please sign in to comment.