Skip to content

Commit

Permalink
Merge pull request #10117 from storybookjs/promote-args-argtypes-in-csf
Browse files Browse the repository at this point in the history
CSF: Promote args/argTypes to first-class metadata
  • Loading branch information
shilman committed Mar 12, 2020
2 parents ab58cf7 + a0dd54b commit a9fc57f
Show file tree
Hide file tree
Showing 6 changed files with 38 additions and 22 deletions.
2 changes: 1 addition & 1 deletion examples/official-storybook/stories/core/args.stories.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const PassedToStory = inputArgs => {
};

PassedToStory.story = {
parameters: { argTypes: { name: { defaultValue: 'initial' } } },
argTypes: { name: { defaultValue: 'initial' } },
};

PassedToStory.propTypes = {
Expand Down
4 changes: 2 additions & 2 deletions lib/client-api/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ For instance, for this story:

```js
export MyStory = ....
MyStory.story = { parameters: {
MyStory.story = {
argTypes: {
primary: { defaultValue: true, /* other things */ },
size: { /* other things */ },
Expand All @@ -80,7 +80,7 @@ MyStory.story = { parameters: {
size: 'large',
extra: 'prop',
}
}}
}
```

Then `context.args` will default to `{ primary: true, size: 'large', extra: 'prop' }`.
Expand Down
6 changes: 3 additions & 3 deletions lib/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ The `start` module initiaizes all the submodules:

- `StoryStore` (from `@storybook/client-api`) - stores the stories and their state as well as the current selection or error.
- `ClientApi` (from `@storybook/client-api`) - provides the entry point for `storiesOf()` API calls; re-exported by each framework.
- `ConfigApi` (from `@storybook/client-api`) - provides the configure API (wrapped by `makeConfigure` below).
- `ConfigApi` (from `@storybook/client-api`) - provides the configure API (wrapped by `loadCsf` below).
- `StoryRenderer` - controls the HTML that is rendered in the preview (calling the `render` function with the current story at appropriate times).
- `url.js` - controls the URL in the preview and sets the selection based on it.
- `makeConfigure` - loads CSF files from `require.context()` calls and uses `ClientApi` to load them into the store.
- `loadCsf` - loads CSF files from `require.context()` calls and uses `ClientApi` to load them into the store.

Each module uses the channel to communicate with each other and the manager. Each module also has direct access to the story store.

### Events on startup

The store can only be changed during "configuration". The `ConfigApi` will call `store.startConfiguration()`, then the user code (or `makeConfigure`'s loader) which will use client API to load up stories. At the end of the user's code the `ConfigApi` will call `store.finishConfiguration()`. At this point the `SET_STORIES` event is emitted and the stories are transmitted to the manager.
The store can only be changed during "configuration". The `ConfigApi` will call `store.startConfiguration()`, then the user code (or `loadCsf`'s loader) which will use client API to load up stories. At the end of the user's code the `ConfigApi` will call `store.finishConfiguration()`. At this point the `SET_STORIES` event is emitted and the stories are transmitted to the manager.

The `SET_CURRENT_STORY` "command" event can be used to set the selection on the store. However only once this has been recieved _and_ configuration is over will the store use the `RENDER_CURRENT_STORY` to tell the `StoryRenderer` to render it.
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ConfigApi, ClientApi, StoryStore } from '@storybook/client-api';
import { RequireContext } from './types';

import { makeConfigure } from './makeConfigure';
import { loadCsf } from './loadCsf';

let cbs: ((data: any) => void)[];
let mod: NodeModule;
Expand Down Expand Up @@ -39,7 +39,7 @@ function makeMocks() {
} as unknown) as ClientApi;

const context = { configApi, storyStore, clientApi };
const configure = makeConfigure(context);
const configure = loadCsf(context);
return { ...context, configure };
}

Expand All @@ -52,7 +52,7 @@ function makeRequireContext(map: Record<string, any>): RequireContext {
});
}

describe('core.preview.makeConfigure', () => {
describe('core.preview.loadCsf', () => {
it('calls storiesOf and add correctly from CSF exports', () => {
const { configure, clientApi } = makeMocks();

Expand Down Expand Up @@ -197,7 +197,7 @@ describe('core.preview.makeConfigure', () => {
});
});

it('allows setting component parameters and decorators', () => {
it('allows setting component parameters, decorators, and args/argTypes', () => {
const { configure, clientApi } = makeMocks();

const decorator = jest.fn();
Expand All @@ -207,6 +207,8 @@ describe('core.preview.makeConfigure', () => {
title: 'a',
parameters: { x: 'y' },
decorators: [decorator],
args: { b: 1 },
argTypes: { b: 'string' },
},
x: () => 0,
},
Expand All @@ -215,11 +217,13 @@ describe('core.preview.makeConfigure', () => {

const mockedStoriesOf = clientApi.storiesOf as jest.Mock;
const aApi = mockedStoriesOf.mock.results[0].value;
expect(aApi.addParameters).toHaveBeenCalledWith(expect.objectContaining({ x: 'y' }));
expect(aApi.addParameters).toHaveBeenCalledWith(
expect.objectContaining({ x: 'y', args: { b: 1 }, argTypes: { b: 'string' } })
);
expect(aApi.addDecorator).toHaveBeenCalledWith(decorator);
});

it('allows setting story parameters and decorators', () => {
it('allows setting story parameters and decorators, and args/argTypes', () => {
const { configure, clientApi } = makeMocks();

const decorator = jest.fn();
Expand All @@ -232,6 +236,8 @@ describe('core.preview.makeConfigure', () => {
story: {
parameters: { x: 'y' },
decorators: [decorator],
args: { b: 1 },
argTypes: { b: 'string' },
},
}),
},
Expand All @@ -244,6 +250,8 @@ describe('core.preview.makeConfigure', () => {
x: 'y',
decorators: [decorator],
__id: 'a--x',
args: { b: 1 },
argTypes: { b: 'string' },
});
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,10 +88,12 @@ const loadStories = (
const {
title: kindName,
id: componentId,
parameters: params,
decorators: decos,
parameters: kindParameters,
decorators: kindDecorators,
component,
subcomponents,
args: kindArgs,
argTypes: kindArgTypes,
} = meta;
// We pass true here to avoid the warning about HMR. It's cool clientApi, we got this
// todo discuss: TS now wants a NodeModule; should we fix this differently?
Expand All @@ -103,38 +105,44 @@ const loadStories = (
component,
subcomponents,
fileName: currentExports.get(fileExports),
...params,
...kindParameters,
args: kindArgs,
argTypes: kindArgTypes,
});

// todo add type
(decos || []).forEach((decorator: any) => {
(kindDecorators || []).forEach((decorator: any) => {
kind.addDecorator(decorator);
});

Object.keys(exports).forEach(key => {
if (isExportStory(key, meta)) {
const storyFn = exports[key];
const { name, parameters, decorators } = storyFn.story || {};
const { name, parameters, decorators, args, argTypes } = storyFn.story || {};
if (parameters && parameters.decorators) {
deprecate(() => {},
`${kindName} => ${name || key}: story.parameters.decorators is deprecated; use story.decorators instead.`)();
}
const decoratorParams = decorators ? { decorators } : null;
const exportName = storyNameFromExport(key);
const idParams = { __id: toId(componentId || kindName, exportName) };
kind.add(name || exportName, storyFn, {

const storyParams = {
...parameters,
...decoratorParams,
...idParams,
});
args,
argTypes,
};
kind.add(name || exportName, storyFn, storyParams);
}
});
});
previousExports = currentExports;
};

let loaded = false;
export const makeConfigure = ({
export const loadCsf = ({
clientApi,
storyStore,
configApi,
Expand Down
4 changes: 2 additions & 2 deletions lib/core/src/client/preview/start.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import Events from '@storybook/core-events';

import { initializePath, setPath } from './url';
import { RenderStoryFunction } from './types';
import { makeConfigure } from './makeConfigure';
import { loadCsf } from './loadCsf';
import { StoryRenderer } from './StoryRenderer';

const isBrowser =
Expand Down Expand Up @@ -93,6 +93,6 @@ export default function start(
window.__STORYBOOK_ADDONS_CHANNEL__ = channel; // may not be defined
}

const configure = makeConfigure({ clientApi, storyStore, configApi });
const configure = loadCsf({ clientApi, storyStore, configApi });
return { configure, clientApi, configApi, forceReRender: () => storyRenderer.forceReRender() };
}

0 comments on commit a9fc57f

Please sign in to comment.