Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Core: Normalize parameters in store/channel #10373

Merged
merged 29 commits into from
Apr 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9ae73c7
Store parameters in the store in a normalized way
tmeasday Apr 10, 2020
e92b44e
Merge remote-tracking branch 'origin/next' into 10361-normalize-param…
tmeasday Apr 21, 2020
7307b76
Fix bad merge
tmeasday Apr 21, 2020
7adf8c0
Fix story sort tests
tmeasday Apr 21, 2020
4a87d5a
Combine parameters in `fromId()`
tmeasday Apr 21, 2020
198a601
We'll always define `parameters.argTypes`
tmeasday Apr 21, 2020
603dfc5
Normalize parameters in the `SET_STORIES` event
tmeasday Apr 21, 2020
bd9dd9f
Rename `SET_STORIES` to `SET_STORY_STORE_DATA`
tmeasday Apr 22, 2020
9b91f38
Pick up in the manager
tmeasday Apr 22, 2020
01867f9
Duplicate types and combineParameters
tmeasday Apr 22, 2020
1bdd48b
Bugfix
tmeasday Apr 22, 2020
febec0f
Merge branch 'next' into 10361-normalize-parameters
ndelangen Apr 23, 2020
cb28194
ADD rest data to ref
ndelangen Apr 23, 2020
63682a2
Explicitly don't allow setting storySort any other way
tmeasday Apr 28, 2020
c0cdd3f
Export `combineParameters` and document in migrations
tmeasday Apr 28, 2020
de6d52e
Remove spurious log
tmeasday Apr 28, 2020
0c6cbd6
Revert "Rename `SET_STORIES` to `SET_STORY_STORE_DATA`"
tmeasday Apr 28, 2020
fecf1d9
More spurious logs
tmeasday Apr 28, 2020
385fe81
Update to use a single SET_STORIES event
tmeasday Apr 28, 2020
afdfd04
Merge branch 'next' into 10361-normalize-parameters
shilman Apr 29, 2020
12a2853
Fix typo
shilman Apr 29, 2020
884bb15
SET_STORY_STORE_DATA => SET_STORIES
shilman Apr 29, 2020
27fadc6
Normalized parameters: Fix failing test
shilman Apr 29, 2020
8390c62
Fix inclusion logic
shilman Apr 29, 2020
03425f6
Fix deepscan
shilman Apr 29, 2020
c0f8fb2
Merge branch 'next' into 10361-normalize-parameters
shilman Apr 29, 2020
241d06c
Ensure we set `parameters.argTypes` in the preview
tmeasday Apr 29, 2020
a1b9372
Fix failing tests
shilman Apr 29, 2020
e2d54f6
Update snapshots
shilman Apr 29, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
- [Rolling back](#rolling-back)
- [New addon presets](#new-addon-presets)
- [Removed Deprecated APIs](#removed-deprecated-apis)
- [New setStories event](#new-setstories-event)
- [Client API changes](#client-api-changes)
- [Removed Legacy Story APIs](#removed-legacy-story-apis)
- [Can no longer add decorators/parameters after stories](#can-no-longer-add-decoratorsparameters-after-stories)
Expand Down Expand Up @@ -295,6 +296,31 @@ See the migration guides for further details:
- [Unified docs preset](#unified-docs-preset)
- [Addon centered decorator deprecated](#addon-centered-decorator-deprecated)

### New setStories event

The `setStories`/`SET_STORIES` event has changed and now denormalizes global and kind-level parameters. The new format of the event data is:

```js
{
globalParameters: { p: 'q' },
kindParameters: { kind: { p: 'q' } },
stories: /* as before but with only story-level parameters */
}
```

If you want the full denormalized parameters for a story, you can do something like:

```js
import { combineParameters } from '@storybook/api';

const story = data.stories[storyId];
const parameters = combineParameters(
data.globalParameters,
data.kindParameters[story.kind],
story.parameters
);
```

### Client API changes

#### Removed Legacy Story APIs
Expand Down Expand Up @@ -347,6 +373,10 @@ _You cannot set parameters from decorators_

Parameters are intended to be statically set at story load time. So setting them via a decorator doesn't quite make sense. If you were using this to control the rendering of a story, chances are using the new `args` feature is a more idiomatic way to do this.

_You can only set storySort globally_

If you want to change the ordering of stories, use `export const parameters = { options: { storySort: ... } }` in `preview.js`.

### Simplified Render Context

The `RenderContext` that is passed to framework rendering layers in order to render a story has been simplified, dropping a few members that were not used by frameworks to render stories. In particular, the following have been removed:
Expand Down
23 changes: 8 additions & 15 deletions docs/src/pages/configurations/options-parameter/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,12 +77,10 @@ addons.setConfig({

### showRoots

Import and use `addParameters` with the `options` key in your `preview.js` file.
Export `parameters` with the `options` key in your `preview.js` file.

```js
import { addParameters } from '@storybook/react';

addParameters({
export const parameters = {
options: {
/**
* display the top-level grouping as a "root" in the sidebar
Expand All @@ -100,46 +98,41 @@ By default, stories are sorted in the order in which they were imported. This ca
The most powerful method of sorting is to provide a function to `storySort`. Any custom sorting can be achieved with this method.

```js
import { addParameters } from '@storybook/react';

addParameters({
export const parameters = {
options: {
storySort: (a, b) =>
a[1].kind === b[1].kind ? 0 : a[1].id.localeCompare(b[1].id, undefined, { numeric: true }),
},
});
};
```

The `storySort` can also accept a configuration object.

```js
import { addParameters, configure } from '@storybook/react';

addParameters({
export parameters = {
options: {
storySort: {
method: 'alphabetical', // Optional, defaults to 'configure'.
order: ['Intro', 'Components'], // Optional, defaults to [].
locales: 'en-US', // Optional, defaults to system locale.
},
},
});
};
```

To sort your stories alphabetically, set `method` to `'alphabetical'` and optionally set the `locales` string. To sort your stories using a custom list, use the `order` array; stories that don't match an item in the `order` list will appear after the items in the list.

The `order` array can accept a nested array in order to sort 2nd-level story kinds. For example:

```js
import { addParameters, configure } from '@storybook/react';

addParameters({
export parameters = {
options: {
storySort: {
order: ['Intro', 'Pages', ['Home', 'Login', 'Admin'], 'Components'],
},
},
});
};
```

Which would result in this story ordering:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ exports[`Storyshots Core/Parameters passed to story 1`] = `
"globalParameter": "globalParameter",
"framework": "angular",
"chapterParameter": "chapterParameter",
"argTypes": {},
"storyParameter": "storyParameter",
"__id": "core-parameters--passed-to-story"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ exports[`Storyshots Core/Parameters passed to story 1`] = `
"globalParameter": "globalParameter",
"framework": "riot",
"chapterParameter": "chapterParameter",
"argTypes": {},
"__id": "core-parameters--passed-to-story",
"storyParameter": "storyParameter",
"id": "root",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ exports[`Storyshots Core/Parameters passed to story 1`] = `
"globalParameter": "globalParameter",
"framework": "vue",
"chapterParameter": "chapterParameter",
"argTypes": {},
"storyParameter": "storyParameter",
"__id": "core-parameters--passed-to-story"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ exports[`Storyshots Custom/Decorator for Vue With Data 1`] = `
"parameters": {
"globalParameter": "globalParameter",
"framework": "vue",
"argTypes": {},
"__id": "custom-decorator-for-vue--with-data"
},
"args": {},
Expand Down
21 changes: 20 additions & 1 deletion lib/api/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,13 @@ import React, {
useMemo,
useRef,
} from 'react';
import { mergeWith } from 'lodash';

import {
SET_STORIES,
STORY_CHANGED,
SHARED_STATE_CHANGED,
SHARED_STATE_SET,
SET_STORIES,
} from '@storybook/core-events';
import { RenderData as RouterData } from '@storybook/router';
import { Listener } from '@storybook/channels';
Expand Down Expand Up @@ -93,9 +94,14 @@ export type ManagerProviderProps = RouterData &
children: ReactNode | ((props: Combo) => ReactNode);
};

// These types are duplicated in addons.
export type StoryId = string;
export type StoryKind = string;

export interface Args {
[key: string]: any;
}

export interface ArgType {
name?: string;
description?: string;
Expand All @@ -107,6 +113,19 @@ export interface ArgTypes {
[key: string]: ArgType;
}

export interface Parameters {
[key: string]: any;
}

// This is duplicated from @storybook/client-api for the reasons mentioned in lib-addons/types.js
export const combineParameters = (...parameterSets: Parameters[]) =>
mergeWith({}, ...parameterSets, (objValue: any, srcValue: any) => {
// Treat arrays as scalars:
if (Array.isArray(srcValue)) return srcValue;

return undefined;
});

export type ModuleFn = (m: ModuleArgs) => Module;

interface Module {
Expand Down
38 changes: 34 additions & 4 deletions lib/api/src/lib/stories.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import deprecate from 'util-deprecate';
import dedent from 'ts-dedent';
import { sanitize, parseKind } from '@storybook/csf';
import { mapValues } from 'lodash';

import { Args } from '../index';
import { StoryId, StoryKind, Args, Parameters, combineParameters } from '../index';
import merge from './merge';
import { Provider } from '../modules/provider';
import { ViewMode } from '../modules/addons';

export type StoryId = string;
export { StoryId };

export interface Root {
id: StoryId;
Expand Down Expand Up @@ -48,7 +49,7 @@ export interface Story {
depth: number;
parent: StoryId;
name: string;
kind: string;
kind: StoryKind;
refId?: string;
children?: StoryId[];
isComponent: boolean;
Expand All @@ -73,7 +74,7 @@ export interface StoryInput {
id: StoryId;
name: string;
refId?: string;
kind: string;
kind: StoryKind;
children: string[];
parameters: {
fileName: string;
Expand Down Expand Up @@ -103,6 +104,20 @@ export interface StoriesRaw {
[id: string]: StoryInput;
}

export interface SetStoriesPayload {
v?: number;
stories: StoriesRaw;
}

export interface SetStoriesPayloadV2 extends SetStoriesPayload {
v: 2;
globalParameters: Parameters;
kindParameters: {
[kind: string]: Parameters;
};
stories: StoriesRaw;
}

const warnUsingHierarchySeparatorsAndShowRoots = deprecate(
() => {},
dedent`
Expand Down Expand Up @@ -135,6 +150,21 @@ const toGroup = (name: string) => ({
id: toKey(name),
});

export const denormalizeStoryParameters = ({
globalParameters,
kindParameters,
stories,
}: SetStoriesPayloadV2): StoriesRaw => {
return mapValues(stories, (storyData) => ({
...storyData,
parameters: combineParameters(
globalParameters,
kindParameters[storyData.kind],
(storyData.parameters as unknown) as Parameters
),
}));
};

export const transformStoriesRawToStoriesHash = (
input: StoriesRaw,
base: StoriesHash,
Expand Down
33 changes: 21 additions & 12 deletions lib/api/src/modules/stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@ import {
UPDATE_STORY_ARGS,
STORY_ARGS_UPDATED,
STORY_CHANGED,
SET_STORIES,
SELECT_STORY,
SET_STORIES,
} from '@storybook/core-events';

import { logger } from '@storybook/client-logger';
import {
denormalizeStoryParameters,
transformStoriesRawToStoriesHash,
StoriesHash,
Story,
Group,
StoriesRaw,
SetStoriesPayload,
StoryId,
isStory,
Root,
isRoot,
StoriesRaw,
SetStoriesPayloadV2,
} from '../lib/stories';

import { Args, ModuleFn } from '../index';
Expand Down Expand Up @@ -302,30 +305,36 @@ export const init: ModuleFn = ({
}
});

fullAPI.on(SET_STORIES, function handleSetStories(data: { stories: StoriesRaw }) {
fullAPI.on(SET_STORIES, function handleSetStories(data: SetStoriesPayload) {
// the event originates from an iframe, event.source is the iframe's location origin + pathname
const { storyId } = store.getState();
const { source }: { source: string } = this;
const [sourceType, sourceLocation] = getSourceType(source);

// TODO: what is the mechanism where we warn here?
if (data.v && data.v > 2)
// eslint-disable-next-line no-console
console.warn(`Received SET_STORIES event with version ${data.v}, we'll try and handle it`);

const stories = data.v
? denormalizeStoryParameters(data as SetStoriesPayloadV2)
: data.stories;

switch (sourceType) {
// if it's a local source, we do nothing special
case 'local': {
fullAPI.setStories(data.stories);
const options = storyId
? fullAPI.getParameters(storyId, 'options')
: fullAPI.getParameters(Object.keys(data.stories)[0], 'options');
fullAPI.setOptions(options);
if (!data.v) throw new Error('Unexpected legacy SET_STORIES event from local source');

fullAPI.setStories(stories);

fullAPI.setOptions((data as SetStoriesPayloadV2).globalParameters.options);
break;
}

// if it's a ref, we need to map the incoming stories to a prefixed version, so it cannot conflict with others
case 'external': {
const ref = fullAPI.findRef(sourceLocation);

if (ref) {
console.log('ref2', ref);
fullAPI.setRef(ref.id, { ...ref, ...data }, true);
fullAPI.setRef(ref.id, { ...ref, ...data, stories }, true);
break;
}
}
Expand Down
Loading