Using storybook with CLJS

https://storybook.js.org/ is a pretty popular JS tool for interactive UI testing.

I was debugging the JS tool integration of shadow-cljs and had to pick a JS tool so I picked storybook. It works quite well and looks nice.

It can produce static output which is sort of neat for demo purposes:
https://code.thheller.com/demos/storybook-static

In CLJS we have devcards but with a few macros the storybook integration could be pretty good as well. I used their react guide and ported a quick demo to CLJS.

(ns demo.app-stories
  (:require
    ["@storybook/react" :refer (storiesOf)]
    ["@storybook/addon-actions" :refer (action)]
    [shadow.markup.react :as html :refer (defstyled)]))

(defstyled big-button :button [_]
  {:font-size "2em"})

;; FIXME: write macro to make this nicer

(-> (storiesOf "Button CLJS" js/module)
    (.add "Normal" #(html/button {:onClick (action "click")} "Hello World"))
    (.add "Big" #(big-button {:onClick (action "big click")} "Hello World")))

Full code:
https://github.com/thheller/shadow-cljs-examples/tree/master/cljs-storybook

10 Likes

Wow, this is really cool. :+1: How much of the interop is made easier by shadow-cljs and how much is “standard” ClojureScript?

You could probably get this to work without shadow-cljs but I honestly couldn’t tell you how.

storybook uses webpack to bundle the JS. shadow-cljs with :target :npm-module outputs CommonJS friendly code that can directly be used with require so it works with JS tools like webpack.

From a Compiler perspective this is standard so nothing special there. Only the output is modified slightly AFTER compilation to add the require boilerplate.

1 Like