Using Storybook with Nuxt

By  on  

Storybook Nuxt

Nuxt.js has been a lot of fun to use in production web apps and side projects. It's simple, fast, and seems very in line with web standards when it comes to creating components, importing plugins, etc. Equally as exciting has been the rise of Storybook. It's gained quite a following in the last year for it's ease of use in creating component/pattern libraries and in it's ability to do component based snapshot testing in isolation.

Recently I was wanting to start a new side project and wanted to use Storybook with Nuxt. I was excited to see their documentation on using Storybook with Vue (though React, React Native, and Angular are also supported) but as I was getting started found some bumps in the road. After pulling an all nighter and a few pull requests, I got everything working! This is a repo of the final product and this the story of my journey that night.

Background

For those unfamiliar with Storybook (as I was a last week) or Nuxt I'd like to share a bit about them. Nuxt is an objectively great framework for build applications. It's community driven, easy to learn, and plays well with existing projects that use Node.js. There's a lot of great projects out there using Nuxt and a few key developers that I'd suggest following for more info would be Sarah Drasner, Evan You, and Rachel Nabors. Storybook has quickly grown in the last year to become an essential part of many teams testing suite and a great way of keeping track of component UI. The main premise is that after creating a component you create it's "story". The story will contain different states of a component which provides a visual representation. When you write multiple stories you have your Storybook.

Installing and Setting Up

This was starightforward. I use vue-cli to create a new Nuxt project (specifically the Express flavor) and then used Storybook's documentation on installing their package with Vue. In their documentation they show an example of a config file for Storybook. It discussed using Vuex as a plugin but that's not required to get things going. If you wanna start without the plugins for now you can check out the config file in the repo of my journey.

Storybook has built in "add-ons" that add a lot of value to the package. I was interested in getting the Action and Storyshot addons working. Actions allow Storybook to display data from event handlers in a panel. It's especially useful when you're wanting to see what data is passed when a button in clicked or some other type of event is triggered through interaction with the UI. Storyshots create a snapshot of the component in isolation so that as updates are made you can run a comparison test on components to ensure that there aren't unexpected changes to it's UI. If I'm honest, snapshot testing was a new term for me a week ago, but it's really proven to be useful. Under the hood Storyshots is using Jest to do it's testing. We'll get to my misadventure with Storyshots and Jest in a bit. If you're interested you can find a full list of add-ons, both native and community created, on their website.

Actions

The only part that I struggled with in setting up this addon was getting the package to run properly when I launched npm run storybook. This command is what starts the localhost server for Storybook's UI. Initally in doing this I ended up seeing a stories but no action panel, despite install storybook/addon-actions through NPM. Turns out I was missing an addons.js file from the .storybook directory. That file should look like this to import the add-ons you want to use within the Storybook UI:

  import '@storybook/addon-actions/register'
  import '@storybook/addon-links/register'

After adding that file the panel appeared but I still wasn't seeing actions appear when I clicked the button in the Button/template + methods story. I figured out through trial and error that changing @click in the button template to :handle-click would give the result I expected. I'm guessing that's because we are testing the component in isolation so the action panel is looking at changes in data and listening to event handlers inside that specific component. I've not confirmed this yet with the community, but I believe it make sense.

Storyshots: Round 1

This is where our journey turns rocky. To install the right packages I had to go a bit outside of what is provided in the docs. Here's the full command to use for all the correct packages: npm install --save-dev @storybook/addon-storyshots jest react-test-renderer jest-vue-preprocessor. I'm still unsure why react-test-renderer is needed. I believe it's a bug but I'm still confirming that. At this point there were some errors and I submitted a couple of issues. While I waited for those answers, instead of going to sleep like a sane person, I decided to mess around with Jest a bit to do some end-to-end testing.

Down the Rabbit Hole We Go

I hadn't used Jest before and wanted to dig in a bit under the hood to see how it worked. I started by creating a test/unit directory to hold the specs directory and some config files. Searching around I found a repo by Brant Willis on GitHub that really helped resolve what the jest.config.js file should look like when working with Nuxt. Turns out this file would also be used by Storyshots later. (So maybe this wasn't a completely off topic decision!)

After sorting out the config I was able to get passing tests! I didn't do anything insane for the tests. There's plenty of tutorials online for creating tests with Jest. I created a simple test for each component, the default layout in Nuxt, and each page in Nuxt. When I tried running the test on Footer.vue I did receive an error. It ended up being due to Footer.vue not having a script area in the file.

Footer component error

Storyshots: Round 2

After figuring out Jest I still didn't have an answer to the submitted issues. In classic 3am logic, I decided to go searching again and I came across a buried file in the Storybook official repo. This is the file needed to tell storyshots where to find the config file, what framework to use with Storybook, and some other peripheral options. I configured it for Vue but I was still running into errors. It was saying that storybook/react wasn't find, even though I set Vue as the framework. After using npm install --save-dev storybook/react all was well. (Yes, very odd.) When trying to run npm test it provided me with an unexpected token error. Something that wasn't documented well was the need to have a .babelrc file. Turns out it wasn't transpiling the code with Babel before running the tests. I ended up with this .babelrc file after seeing this issue in the jest-vue-preprocessor package.

The next half hour from here was a blur. When I snapped out of it I had really messed up the repo and decided to rollback to a commit that I knew was working. Also my wife woke up and told me I was typing too loud and to move to my office. #truLove (Who thought this was a good idea?)

After some caffine I realized that I had added a webpack.config.js file in .storybook that was overriding a lot of settings in the Nuxt config file. I put humpty dumpty back together again and knew I was getting close. After running npm test and trying a few different things I still got an error saying storyshots is intended only to be used with storybook. Turns out Storyshots is available starting in Storybook 3.4.0-alpha.5. To update to that version I had to set the version for storybook-vue, storybook/addon-action, and storybook/addon-storyshots. I also figured out that @storybook/addon is required to get addons working. It appears it's a newer way of getting add-ons to work in an upcoming update.

When I ran npm test again I got the following:

({"Object.<anonymous>":function(module,exports,require,__dirname,__filename,global,jest){<template>
                                                                                    ^
  SyntaxError: Unexpected token <

  at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:318:17)
  at Object.<anonymous> (node_modules/@storybook/vue/dist/client/preview/render.js:24:21)
  at Object.<anonymous> (node_modules/@storybook/vue/dist/client/preview/index.js:32:15)

Thanks to the community at Storybook I found out that there was a workaround to the this bug by adding the following to jest.conf.js

  transformIgnorePatterns: [
    '/node_modules/(?!(@storybook/.*\\.vue$))',
  ],

And TA-DA!!! It works!

As the Sun Began to Rise...

I was really excited to see it working. Turns out my Jest tests were creating snapshots for my specs and Storyshots was creating snapshots of the stories. It also comes with coversage stats which was a nice easter egg. Though I'm not sure why I only get 50% coverage on my Button.vue file. (Anyone know why? Leave a comment!) Along the way I found a great resource for Jest specs with Vue. I'm excited to dig in more with the specs and play around with a few more add-ons for Storybook.

Feeling invincible and extremely worn out, I decided to close the laptop and lay down for some rest. And that's when I heard it...the cry of my 2 year old daughter, the whine of a grouchy 4 year old little boy, and the loud barking of a puppy needing to go outside. Seriously...whose idea was this?!

Matt Shull

About Matt Shull

Data Science Program Manager at Thinkful. Writes a lot about performance, Vue/Nuxt, and IoT. Loves tweeting and retweeting practical development advice.

Recent Features

  • By
    Animated 3D Flipping Menu with CSS

    CSS animations aren't just for basic fades or sliding elements anymore -- CSS animations are capable of much more.  I've showed you how you can create an exploding logo (applied with JavaScript, but all animation is CSS), an animated Photo Stack, a sweet...

  • By
    Designing for Simplicity

    Before we get started, it's worth me spending a brief moment introducing myself to you. My name is Mark (or @integralist if Twitter happens to be your communication tool of choice) and I currently work for BBC News in London England as a principal engineer/tech...

Incredible Demos

  • By
    Parallax Sound Waves Animating on Scroll

    Scrolling animations are fun. They are fun to create and fun to use. If you are tired of bootstrapping you might find playing with scrolling animations as a nice juicy refreshment in your dry front-end development career. Let's have a look how to create animating...

  • By
    CSS Ellipsis Beginning of String

    I was incredibly happy when CSS text-overflow: ellipsis (married with fixed width and overflow: hidden was introduced to the CSS spec and browsers; the feature allowed us to stop trying to marry JavaScript width calculation with string width calculation and truncation.  CSS ellipsis was also very friendly to...

Discussion

  1. Gustojs

    Thanks for the article, as you recommended it, I’m interested in reading Rachel’s and Evan’s thoughts on Nuxt. Where can I find it? Advanced search on Twitter didn’t help with that, so I assume you meant some other platform?

    • Rachel is giving a talk about Vue and Evan is the creator of Vue. What you see about Vue can be grouped in with Nuxt most of the time since Nuxt is the server side rendering version of Vue.

Wrap your code in <pre class="{language}"></pre> tags, link to a GitHub gist, JSFiddle fiddle, or CodePen pen to embed!