StoryPops — Or — The benefits of component isolation with Storybook

Jan Drewniak
Down the Rabbit Hole
7 min readMar 28, 2019
Graphic by Jan Drewniak, https://storypops.netlify.com, CC BY-SA 4.0.

Tldr; The Popups extension (i.e. those page previews that popup on Wikipedia) has adopted the Storybook UI framework to help improve visual regression testing and component development (all without needing React), check out the demo here.

The 8-pointer

My team (Readers Web) at the Wikimedia Foundation discovered a subtle bug in the Popups codebase where the “pokey”, the little triangle that sticks out of the end of the preview, was occasionally misaligned. This bug only occurred on certain previews and we weren’t sure why. We didn’t know what these previews had in common, only that is showed up consistently in some orientations on the preview for Eliud Kipchoge (among others).

The “pokey” circled, missaligned. Text from Eliud Kipchoge, CC BY-SA 3.0. Image by Denis Barthel “Berlin Marathon 2015 Kipchoge, Eliud (KEN)CC BY-SA 4.0.

When we pointed that task, we settled on 8 points (high risk, high complexity) because of the difficulty of debugging this problem.

Debugging these kinds of issues used to consist of the following workflow:

  1. Starting the build process with npm -s start
  2. Opening a browser
  3. Finding a page with the preview (using MobileFrontend’s MobileContentProvider to show production content locally).
  4. Open DevTools, and using the element selector, select the link you want to trigger a preview for.
  5. In the DevTools console, type $($0).trigger(‘mouseover’) to force the preview to appear ($0 is a console shorthand for the currently selected element).
  6. Don’t hover over any other links on the page because that will cause the preview to disappear.
  7. Refresh the page and repeat.
Triggering the preview to appear in the DevTools console. Text from the English Wikipedia’s Main Page and Baltic Sea, CC BY-SA 3.0.

This workflow made debugging tedious and making CSS/HTML changes difficult because whenever you’d accidentally hover your mouse over another link on the page, the selected preview would close. That, and there could only ever be one popup open on the page at once.

The idea

For a feature that looks so simple, previews have a surprising amount of variation. The preview can have be oriented in all directions, but also:

  • Landscape mode with/without a thumbnail.
  • Portrait mode with/without a thumbnail.
  • Thumbnails that are very narrow in landscape mode or very short in portrait mode.
  • Thumbnails that are vector graphics or translucent images.
  • Thumbnails that only appear on low-resolution monitors because high-DPI versions of the image are not available.
  • Thumbnails that are not images but videos or various other media.
  • Compatibility with right-to-left languages. This mode flips all the text and the thumbnails, which essentially adds an extra variant to each existing layout.
  • Accommodating various font-sizes and line heights of different language fonts.
  • Handling super/subscripts, chemical formulas, math, and any other HTML generated by our vast community of editors.

All these variations make it hard to ensure we’re not introducing regressions, and our team had spoken several time about how it would be great if we could see all these variations on a single page at once (but as priorities go, this was pretty low on the list).

Introducing Storybook

While I was experimenting with various style-guide tools, I came across Storybook. Initially, Storybook only supported React components, so it fell off radar pretty quickly. However, as of version 4.0 (released October 2018), the framework added support for a broad range of rendering layers, as well as the ability to render plain HTML (imagine that!).

The HTML rendering feature is what piqued my interest. This allows Storybook to be used by any project that outputs DOM (or even strings that represent DOM) and as we’ve written about before, the Popups extension uses ES6 string literals as its rendering layer. This, combined with ES6 modules, allowed us to seamlessly integrate the two. Well, almost seamlessly

…OK, there are seams

Although Popups is mostly self-contained, it does depend on some features built into the MediaWiki core javascript library (which is not available as an NPM module since it’s meant to go hand-in-hand with MediaWiki). These internal MediaWiki helper functions had to be stubbed in basically the same way you’d stub dependencies in a unit testing environment.

The much more challenging dependency was the LESS (CSS preprocessor) styles. In order to achieve visual consistency, Popups uses LESS variables from MediaWiki’s core library. Unlike the JS, these dependencies couldn’t be stubbed because they affect the visual appearance of Popups. They had to be pulled in from MediaWiki Core (in a very roundabout way): Webpack had to be configured to resolve LESS imports to a specific folder, which just contained files that had imports pointing to MediaWiki core.

Also, there were some incompatibilities with Storybook and the version of Node (v6) we run in our CI pipeline, so the Storybook app had to be setup as a separate NPM project inside the Popups repository itself.

But finally after all that rigmarole, we were finally ready to Popup & roll.

The Storybook pops up

After getting the initial setup working and adding a few helpers to make creating page previews easier, the vision started to materialize.

The “non-latin” page-preview stories in Storybook. Text and images from various language Wikipedia articles, text CC BY-SA 3.0.

We were able to create test-cases, or as Storybook calls them, “stories”, for all the permutations mentioned above (with/without thumbnail, RTL, SVG, etc). This was in large part possible thanks to the architecture of the Popups extension, which clearly separated the rendering layer so that it could be exported and used independently of the state management and other business logic. The fact that the Popups codebase uses ES6 Modules along with Webpack (which Storybook also uses), helped integrate the two as well.

However, Storybook doesn’t require you to use ES6 modules or Webpack. All it requires is a rendering layer that can be potentially imported by Webpack, and since Webpack has a large ecosystem of “loaders”, almost any DOM rendering layer or templating library can be funnelled into Storybook. This includes plain JavaScript files that output DOM or an HTML string, or even Mustache templates, which can be converted into strings using the Webpack mustache-loader and then rendered using the Storybook for HTML module.

Oooo shiny

With Storybook, any changes we make to the Popups rendering layer (or even to LESS variables in MediaWiki Core) are immediately visible in the Storybook app (kids these days call this “hot-module reloading”) without having to reload a page or trigger a preview manually.

Another neat trick Storybook gives us is the Knobs addon, which allows modifying the HTML output from a friendly UI.

Using the Knobs add-on to change page previews in Storybook. Images “Thatcher Peninsula” public domain, “Eberndorf Köcking Sonnenblumenfeld Biohof Tomic” by Johann Jaritz CC BY-SA 4.0.

The CSSResources add-on also comes in handy for applying my secret CSS weapon to any page quickly. That weapon being * { outline: 1px solid red;} I call this “x-ray mode”.

Inspecting the layout with the CSSResources add-on. Image “Flag of South Georgia and the South Sandwich Islands,” public domain.

Learnings

At first I thought Storybook would be a good tool for presenting components, but as I dug into it, I realized Storybook is a UI component workbench, providing an environment not just for looking at components, but for actually building them. In this sense, Storybook expresses the idea of component-driven development, which encourages thinking of components as self-contained entities. The benefit of this approach is that components can be swapped out or updated independently of each other, which is often difficult with long inheritance chains or CSS that touches many different parts of the code (as the joke goes).

More so, when you begin treating components as self-contained entities, you realize that they can be treated as “units” of code. So even though Storybook might look like “just” an iframe to stick components into, that alone provides something that we’re only beginning to explore in UI development, which is the idea of a unit-testing environment for components. An environment which not only lets you test the correct behaviour of a component, but its visual correctness as well.

So even if you’re not using the latest front-end frameworks, I recommend giving Storybook a spin, it might shed some light on those hard to reproduce edge-cases and help you avoid visual regressions.

In the meantime, you can check out a demo of the Popups Storybook here! or clone the repository and build it yourself from Gerrit or Github.

And yes, after some investigation with the Storybook app, we were able to track down and solve that 8-pointer.

--

--