Storybook: Addon-controls: Add custom user-defined controls

Created on 9 Jul 2020  路  8Comments  路  Source: storybookjs/storybook

We are currently documenting our React components in Storybook, and decided to go straight to version 6.0 to make use of the new Args and Controls addons. During that work, we realised that the choice of controls is very limited.

Here are some examples:

  • We want users to select from a pre-defined list of colors. The "select" control can only display text but we want to preview the color next to it.
  • We want users to select from a list of icons. The control should display a list of all icons and clicking on an icon selects it.
  • One prop may allow for string[], so we want to allow the user to add strings to the list, or remove existing strings.

Any chance to allow users to specify custom controls in argTypes that are not built into addon-controls?

Here's how this could look like in React code, where ColorPicker is a custom control we'd build:

const ColorPicker = ({ value, setValue }) => {
  return (
    <select value={value} onChange={setValue}>
      <option>red</option>
      <option>blue</option>
    </select>
  );
}

export default {
  title: "ExampleComponent",
  component: ExampleComponent,
  argTypes: {
    color: {
      control: ColorPicker,
    },
  },
};
controls feature request todo

Most helpful comment

@stefanhamburger @hydrosquall @Rycochet @ryaninvents super excited to see ya'll helping 馃檱

@shilman I'm concerned about:

export default {
  title: "ExampleComponent",
  component: ExampleComponent,
  argTypes: {
    color: {
      control: ColorPicker,
    },
  },
};

If the actual control component is specified in the story (preview), but the control component itself is displayed in an addon (manager) aren't we opening ourselves up to a world of hurt?

Should we not do this instead:

// story
export default {
  title: "ExampleComponent",
  component: ExampleComponent,
  argTypes: {
    color: {
      control: 'ControlColorPicker',
    },
  },
};
// some addon
addons.register('AddonControlsColorPicker', api => {
  addons.add('AddonControlsColorPicker', {
    type: addons.types.CONTROL,
    render: ColorPicker,
  }
})

All 8 comments

I'd be happy to start working on this. I'd like to add a gradient editor and rich text editor to my Storybook's controls without having to modify the core controls package.

One generic thing that would be good is nested controls. For arrays and tuples it is relatively simple (a tuple is a fixed length array) - in my use case I'd like to be able to have a [number, number] that can get the normal range options on it. Object types would likely be a bit more complicated - though the current way of pasting some JSON is pretty good for most situations.

I've not looked at the source, but I feel that making the recognition and control both generic, so certain ones can try to identify then including user-defined controls that support it (the aforementioned array type might be useful etc)

Copying @ndelangen 's suggestion from another thread to make these user-defined controls lazy-loaded:

https://github.com/storybookjs/storybook/pull/12685#issuecomment-711761883

@stefanhamburger @hydrosquall @Rycochet @ryaninvents super excited to see ya'll helping 馃檱

@shilman I'm concerned about:

export default {
  title: "ExampleComponent",
  component: ExampleComponent,
  argTypes: {
    color: {
      control: ColorPicker,
    },
  },
};

If the actual control component is specified in the story (preview), but the control component itself is displayed in an addon (manager) aren't we opening ourselves up to a world of hurt?

Should we not do this instead:

// story
export default {
  title: "ExampleComponent",
  component: ExampleComponent,
  argTypes: {
    color: {
      control: 'ControlColorPicker',
    },
  },
};
// some addon
addons.register('AddonControlsColorPicker', api => {
  addons.add('AddonControlsColorPicker', {
    type: addons.types.CONTROL,
    render: ColorPicker,
  }
})

@ndelangen I'm with you there - it's not so much a security thing, but being able to keep the definition and usage separate makes it easier to debug things. I do feel that it might be a little bit much for simple uses - but given a suitably "powerful" api for registering and using them it could probably reduce the need for actually doing things like that. Definitely needs a nice and simple example however it gets added so that people don't get confused over what to do, and what it can achieve ;-)

@ndelangen Yep, the user/addon needs to register a mapping from string to React component in both the preview and the manager, since the control will be rendered in both the addons panel AND in the docs page. The user would then reference that control by string in the argTypes annotation.

Strawman spec:

  • [ ] Add control registration to addons API (both manager & preview)
  • [ ] Convert built-in controls to use registration mechanism (then they can be overridden!)
  • [ ] Create a sample custom control in official-storybook
  • [ ] Add to controls documentation

I'd be happy to assist in the creation of this

Was this page helpful?
0 / 5 - 0 ratings