Designing React components is easier with Cosmos
All of the benefits of React – when combined with a single-page web app – can, however, lead to complications. Specifically, in the process of building a new component, you might feel the need to get visual feedback on it, for either styling or debugging matters. For example, previewing a button. Nonetheless, building and starting your entire application just for previewing that button is a slow and unnecessary process. This becomes more and more complicated if you’re using many components that have intricate relationships between them.
Read on to find out how we managed to solve this kind of problem and also how we improved the overall design experience for React developers.
Meet React Cosmos
In Hootsuite’s Bucharest office our front-end developers use a great open source tool for designing React components called React Cosmos. This DX (developer experience) tool provides your project with a powerful environment, in which you can easily:
- Render any of your project’s components under any combination of props and state – so you can easily see the output for a given input
- Observe how it reacts to prop changes in real-time, in a 2-way manner:
- Edit the props/state and see the component update
- Interact with the component and see its props/state change
When I stumbled upon the project, it looked like this:
ImprovementsI loved using Cosmos from the start but our front-end developers and I felt that it needed some new usability features. So we all gathered together and came up with a list of improvements that would make Cosmos more delightful to use. I implemented these during my co-op term here. Here’s the list of improvements, all of which are now live in the core product, ready to use:
Scroll to selected fixture on reloadProblem
If your project has a huge component list, and you’re editing a fixture, then need a refresh, it’s tedious to scroll back to the fixture you were previewing.
Automatically scroll to the selected component on page load, so you can quickly see the component and fixture you are working on.
You can notice how we’re searching for the selected fixture’s ref and using scrollIntoView on its DOM node to make it just visible.
Make editor panel resizableProblem
Aside from the left navigation panel, the editor was occupying half of the available space, with the preview taking up the other half. As these were fixed sizes, you might sometimes feel restrained by this layout. For example, you might need to render your entire app, which is big, but has no props at all. In this situation, the preview panel could use more breathing space.
For this I used the React split-pane package, that features a component which is able to render two children side by side with a divider bar between them. Dragging this bar allows resizing the two components, improving space management. It does this by wrapping its children in Pane elements, which listen to the divider bar changes and modify their size accordingly.
Filter fixture and component namesProblem
Most of the time when booting up Cosmos, you have a specific fixture in mind, so scrolling through the fixture list is unnecessary. Also, you might want to filter components or fixtures by some criteria, e.g. see all the buttons.
A search bar was one of the most straightforward solutions to this problem. I added a minimal input field to the left panel, and only showed components and fixtures that matched the search. That was cool, but why not go one step further? Make the search algorithm fuzzy, and match against both fixture and component names.
This algorithm is implemented in the fuzzaldrin-plus package, also used by the Atom editor. It has several improvements over a classic matching algorithm, such as better acronym detection. You can find out more on the npm page.
Enhance editor capabilitiesProblem
The editor is good for updating component props and state in real time, but it didn’t have a great look, and you could easily get lost in it. Imagine a fixture containing an array of 1000 elements. Now that’s some scrolling!
To enhance both its look and functionality, I had to replace the basic text area with a more powerful tool. I ended up using Codemirror, which features, among others:
- Syntax highlighting: Not that important for JSON, but it makes keys stand out from the values. It has numerous themes available, for different keyword colors.
- Code folding: This is the where Codemirror shines for my use case, as it allows folding of both curly (objects) and square (arrays) brackets. Those are automatically detected, so you can instantly press the arrows to collapse/expand a certain part of the code.
- Line numbers: Not the most important feature either, but it helps to not get lost in the noise.
- Smart indentation: This is useful both when manually writing code, as it indents the cursor when beginning a new line, and when pasting code, as it “prettifies” it.
Supporting ReduxAnother framework that’s rapidly gaining popularity is Redux. It helps centralize the state of your application in a single store. You can read more about it here. This was a problem for Cosmos, since it couldn’t inject component state from the fixture.
What I did to solve this was to wrap the component in a “ReduxProxy”, a component which simply makes a fake Redux store available to its children via context, a cool React feature. Thus the rendered component could locate the store it also needed in its original environment.
Here’s how this “ReduxProxy” code looks:
You can see how the proxy creates a Redux store on initialization, then attaches that store on the context to make it available to the rendered component.Notice how we need to call createElement on its child, so that the proxy becomes the component’s owner. That way, context works correctly in older versions of React, too. More on owner-based vs parent-based context here.
What’s NextCosmos will continue to evolve, with me being an active part of its development With the introduction of the Redux proxy, we are working on making the proxy infrastructure a universal add-on system, where you can modify the rendered component at will. This opens up a whole new world of possibilities:
- Alter component props
- Render another component beside the original one
- Make the component resizable or draggable
- And many more…