storybook with preact + TS + goober
Tags: preact, storybook, typescript, goober
Published: 2021-01-26 18:30:40

## fail setup storybook

Create a component with preact, TS and goober.

```tsx
import { h } from "preact"
import { styled } from "goober"

const _Button = styled("button")`
  background-color: red;
`

export const Button = () => {
  return <_Button>ok</_Button>
}
```

Next, create the storybook's story.

```
npx sb init
```

```tsx
import { h } from "preact"
import { Button } from "./button"

export default {
  title: "custom/Button",
  component: Button,
  argTypes: {
    backgroundColor: { control: "color" },
    onClick: { action: "onClick" },
  },
}

const Template = (args: any) => <Button {...args} />

export const Primary = Template.bind({})
```

And start this.

```
npm run storybook
```

Let's look at this story. You will see this fail view.

```
h is not defined
ReferenceError: h is not defined
    at Object.Template (http://192.168.0.3:6006/main.4bde6a78d76d85c8a393.bundle.js:353:3)
    at finalStoryFn (http://192.168.0.3:6006/vendors~main.4bde6a78d76d85c8a393.bundle.js:16622:32)
    at http://192.168.0.3:6006/vendors~main.4bde6a78d76d85c8a393.bundle.js:13062:21
    at http://192.168.0.3:6006/vendors~main.4bde6a78d76d85c8a393.bundle.js:14861:14
    at wrapper (http://192.168.0.3:6006/vendors~main.4bde6a78d76d85c8a393.bundle.js:12801:12)
    at http://192.168.0.3:6006/vendors~main.4bde6a78d76d85c8a393.bundle.js:13580:14
    at http://192.168.0.3:6006/vendors~main.4bde6a78d76d85c8a393.bundle.js:13594:26
    at http://192.168.0.3:6006/vendors~main.4bde6a78d76d85c8a393.bundle.js:13062:21
    at http://192.168.0.3:6006/vendors~main.4bde6a78d76d85c8a393.bundle.js:14854:12
    at http://192.168.0.3:6006/vendors~main.4bde6a78d76d85c8a393.bundle.js:14861:14
```

![error](https://dev-to-uploads.s3.amazonaws.com/i/se568pyqxa42fa9iznwt.png)

On the other hand, generated story files by `npx sb init` are successful.

![sampleStory](https://dev-to-uploads.s3.amazonaws.com/i/veh70w7ksii6td3aaxpn.png)
 
Why is this example success, is My story fail to view.

## Let's look at Success Component

Firstly, preact is supported by the storybook official.

FYI: https://www.npmjs.com/package/@storybook/preact

That's why we can generate example preact story files by `npx sb init`.Let's look at this component.

```js
/** @jsx h */
import { h } from "preact"
import PropTypes from "prop-types"
import "./button.css"

/**
 * Primary UI component for user interaction
 */
export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
  const mode = primary
    ? "storybook-button--primary"
    : "storybook-button--secondary"
  return (
    <button
      type="button"
      className={["storybook-button", `storybook-button--${size}`, mode].join(
        " "
      )}
      style={backgroundColor && { backgroundColor }}
      {...props}
    >
      {label}
    </button>
  )
}

Button.propTypes = {
  /**
   * Is this the principal call to action on the page?
   */
  primary: PropTypes.bool,
  /**
   * What background color to use
   */
  backgroundColor: PropTypes.string,
  /**
   * How large should the button be?
   */
  size: PropTypes.oneOf(["small", "medium", "large"]),
  /**
   * Button contents
   */
  label: PropTypes.string.isRequired,
  /**
   * Optional click handler
   */
  onClick: PropTypes.func,
}

Button.defaultProps = {
  backgroundColor: null,
  primary: false,
  size: "medium",
  onClick: undefined,
}
```

There are some different points between my components.

* not TS, but JS.
* jsx pragma in first line.
* not use CSS in JSS, but use plain CSS

Those points are just the reason why my story doesn't work.

## need config jsx factory

### what is /\*\* @jsx h \*/

This is called a jsx pragma.it tells jsx factory to the compiler. For example, react's jsx factory is `createElement`,  preact's jsx factory is `h`, but the compiler doesn't know it.

### story book builds a source by babel

If you use TypeScript Compiler to build an application, the storybook builds a story by babel. That's why you must config about jsx factory to the babel config file.

```js:title=main.js
module.exports = {
  stories: ["../src/**/*.stories.mdx", "../src/**/*.stories.@(js|jsx|ts|tsx)"],
  addons: ["@storybook/addon-links", "@storybook/addon-essentials"],
  babel: async options => ({
    ...options,
    presets: [["@babel/typescript", { jsxPragma: "h" }]],
  }),
}
```

Otherwise, you should write jsx pragma in your story file.

## goober needs init

Goober is a CSS in JS library.This needs a `setup` to run the application for the first time.

```ts
import { setup } from "goober"
import { h, render } from "preact"
import { Button } from "./button"

setup(h)

const App = () => {
  return (
    <div>
      <Button></Button>
    </div>
  )
}

render(<App></App>, document.body)
```

But story book's file doesn't read this entry point. That's why each story file needs `setup(h)`.

```js
import { setup } from "goober"
import { h } from "preact"
import { Button } from "./button"

setup(h)

export default {
  title: "custom/Button",
  component: Button,
  argTypes: {
    backgroundColor: { control: "color" },
    onClick: { action: "onClick" },
  },
}

const Template = (args: any) => <Button {...args} />

export const Primary = Template.bind({})
```

## it works!!

Start storybook after config jsx factory to babel config file and setup goober each story file.
 
```sh
npm run storybook
```

Success!!

![success](https://dev-to-uploads.s3.amazonaws.com/i/dxeovlvo64uer1npcrcf.png)

Source code is here. 

https://github.com/ojisan-toybox/preact-storybook