Skip to content

Commit

Permalink
Merge pull request #9914 from storybookjs/refactor-client-api-events
Browse files Browse the repository at this point in the history
Core: Overhaul start.js and event emitting/listening
  • Loading branch information
shilman authored Mar 2, 2020
2 parents 7c55dd1 + b9af9f6 commit aee8873
Show file tree
Hide file tree
Showing 61 changed files with 2,031 additions and 1,027 deletions.
16 changes: 16 additions & 0 deletions MIGRATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
- [Removed legacy story APIs](#removed-legacy-story-apis)
- [Can no longer add decorators/parameters after stories](#can-no-longer-add-decorators-parameters-after-stories)
- [Changed Parameter Handling](#changed-parameter-handling)
- [Simplified Render Context](#simplified-render-context)
- [Story Store immutable outside of configuration](#story-store-immutable-outside-of-configuration)
- [From version 5.2.x to 5.3.x](#from-version-52x-to-53x)
- [To main.js configuration](#to-mainjs-configuration)
- [Create React App preset](#create-react-app-preset)
Expand Down Expand Up @@ -181,6 +183,20 @@ _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.

### 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:

- `selectedKind`/`selectedStory` -- replaced by `kind`/`name`
- `configApi`
- `storyStore`
- `channel`
- `clientApi`

### Story Store immutable outside of configuration

You can no longer change the contents of the StoryStore outside of a `configure()` call. This is to ensure that any changes are properly published to the manager. If you want to add stories "out of band" you can call `store.startConfiguring()` and `store.finishConfiguring()` to ensure that your changes are published.

## From version 5.2.x to 5.3.x

### To main.js configuration
Expand Down
4 changes: 2 additions & 2 deletions addons/docs/docs/docspage.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ Here is the `SlotContext` type definition:
```ts
export interface SlotContext {
id?: string;
selectedKind?: string;
selectedStory?: string;
kind?: string;
name?: string;
parameters?: any;
storyStore?: any;
}
Expand Down
4 changes: 2 additions & 2 deletions addons/docs/src/blocks/DocsContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import { Context, createContext } from 'react';

export interface DocsContextProps {
id?: string;
selectedKind?: string;
selectedStory?: string;
kind?: string;
name?: string;

/**
* mdxStoryNameToKey is an MDX-compiler-generated mapping of an MDX story's
Expand Down
18 changes: 9 additions & 9 deletions addons/docs/src/blocks/DocsPage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ describe('defaultTitleSlot', () => {
const parameters = {
options: { showRoots: true },
};
expect(defaultTitleSlot({ selectedKind: 'a/b/c', parameters })).toBe('c');
expect(defaultTitleSlot({ selectedKind: 'a|b', parameters })).toBe('a|b');
expect(defaultTitleSlot({ selectedKind: 'a/b/c.d', parameters })).toBe('c.d');
expect(defaultTitleSlot({ kind: 'a/b/c', parameters })).toBe('c');
expect(defaultTitleSlot({ kind: 'a|b', parameters })).toBe('a|b');
expect(defaultTitleSlot({ kind: 'a/b/c.d', parameters })).toBe('c.d');
});
it('no showRoots', () => {
const parameters = {};
expect(defaultTitleSlot({ selectedKind: 'a/b/c', parameters })).toBe('c');
expect(defaultTitleSlot({ selectedKind: 'a|b', parameters })).toBe('b');
expect(defaultTitleSlot({ selectedKind: 'a/b/c.d', parameters })).toBe('d');
expect(defaultTitleSlot({ kind: 'a/b/c', parameters })).toBe('c');
expect(defaultTitleSlot({ kind: 'a|b', parameters })).toBe('b');
expect(defaultTitleSlot({ kind: 'a/b/c.d', parameters })).toBe('d');
});
it('empty options', () => {
const parameters = { options: {} };
expect(defaultTitleSlot({ selectedKind: 'a/b/c', parameters })).toBe('c');
expect(defaultTitleSlot({ selectedKind: 'a|b', parameters })).toBe('b');
expect(defaultTitleSlot({ selectedKind: 'a/b/c.d', parameters })).toBe('d');
expect(defaultTitleSlot({ kind: 'a/b/c', parameters })).toBe('c');
expect(defaultTitleSlot({ kind: 'a|b', parameters })).toBe('b');
expect(defaultTitleSlot({ kind: 'a/b/c.d', parameters })).toBe('d');
});
});
12 changes: 6 additions & 6 deletions addons/docs/src/blocks/Title.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ interface TitleProps {
slot?: StringSlot;
children?: JSX.Element | string;
}
export const defaultTitleSlot: StringSlot = ({ selectedKind, parameters }) => {
export const defaultTitleSlot: StringSlot = ({ kind, parameters }) => {
const {
showRoots,
hierarchyRootSeparator: rootSeparator = '|',
Expand All @@ -17,27 +17,27 @@ export const defaultTitleSlot: StringSlot = ({ selectedKind, parameters }) => {

let groups;
if (typeof showRoots !== 'undefined') {
groups = selectedKind.split('/');
groups = kind.split('/');
} else {
// This covers off all the remaining cases:
// - If the separators were set above, we should use them
// - If they weren't set, we should only should use the old defaults if the kind contains '.' or '|',
// which for this particular splitting is the only case in which it actually matters.
({ groups } = parseKind(selectedKind, { rootSeparator, groupSeparator }));
({ groups } = parseKind(kind, { rootSeparator, groupSeparator }));
}

return (groups && groups[groups.length - 1]) || selectedKind;
return (groups && groups[groups.length - 1]) || kind;
};

export const Title: FunctionComponent<TitleProps> = ({ slot, children }) => {
const context = useContext(DocsContext);
const { selectedKind, parameters } = context;
const { kind, parameters } = context;
let text: JSX.Element | string = children;
if (!text) {
if (slot) {
text = slot(context);
} else {
text = defaultTitleSlot({ selectedKind, parameters });
text = defaultTitleSlot({ kind, parameters });
}
}
return text ? <PureTitle className="sbdocs-title">{text}</PureTitle> : null;
Expand Down
4 changes: 2 additions & 2 deletions addons/docs/src/blocks/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ export type DocsStoryProps = StoryData & {

export interface SlotContext {
id?: string;
selectedKind?: string;
selectedStory?: string;
kind?: string;
name?: string;
parameters?: any;
storyStore?: any;
}
Expand Down
4 changes: 2 additions & 2 deletions addons/docs/src/blocks/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import { DocsContextProps } from './DocsContext';
import { StoryData, Component } from './shared';

export const getDocsStories = (context: DocsContextProps): StoryData[] => {
const { storyStore, selectedKind } = context;
const { storyStore, kind } = context;

if (!storyStore) {
return [];
}

return storyStore
.getStoriesForKind(selectedKind)
.getStoriesForKind(kind)
.filter((s: any) => !(s.parameters && s.parameters.docs && s.parameters.docs.disable));
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ function bootstrapADocumentAndReturnANode() {

function makeSureThatResultIsRenderedSomehow({ context, result, rootElement }: any) {
if (!rootElement.firstChild) {
const { kind, name } = context;
riotForStorybook.render({
storyFn: () => result,
selectedKind: context.kind,
selectedStory: context.name,
kind,
name,
});
}
}
Expand Down
12 changes: 3 additions & 9 deletions app/ember/src/client/preview/render.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { window, document } from 'global';
import dedent from 'ts-dedent';
import { RenderMainArgs, ElementArgs, OptionsArgs } from './types';
import { RenderContext, ElementArgs, OptionsArgs } from './types';

declare let Ember: any;

Expand Down Expand Up @@ -52,18 +52,12 @@ function render(options: OptionsArgs, el: ElementArgs) {
});
}

export default function renderMain({
storyFn,
selectedKind,
selectedStory,
showMain,
showError,
}: RenderMainArgs) {
export default function renderMain({ storyFn, kind, name, showMain, showError }: RenderContext) {
const element = storyFn();

if (!element) {
showError({
title: `Expecting a Ember element from the story: "${selectedStory}" of "${selectedKind}".`,
title: `Expecting a Ember element from the story: "${name}" of "${kind}".`,
description: dedent`
Did you forget to return the Ember element from the story?
Use "() => hbs('{{component}}')" or "() => { return {
Expand Down
10 changes: 1 addition & 9 deletions app/ember/src/client/preview/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
import { StoryFn } from '@storybook/addons'; // eslint-disable-line

export interface RenderMainArgs {
storyFn: StoryFn<any>;
selectedKind: string;
selectedStory: string;
showMain: () => void;
showError: (args: ShowErrorArgs) => void;
}
export { RenderContext } from '@storybook/core';

export interface ShowErrorArgs {
title: string;
Expand Down
10 changes: 5 additions & 5 deletions app/html/src/client/preview/render.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
import { document, Node } from 'global';
import dedent from 'ts-dedent';
import { RenderMainArgs } from './types';
import { RenderContext } from './types';

const rootElement = document.getElementById('root');

export default function renderMain({
storyFn,
selectedKind,
selectedStory,
kind,
name,
showMain,
showError,
forceRender,
}: RenderMainArgs) {
}: RenderContext) {
const element = storyFn();

showMain();
Expand All @@ -27,7 +27,7 @@ export default function renderMain({
rootElement.appendChild(element);
} else {
showError({
title: `Expecting an HTML snippet or DOM node from the story: "${selectedStory}" of "${selectedKind}".`,
title: `Expecting an HTML snippet or DOM node from the story: "${name}" of "${kind}".`,
description: dedent`
Did you forget to return the HTML snippet from the story?
Use "() => <your snippet or node>" or when defining the story.
Expand Down
11 changes: 1 addition & 10 deletions app/html/src/client/preview/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { StoryFn } from '@storybook/addons';
export { RenderContext } from '@storybook/core';

export type StoryFnHtmlReturnType = string | Node;

Expand All @@ -16,12 +16,3 @@ export interface ShowErrorArgs {
title: string;
description: string;
}

export interface RenderMainArgs {
storyFn: () => StoryFn<StoryFnHtmlReturnType>;
selectedKind: string;
selectedStory: string;
showMain: () => void;
showError: (args: ShowErrorArgs) => void;
forceRender: boolean;
}
6 changes: 3 additions & 3 deletions app/marionette/src/client/preview/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ function render(view) {
rootRegion.show(view);
}

export default function renderMain({ storyFn, selectedKind, selectedStory, showMain, showError }) {
export default function renderMain({ storyFn, kind, name, showMain, showError }) {
const element = storyFn();

if (!element) {
showError({
title: `Expecting a Marionette View from the story: "${selectedStory}" of "${selectedKind}".`,
title: `Expecting a Marionette View from the story: "${name}" of "${kind}".`,
description: stripIndents`
Did you forget to return the React element from the story?
Use "() => (<MyComp/>)" or "() => { return <MyComp/>; }" when defining the story.
Expand All @@ -26,7 +26,7 @@ export default function renderMain({ storyFn, selectedKind, selectedStory, showM

if (!isMarionetteRenderable(element)) {
showError({
title: `Expecting a valid Marionette View from the story: "${selectedStory}" of "${selectedKind}".`,
title: `Expecting a valid Marionette View from the story: "${name}" of "${kind}".`,
description: stripIndents`
Seems like you are not returning a correct Marionette View from the story.
Could you double check that?
Expand Down
6 changes: 3 additions & 3 deletions app/marko/src/client/preview/render.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ let activeTemplate = null; // template for the currently loaded component.

export default function renderMain({
storyFn,
selectedKind,
selectedStory,
kind,
name,
showMain,
showError,
parameters,
Expand All @@ -20,7 +20,7 @@ export default function renderMain({

if (!config || !(config.appendTo || config.component || parameters.component)) {
showError({
title: `Expecting an object with a component property to be returned from the story: "${selectedStory}" of "${selectedKind}".`,
title: `Expecting an object with a component property to be returned from the story: "${name}" of "${kind}".`,
description: dedent`
Did you forget to return the component from the story?
Use "() => ({ component: MyComponent, input: { hello: 'world' } })" when defining the story.
Expand Down
12 changes: 3 additions & 9 deletions app/mithril/src/client/preview/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,16 @@ import { document } from 'global';
import m from 'mithril';
import dedent from 'ts-dedent';

import { RenderMainArgs } from './types';
import { RenderContext } from './types';

const rootEl = document.getElementById('root');

export default function renderMain({
storyFn,
selectedKind,
selectedStory,
showMain,
showError,
}: RenderMainArgs) {
export default function renderMain({ storyFn, kind, name, showMain, showError }: RenderContext) {
const element = storyFn();

if (!element) {
const error = {
title: `Expecting a Mithril element from the story: "${selectedStory}" of "${selectedKind}".`,
title: `Expecting a Mithril element from the story: "${name}" of "${kind}".`,
description: dedent`
Did you forget to return the Mithril element from the story?
Use "() => MyComp" or "() => { return MyComp; }" when defining the story.
Expand Down
12 changes: 2 additions & 10 deletions app/mithril/src/client/preview/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import m from 'mithril';

export { RenderContext } from '@storybook/core';

export interface IStorybookStory {
name: string;
render: () => any;
Expand All @@ -16,13 +18,3 @@ export interface ShowErrorArgs {
title: string;
description: string;
}

export interface RenderMainArgs {
storyFn: () => StoryFnMithrilReturnType;
selectedKind: string;
selectedStory: string;
showMain: () => void;
showError: (args: ShowErrorArgs) => void;
showException: (err: Error) => void;
forceRender: boolean;
}
12 changes: 3 additions & 9 deletions app/preact/src/client/preview/render.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
import { h, render } from 'preact';
import { document } from 'global';
import dedent from 'ts-dedent';
import { RenderMainArgs } from './types';
import { RenderContext } from './types';

let renderedStory: Element;
const rootElement = document ? document.getElementById('root') : null;

export default function renderMain({
storyFn,
selectedKind,
selectedStory,
showMain,
showError,
}: RenderMainArgs) {
export default function renderMain({ storyFn, kind, name, showMain, showError }: RenderContext) {
const element = storyFn();

if (!element) {
showError({
title: `Expecting a Preact element from the story: "${selectedStory}" of "${selectedKind}".`,
title: `Expecting a Preact element from the story: "${name}" of "${kind}".`,
description: dedent`
Did you forget to return the Preact element from the story?
Use "() => (<MyComp/>)" or "() => { return <MyComp/>; }" when defining the story.
Expand Down
Loading

0 comments on commit aee8873

Please sign in to comment.