When we write HTML, we try to ensure that each fragment serves a specific purpose. Despite this, sometimes those fragments can end up being quite complex. They can have many levels of nested children. They might have a variety of content and states that interact in ways that are hard to predict.

Danielle Huntrods
Danielle Huntrods
one week ago

Imagine, if you will, a typical navigation menu on a typical website, with submenus when we hover over a link. We might at first try to build it as a single isolated component. If we use a pattern library, this is easy to do. But how we choose to structure our component within the pattern library has a big impact on how easy it will be to test and update it.

A navigation menu in Enabled, Current Item, Selected Item, and Js-Disabled states. We can simplify testing by assembling it from smaller components, and creating separate variants for each state.

Building it as single component has the advantage of being quick - as long as you know the exact content. But if you’re using a CMS, your menu can contain an arbitrary number of links. The length of a given line of text can vary across a huge range. And of course, the component relies on Javascript for some of its states. Your component might work well for the initial set of content you’ve defined but in many other instances it will most likely misbehave. Testing each combination of state and content is a manual process, prone to error. We must add and remove links; hover over each item in turn; disable and re-enable javascript on the page. Finally we run accessibility checks after each change.

This state of affairs doesn’t seem ideal. What happens if we break down our components into their smallest constituent parts? If we structure them all with modularity in mind? If we set up our component preview files to account for missing resources? Our components will gain a super power: Ultra Testability!

Some of the parts that make up the navigation menu. I created and tested individual links, headers, sections, and list items.

We build from the smallest units up. We can now check each part with its own inherent states in isolation. We can immediately see where regressions, combinatory errors, or edge cases crop up. We make no assumptions about where these units fit within the larger structure. As a result, we can use these smaller pieces elsewhere without modification. Once we’ve created all the parts, assembling the whole is as simple as putting them together. Then we need test only the states of the larger assembly. The result is robust, flexible, and easy to change. Congratulations, you have created a Super Component!

Thunder Megazord, fully assembled

Tiny Lessons

I’ve found it useful to create content generators for creating specific text when you want it, or random text if not. You can set ranges of likely text or item lengths to keep things reasonable. The node library faker.js is helpful for this.