React Storybook Controls - The easy way
Tags: react, storybook, javascript, webdev
Published: 2021-09-12 07:16:32

Storybook is an open source tool for building UI components and pages in isolation. It streamlines UI development, testing, and documentation.

Storybook for React offers functionality to control props from a very pretty ui.
![Storybook Controls](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/btvpda8b7lo5s7lly7mo.png)

We will explore these things in this post:
- Knobs vs Controls
- The good - React-docgen
- The bad - More words to type
- The solution
- Bonus - Snippets

Till storybook v5, this was achieved using a community plugin `Knobs`, which is very popular with around ~1m downloads weekly.
![Knobs weekly downloads](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y49sx31g4uovzk84d2wp.png)
<figcaption>Knobs weekly downloads</figcaption>
 
In Storybook v6, the team released Essentials addons:
![Storybook essentials](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/1gilnw2cbmie30zqxhqi.png)

This was in alignment with [Storybook's Zero-config initiative](https://storybook.js.org/blog/zero-config-storybook/) (which included out-of-box TS support, essentials addons, and extended support of more frameworks).

## Knobs vs Controls
With this storybook migrated from Knobs in v5 to Controls in v5
#### Knobs
![Knobs in v5](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/40p7di4qvc3i5scmml13.png)
<figcaption>Knobs in v5</figcaption>
```typescript
export const withAButton = () => (
  <button disabled={boolean('Disabled', false)}>{text('Label', 'Hello Storybook')}</button>
);
```

#### Controls
![Controls in v6](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/5g9frllxivnvss10ch1w.png)
<figcaption>Controls in v6</figcaption>
```typescript
export default {
  title: 'Button',
  component: Button,
  argTypes: {
    variant: {
      options: ['primary', 'secondary'],
      control: { type: 'radio' }
    }
  }
};
```

### The good
With the controls, storybook can automatically detect props using react doc-gen, and can automatically generate all the controls for you.
![React docgen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bkpm41ffjoolqe5u7m3d.png)
 
### The bad
For me, some of the controls were not automatically generated due to some of HOCs that we were using (I know, fixing HOCs can fix react docgen as well).

Also, in-case I want to add custom controls, or want to define controls on my own, then the syntax is a little bit more typing over Knobs syntax.

## The Solution
Given the new controls syntax, and the ease of use of old Knobs functions, I ended up creating my own wrappers over new controls.

### Wrappers
The approach looks like this:

```typescript
export const select = <T extends any>(options: T[], defaultValue?: T, props: Partial<ArgProps> = {}) => {
  const allProps: Partial<ReturnedArg<T>> = createControlFromProps<T>(props);
  const type = props.type || 'select';

  allProps.defaultValue = defaultValue || options[0];
  allProps.control = { type, options };
  return allProps;
};
```

So basically, I took input in knobs format, and returned in controls format.

Here is the full file with these controls:
- select
- boolean
- text
- number
- obj
- action

{% gist https://gist.github.com/piyushkmr/67310263559bdbb6b0d2dca7cda363fc file=storybookControlsWrapper.tsx %}

### Usage 
With this, the usage looks like:

```typescript
const argTypes = getArgs<InputProps>({
  label: text('Name'),
  size: select(['small', 'medium', 'large']),
  fullWidth: boolean (false),
  onChange: action('change'),
});
```

Here is the full code with Input Stories:
{% gist https://gist.github.com/piyushkmr/67310263559bdbb6b0d2dca7cda363fc file=Input.stories.tsx %}

### Bonus - Snippets
An added bonus will be, create a snippet for a story, and next time have a story ready to be served with few key-strokes.

{% gist https://gist.github.com/piyushkmr/67310263559bdbb6b0d2dca7cda363fc file=Story.code-snippets.json %}