Skip to content

Instantly share code, notes, and snippets.

@shilman
Last active April 12, 2018 13:39
Show Gist options
  • Save shilman/792dc25550daa9c2bf37238f4ef7a398 to your computer and use it in GitHub Desktop.
Save shilman/792dc25550daa9c2bf37238f4ef7a398 to your computer and use it in GitHub Desktop.
withX strawman

WithX API proposal

Today's type system

type story = (context) => renderable
type decorator = (story, context) => renderable

For example:

storiesOf('kindName', module)
  .addDecorator(withKnobs)
  .add('storyName', (context) => <Comp name={context.story} />)

Proposed type system

type story = (context) => renderable
type wrapper = story => story
type makewrapper = (options) => wrapper

# purely for backwards compatibility
type decorator = (story, context) => renderable

We can also create an adapter to use HOC's in the existing decorator API:

const makeDecorator = (wrapper) => {
  return (story, context) =>
    wrapper(story)(context)
}

For example:

storiesOf('foo', module)
  .addDecorator(makeDecorator(withNotes({ text: 'notes for foo' })))
  .addDecorator(makeDecorator(withInfo({ text: 'info for foo' })))
  .add('story1', () => <Comp {...props1} />)
  .add('story2', () => <Comp {...props2} />)
  .add('story3', compose(
    withNotes({ text: 'notes for story3' }),
    withInfo({ text: 'info for story3'})
  )((context) => <Comp {...props3} />))

Options types

withX functions are a simple and well-understood building block, but they should also be easy to use. Therefore, we propose the following convention for options types:

withInfo({ text: 'some info text', option1: someVal, option2: someVal })(storyFn)

This makes it easy to extend addons with named options in a future-proof way. However, it's not very convenient if you've already set up the default options elsewhere in your code. Therefore, addons can support whatever options they want, for example, withInfo also supports:

withInfo('some info text')(storyFn)

In the future, we propose to support a simpler API to addons, such as the following:

registerAddon({ info: withInfo, notes: withNotes })
storiesOf(...)
  .add('story', storyFn, { info: 'some info text', notes: 'some notes text' })

Or, more verbose:

registerAddon({ info: withInfo, notes: withNotes })
storiesOf(...)
  .add('story', storyFn, { 
     info: { text: 'some info text', option1: val1, .... }
     notes: 'some notes text'
   })

Dealing with existing withKnobs

Today's withKnobs is a decorator which screws things up. Proposal:

const withKnobs = (options) => (story) => ...;
const withKnobsDeprecated = deprecate(makeDecorator(withKnobs))
export { withKnobsDeprecated as withKnobs };
export default withKnobs;

For example (new code):

import withKnobs from '@storybook/addon-knobs';
import { makeDecorator } from '@storybook/addons';
addDecorator(makeDecorator(withKnobs()));

For example (legacy code):

import { withKnobs } from '@storybook/addon-knobs';
addDecorator(withKnobs);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment