mirror of
https://github.com/storybookjs/storybook.git
synced 2025-03-28 05:10:17 +08:00
Some documentation of what the four packages do now
This commit is contained in:
parent
4c598550f2
commit
620fc19e5a
@ -1,123 +1,16 @@
|
||||
# `@storybook/client-api` -- APIs that control the rendering of stories in the preview iframe.
|
||||
# `@storybook/client-api` -- Deprecated Story APIs (`storiesOf`)
|
||||
|
||||
## Story store
|
||||
**NOTE** This API is deprecated, and the CSF format is preferred for all stories.
|
||||
|
||||
The story store contains the list of stories loaded in a Storybook.
|
||||
## `storiesOf` API
|
||||
|
||||
Each story is loaded via the `.add()` API and contains the follow attributes, which are available on the `context` (which is passed to the story's render function and decorators):
|
||||
The `@storybook/client` API provides the [`storiesOf()` API](../core/docs/storiesOf.md), which is proxied through to the CSF API.
|
||||
|
||||
- `kind` - the grouping of the story, typically corresponds to a single component. Also known as the `title` in CSF.
|
||||
- `name` - the name of the specific story.
|
||||
- `id` - an unique, URL sanitized identifier for the story, created from the `kind` and `name`.
|
||||
- `parameters` - static data about the story, see below.
|
||||
- `args` - dynamic inputs to the story, see below.
|
||||
- `hooks` - listeners that will rerun when the story changes or is unmounted, see `@storybook/addons`.
|
||||
- `viewMode` - property that tells if the story is being rendered in Canvas or Docs tab. Values are `story` for canvas and `docs` for docs.
|
||||
### Internals
|
||||
|
||||
## Parameters
|
||||
In order to appear to the store like the CSF API, a call to `storiesOf().add()` does the following:
|
||||
|
||||
The story parameters is a static, serializable object of data that provides details about the story. Those details can be used by addons or Storybook itself to render UI or provide defaults about the story rendering.
|
||||
- Tracks the story added in a synthetic `StoryIndex` data structure
|
||||
- Constructs a `moduleExports` object that is equivalent to the exports from a CSF file that produced the same stories.
|
||||
|
||||
Parameters _cannot change_ and are synchronized to the manager once when the story is loaded (note over the lifetime of a development Storybook a story can be loaded several times due to hot module reload, so the parameters technically can change for that reason).
|
||||
|
||||
Usually addons will read from a single key of `parameters` namespaced by the name of that addon. For instance the configuration of the `backgrounds` addon is driven by the `parameters.backgrounds` namespace.
|
||||
|
||||
Parameters are inheritable -- you can set global parameters via `addParameters` (exported by `client_api` and each framework), at the component level by the `parameters` key of the component (default) export in CSF (or in `.storiesOf`), and on a single story via the `parameters` key on the story data, or the third argument to `.add()`.
|
||||
|
||||
Some notable parameters:
|
||||
|
||||
- `parameters.fileName` - the file that the story was defined in, when available
|
||||
- `parameters.argsTypes` - type information about args (see below)
|
||||
|
||||
### Parameter enhancement
|
||||
|
||||
Ideally all parameters should be set _at build time_ of the Storybook, either directly by the user, or via the use of a webpack loader. (For an example of this, see `addon-storysource`, which writes to the `parameters.storysource` parameter with a set of static information about the story file).
|
||||
|
||||
However, in some cases it is necessary to set parameters at _load time_ when the Storybook first loads. This should be avoided if at all possible as it is cost that must be paid each time a Storybook loads, rather than just once when the Storybook is built.
|
||||
|
||||
To add a parameter enhancer, call `store.addArgTypesEnhancer(enhancer)` _before_ any stories are loaded (in addon registration or in `preview.js`). As each story is loaded, the enhancer will be called with the full story `context` -- the return value should be an object that will be patched into the Story's `argTypes`.
|
||||
|
||||
There is a default enhancer that ensures that each `arg` in a story has a baseline `argType`. This value can be improved by subsequent enhancers, e.g. those provided by `@storybook/addon-docs`.
|
||||
|
||||
## Args
|
||||
|
||||
Args are "inputs" to stories.
|
||||
|
||||
You can think of them equivalently to React props, Angular inputs and outputs, etc.
|
||||
|
||||
Changing the args cause the story to be re-rendered with the new set of args.
|
||||
|
||||
### Using args in a story
|
||||
|
||||
By default (starting in 6.0) the args will be passed to the story as first argument and the context as second:
|
||||
|
||||
```js
|
||||
const YourStory = ({ x, y } /*, context*/) => /* render your story using `x` and `y` */
|
||||
```
|
||||
|
||||
If you set the `parameters.options.passArgsFirst` option on a story to false, args are passed to a story in the context, preserving the pre-6.0 story API; like parameters, they are available as `context.args`.
|
||||
|
||||
```js
|
||||
const YourStory = ({ args: { x, y }}) => /* render your story using `x` and `y` */
|
||||
```
|
||||
|
||||
### Arg types and values
|
||||
|
||||
Arg types are used by the docs addon to populate the props table and are documented there. They are controlled by `parameters.argTypes` and can (sometimes) be automatically inferred from type information about the story or the component rendered by the story.
|
||||
|
||||
A story can set initial values of its args with the `parameters.args` parameter. If you set an initial value for an arg that doesn't have a type a simple type will be inferred from the value.
|
||||
|
||||
The initial value for an arg named "X" will be either `parameters.args.X` (if set) or `parameters.argTypes.X.defaultValue`. If an arg doesn't have a default value or an initial value, it will start unset, although it can still be set later via user interaction.
|
||||
|
||||
For instance, for this story:
|
||||
|
||||
```js
|
||||
export MyStory = ....
|
||||
MyStory.argTypes = {
|
||||
primary: { defaultValue: true, /* other things */ },
|
||||
size: { /* other things */ },
|
||||
color: { /* other things */ },
|
||||
};
|
||||
MyStory.args = {
|
||||
size: 'large',
|
||||
extra: 'prop',
|
||||
};
|
||||
```
|
||||
|
||||
Then `context.args` will default to `{ primary: true, size: 'large', extra: 'prop' }`.
|
||||
|
||||
### Using args in an addon
|
||||
|
||||
Args values are automatically synchronized (via the `changeStoryArgs` and `storyArgsChanged` events) between the preview and manager; APIs exist in `lib/api` to read and set args in the manager.
|
||||
|
||||
Args need to be serializable -- so currently cannot include callbacks (this may change in a future version).
|
||||
|
||||
Note that arg values are passed directly to a story -- you should only store the actual value that the story needs to render in the arg. If you need more complex information supporting that, use parameters or addon state.
|
||||
|
||||
Both `@storybook/client-api` (preview) and `@storybook/api` (manager) export a `useArgs()` hook that you can use to access args in decorators or addon panels. The API is as follows:
|
||||
|
||||
```js
|
||||
import { useArgs } from '@storybook/client-api'; // or '@storybook/api'
|
||||
|
||||
// `args` is the args of the currently rendered story
|
||||
// `updateArgs` will update its args. You can pass a subset of the args; other args will not be changed.
|
||||
const [args, updateArgs] = useArgs();
|
||||
```
|
||||
|
||||
## Global Args
|
||||
|
||||
Global args are args that are "global" across all stories. They are used for things like themes and internationalization (i18n) in stories, where you want Storybook to "remember" your setting as you browse between stories.
|
||||
|
||||
### Initial values of global args
|
||||
|
||||
To set initial values of global args, set the `parameters.globals` parameters. Addons can use parameter enhancers (see above) to do this.
|
||||
|
||||
### Using global args in an addon
|
||||
|
||||
Similar to args, global args are synchronized to the manager and can be accessed via the `useGlobals` hook.
|
||||
|
||||
```js
|
||||
import { useGlobals } from '@storybook/client-api'; // or '@storybook/api'
|
||||
|
||||
const [globals, updateGlobals] = useGlobals();
|
||||
```
|
||||
In order to achieve the old `storySort` functionality, the client API also needs access to the project annotations.
|
||||
|
@ -1,26 +1,14 @@
|
||||
# Storybook Core-Client
|
||||
|
||||
This package contains browser-side functionality shared amongst all the frameworks (React, RN, Vue, Ember, Angular, etc).
|
||||
This package contains browser-side functionality shared amongst all the frameworks (React, RN, Vue, Ember, Angular, etc) in the old "v6" story store back-compatibility layer.
|
||||
|
||||
## Preview
|
||||
A framework calls the `start(renderToDom, { render, decorateStory })` function and provides:
|
||||
|
||||
The files in `src/preview` alongside the `@storybook/client-api` package form the "API" of the preview (iframe) side of Storybook. Here is a brief overview of the architecture:
|
||||
- The `renderToDom` function, which tells Storybook how to render the result of a story function to the DOM
|
||||
- The `render` function, which is a default mapping of `args` to a story result in CSFv3
|
||||
- The `decorateStory` function, which tells Storybook how to combine decorators in the framework.
|
||||
|
||||
Each framework (e.g. `@storybook/react` / `@storybook/angular` / et al.) initializes the preview by calling into `src/preview/start.ts`, passing a `render` function that will be used to render stories.
|
||||
The `start` function will return a `configure()` function, which can be re-exported to be used in `preview.js` (deprecated), or automatically by the `main.js:stories` field to:
|
||||
|
||||
The `start` module initializes 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 `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.
|
||||
- `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 `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 URL of the preview is a "selection specifier" that controls the initial selection, which is chosen when configuration ends. Also (outside of configuration) the `SET_CURRENT_STORY` "command" event can be used to set the selection on the store. When the selection is set, a `CURRENT_STORY_WAS_SET` event is emitted which triggers a rendering.
|
||||
- return a list of CSF files
|
||||
- [deprecated] make calls to the `storiesOf` API.
|
||||
|
@ -16,4 +16,59 @@ The story store is designed to load stories 'on demand', and will operate in thi
|
||||
|
||||
However, for back-compat reasons, in v6 mode, we need to load all stories, synchronously on bootup, emitting the `SET_STORIES` event.
|
||||
|
||||
In V7 mode we do not emit that event, instead preferring the `STORY_PREPARED` event.
|
||||
In V7 mode we do not emit that event, instead preferring the `STORY_PREPARED` event, with the data for the single story being rendered.
|
||||
|
||||
## Initialization
|
||||
|
||||
The preview is `initialized` in two ways.
|
||||
|
||||
### V7 Mode:
|
||||
|
||||
- `importFn` - is an async `import()` function
|
||||
|
||||
- `getProjectAnnotations` - is a simple function that evaluations `preview.js` and addon config files and combines them. If it errors, the Preview will show the error.
|
||||
|
||||
- No `getStoryIndex` function is passed, instead the preview creates a `StoryIndexClient` that pulls `stories.json` from node and watches the event stream for invalidation events.
|
||||
|
||||
### V6 Mode
|
||||
|
||||
- `importFn` - is a simulated `import()` function, that is synchronous, see `client-api` for details.
|
||||
- `getProjectAnnotations` - also evaluates `preview.js` et al, but watches for calls to `setStories`, and passes them to the `ClientApi`
|
||||
- `getStoryIndex` is a local function (that must be called _after_ `getProjectAnnotations`) that gets the list of stories added.
|
||||
|
||||
See `client-api` for more details on this process.
|
||||
|
||||
## Story Rendering and interruptions
|
||||
|
||||
A rendering story goes through these phases:
|
||||
|
||||
- `loading` - async loaders are running
|
||||
- `rendering` - the `renderToDom` function for the framework is running
|
||||
- `playing` - the `play` function is running
|
||||
- `completed` - the story is done.
|
||||
|
||||
It also has two error states:
|
||||
|
||||
- `aborted` - the story was stopped midway (see below)
|
||||
- `errored` - there was an error thrown somewhere along the way.
|
||||
|
||||
### Re-rendering and aborting
|
||||
|
||||
A story may re-render due to various events, which can have implications if the story is not in the `completed` phase:
|
||||
|
||||
- `UPDATE_STORY_ARGS` / `UPDATE_GLOBALS` -- change of inputs
|
||||
- `FORCE_RE_RENDER` - re-render unchanged
|
||||
|
||||
If these events happen during a render:
|
||||
|
||||
- if the story is `loading`, leave thing unchanged and let the new `args`/`globals` be picked up by the render phase
|
||||
- otherwise, use the result of the previous `loaders` run, and simply re-render over the top
|
||||
|
||||
- `FORCE_REMOUNT` - remount (or equivalent) the component and re-render.
|
||||
|
||||
If this happens during a render, treat `loading` similarly, but:
|
||||
|
||||
- if the story is `rendering`, start a new render and abort the previous render immediately afterwards
|
||||
- if the story is `playing`, attempt to abort the previous play function, and start a new render.
|
||||
|
||||
Also the `SET_CURRENT_STORY` event may change the current story. If the old story is not `completed`, we try to abort it immediately. If that fails (e.g. the `play` function doesn't respond to the `abort` event), then we reload the window.
|
||||
|
@ -120,3 +120,17 @@ import { useGlobals } from '@storybook/addons'; // or '@storybook/api'
|
||||
|
||||
const [globals, updateGlobals] = useGlobals();
|
||||
```
|
||||
|
||||
## Technical details
|
||||
|
||||
### Initialization
|
||||
|
||||
- The store is created "uninitialized".
|
||||
- It is assumed at some later time it will be initialized with the Story Index, the set of stories (this may be loaded async).
|
||||
- You _can_ call `loadStory` prior to that time, in which case it will wait for initialization.
|
||||
|
||||
### Caching
|
||||
|
||||
- "All story" APIs like `extract()` require all stories to be loaded.
|
||||
- For backwards-compatibility, these APIs are _not_ async, so it is required that `store.cacheAllCSFFiles()` is called first
|
||||
- In v6 mode, this will be called on initialization but `start.ts`.
|
||||
|
Loading…
x
Reference in New Issue
Block a user