60
54%
MIT
BuckleScript bindings for Storybook.js

bs-storybook

BuckleScript bindings for Storybook.js! 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 bs-storybook

Next, you'll need to add bs-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:

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

Note that in the above example, we're assuming the convention of each module containing a function as the default export. We'll account for that when writing our stories in the next section.

Writing a story

Here's a basic story in its entirety:

  • RE
  • ML
open BsStorybook.Story;

let _module = [%bs.raw "module"];

storiesOf("My First Reason Story", _module)
|. addDecorator()
|. add("first chapter", () =>
     <span> (ReasonReact.stringToElement("Hello bs-storybook!")) </span>
   );
open BsStorybook.Story
let _module = [%bs.raw "module"]
let _ =
  ((storiesOf "My First Reason Story" _module) |. (addDecorator ())) |.
    (add "first chapter"
       (fun ()  ->
          ((span
              ~children:[ReasonReact.stringToElement "Hello bs-storybook!"]
              ())[@JSX ])))

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

The Actions Addon

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

  • RE
  • ML
let clickAction = Action.action("I Clicked The Button!");
let clickAction = Action.action "I Clicked The Button!"

The Knobs Addon

To use knobs, be sure to add the decorator to your story definition:

  • RE
  • ML
let knobsStory =
	createStory(~title="Hey look, knobs!", ~decorators=[Knobs.withKnobs], ~_module, ());
let knobsStory =
  createStory ~title:"Hey look, knobs!" ~decorators:[Knobs.withKnobs]
    ~_module ()

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

  • RE
  • ML
let myText = Knobs.text(~label="What should it say?", ~defaultValue="Sup?", ());
let myText = Knobs.text ~label:"What should it say?" ~defaultValue:"Sup?" ()

Boolean

  • RE
  • ML
let myBoolean = Knobs.boolean(~label="Should Show?", ~defaultValue=true, ());
let myBoolean = 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

  • RE
  • ML
let myColor = Knobs.color(~label="Color", ~defaultValue="#333" ());
let myColor = 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.

  • RE
  • ML
let num1 = Knobs.number(~label="Number 1", ());
let num2 =
	Knobs.number(
		~label="Number 2",
		~rangeConfiguration={min: 0., max: 10., step: 1.},
		()
	);
let num1 = Knobs.number ~label:"Number 1" ()
let num2 =
  Knobs.number ~label:"Number 2"
    ~rangeConfiguration:{ 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:

  • RE
  • ML
type selectOptions = {
	one: string,
	two: string
};

let options : Knobs.selectConfig(selectOptions) = {
	one: "Hello",
	two: "Hi"
};
type selectOptions = {
  one: string;
  two: string;}
let options: selectOptions Knobs.selectConfig = { one = "Hello"; two = "Hi" }

Then define the select knob like so:

  • RE
  • ML
let greeting = Knobs.select(~label="Greeting", ~options, ~defaultValue=options.one, ());
let greeting =
  Knobs.select ~label:"Greeting" ~options ~defaultValue:(options.one) ()

Button

  • RE
  • ML
Knobs.button(
	~label="Knob Button",
	~handler=Action.action("Clicked the knob button"),
	()
)
let _ =
  Knobs.button ~label:"Knob Button"
    ~handler:(Action.action "Clicked the knob button") ()

Object

Not yet implemented.

Array

Not yet implemented.