Skip to content

Repository files navigation

rescript-storybook

Rescript bindings for Storybook.

The goal of this project is to provide bindings for the main Storybook API, as well as the official add-ons. Currently it supports:

Getting Started

First install this package:

npm install rescript-storybook

Next, you'll need to add rescript-storybook to your bsconfig.json as a dependency.

Then, get Storybook up and running according to their docs

Note: This library does not attempt to provide a way to configure storybook in Reason - just use the standard JS configs.

In your /.storybook/config.js, import your stories from wherever your compiled Reason modules end up. For example, if you're writing your stories inside a __stories__ directory, and bsb is configured for a standard build, you might do something like:

constreq=require.context("../lib/js",true,/\__stories__\/.*.js$/);configure(()=>{req.keys().forEach((module)=>{req(module).default();});},module);

or if you are using Storybook v6.

/* .storybook/main.js */module.exports={stories: ["../stories/**/*.js"],addons: ["@storybook/addon-actions","@storybook/addon-links","@storybook/addon-knobs/register",],};

Note that in the above example, we're assuming the convention of each module containing a function as the default export. rescript-storybook is accountable for that while writting your stories:

Writing a story

Here's a basic story in its entirety:

openBsStorybook.Story;let_module= [%bs.raw"module"];
storiesOf("My First Reason Story", _module)
->add("Chapter I",()=> <span> {React.string("Hello rescript-storybook!")} </span>);

Storybook uses a reference to the module global provided by webpack to facilitate hot-reloading. We'll access that via the [%bs.raw] decorator.

Writing a CSF Story

If you'd prefer to use the newer Component Story Format, you can do that as well:

openBsStorybook;letdefault=CSF.make(~title="My CSF Story",());letbutton=()=> <MyButton />;
button->CSF.addMeta(~name="Plain Button",());

The Actions Addon

The action addon's API is essentially unchanged from its JS implementation:

Make sure that you have @storybook/addon-actions in the config

letclickAction=Action.action("I Clicked The Button!");
<div onClick={clickAction} />

The Knobs Addon

To use knobs you have twoo ways:

Make sure that you have @storybook/addon-knobs/register in the config

As a decorator

openStorybook;openStory;let_module= [%bs.raw"module"];
storiesOf("My First Reason Story", _module)
->addDecorator(Knobs.withKnobs)
->add("Chaper with Knobs",()=> {
letname=Knobs.text(~label="Name", ~defaultValue="Patrick",());
<span> {React.string(name)} </span>;
})

Creating the story

openStorybook;openStory;let_module= [%bs.raw"module"];letknobsStory=Main.createStory(
~title="Hey look, knobs!",
~decorators=[Knobs.withKnobs],
~_module,(),
);
knobsStory.add("Chaper with Knobs",()=> {
letname=Knobs.text(~label="Name", ~defaultValue="Patrick",());
<span> {React.string(name)} </span>;
})

Each knob type is invoked using a function with labeled arguments, and each requires passing unit as the final argument. They all share a ~label argument, and a ~defaultValue argument (where appropriate):

Text

letmyText=Knobs.text(~label="What should it say?", ~defaultValue="Sup?",());

Boolean

letmyBoolean=Knobs.boolean(~label="Should Show?", ~defaultValue=true,());

Note: The boolean type will call the underlying JS knob with a defaultValue of false if one is not provided.

Color

letmyColor=Knobs.color(~label="Color", ~defaultValue="#333"());

Number

The number type works with floats. If no defaultValue is provided, it will pass 0. It also takes an optional rangeConfig record, which allows for specifying a min, max, and step so that the knob is displayed as a range slider.

letnum1=Knobs.number(~label="Number 1",());letnum2=Knobs.number(
~label="Number 2",
~rangeConfiguration={range:true, min:0., max:10., step:1.},()
);

Select

To use the select knob, first define a record type that contains the shape of the options, then the actual options as a type of selectConfig, passing your shape as the constructor type:

typeselectOptions= {
one:string,
two:string
};letoptions:Knobs.selectConfig(selectOptions) = {
one:"Hello",
two:"Hi"
};

Then define the select knob like so:

letgreeting=Knobs.select(~label="Greeting", ~options, ~defaultValue=options.one,());

Button

Knobs.button(
~label="Knob Button",
~handler=Action.action("Clicked the knob button"),()
)

Object

letobj=Knobs.object_(~label="User", ~defaultValue={"color":"grey"},());

Js.Dict

https://bucklescript.github.io/bucklescript/api/Js.Dict.html

letoptions=Js.Dict.fromArray([|
("Red","red"),
("Blue","blue"),
("Yellow","yellow"),
("None",""),|]);letcolor=Knobs.selectFromDict(
~label="MySelection",
~options,
~defaultValue="red",(),
);

Array

letcolor=Knobs.selectFromArray(
~label="MySelection",
~options=[|"red","blue","yellow"|],
~defaultValue="red",(),
);

About

Storybook bindings for Rescript

Topics

Resources

Stars

78 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages