Lessons from DS for a dev - cover.png
Olga Pinheiro

Olga Pinheiro

25 Jul 2022 7 min read

Two lessons learned from our Design System from the POV of a dev

Here at Labcodes, we’re finding out the ups and downs of developing our own Design System. In a previous post, we shared lessons learned from the POV of the design team. In that post, you’ll also find an overview of what is a Design System and how we chose the tools we use (FYI Figma and Storybook with ReactJS). In today’s post, we’re gonna share some things we learned focusing on the POV of the development team.

I was the one responsible for implementing a beautiful text input. For priority reasons, this was one of the first components we built. The thing is… it’s also one of the most complex ones, because it is highly customizable, accepts a huge variety of options (I’m gonna show you the nice list of props in a few), and because we decided to deal with the data validation.

Screencast animation showing the default behavior of the text input component Our baby, the TextInput component, in its default form, with a customizable help message

Starting with such a complex component resulted in a PR being open for months, an absurd number of commits, and likely 10 moments of “I think I finished” followed by “Oh no, another bug”. On the other hand, it was a big source of learning, about coding as much as about processes. More than that, it helped fix important knowledge that used to seem irrelevant. The most remarkable thing to me was to notice how much the product became more robust when we started taking the playground and the JS tests more seriously.

Screenshot of a 15 lines table with the component props, their types and default valuesThis is just to give you an idea of the amount of customization allowed by this component. I’m gonna show it in a better way later, but if I do it now it’s a spoiler

Playground isn’t just for the user

There’s a huge difference between developing ReactJS components for a web application and developing for a Design System. In the first case, the components are used in specific known contexts; the user is the person who accesses the application. In a Design System, however, the context where the component will be applied is unknown, and the user is the developer person who uses the component in their code.

As our users are developers, it is quite important to offer good documentation, visible to the user not only inside the code, so we looked for a tool that could help on this and decided to use Storybook. The Storybook, according to its own website, is a developer, documentation, and visual testing environment for UI components.

At the beginning of our development process, the documentation of each component displayed some interactive examples of use cases, showing how would be the interaction with the final user in very specific situations, among with some guidance about its behavior and applications.

Screenshot of different options for the text input: disabled and not disabled with suffix Screenshot of different options for the text input: with suffix and validation error, no suffix and required with help message, required with validation errorIn the beginning, the documentation only showed examples like these, where it’s possible to interact with the element but it isn’t possible to change the props it receives

Everything seemed to be OK. I thought that the component was ready and was about to start writing the JS tests. But then I asked myself: “what if I want the Input to have a ‘suffix’ and to be ‘required’ at the same time? Will it work fine?”. I testes temporarily at any place in the code, and…

Screenshot of text input with suffix ".com" and required (with a small star at the top right corner). Part of the star is hidden by the suffix light blue backgroundOPS! When setting “suffix” and “required” at the same time, the “required” symbol would get hidden behind the “suffix”’s background. That’s definitely NOT the desirable behavior

At that moment, I understood that the Storybook functionalities that we were using were not enough for this scenario. It became evident to me that a demo space that allowed editing the props values — simulating the component usage by a developer — was important not only for the user’s understanding and exploring the component but also as a tool for visual validation during the development of the design system itself.

The Storybook offers a tool for that, but its usage was not so intuitive for us, and neither was integrating it into our visual identity. It was then that I added a customized “playground” section to the component’s documentation, which is common in other Design System documentation, such as Carbon.

Very fast, what seemed to be ready had already shown to be full of bugs:

After identifying and solving these issues, it was possible to deliver a much more robust component, visually speaking. If the playground had been implemented earlier, we would probably have had fewer surprises and the development would have been faster since it brings us scenarios closer to the real use of the component. But better later than never.

That done, now I could head to the creation of automated tests that would assure the correct behavior of the component in a real case.

It should be obvious, but JS tests are not just about snapshots

Again, we must remember that our development and testing context is quite particular of a Design System. As we are developing a library of isolated components, we can test them without worrying about the part of the application where they are going to be used. For example, it doesn’t matter for the TextInput component if it’s using values from Redux or from the parent component. All we need to ensure is that the components get and return the necessary information to allow a developer to implement the data flows they desire in its specific context.

In the case of the Input, my biggest concerns were about how the component would behave depending on the developer’s choice of passing or not passing the value by props, as well as the correct utilization of defaultValue and the validation of the content inserted by the final user, depending on the type of the input chosen and on the possibility of the developer handling external validation rules. As our components were written using classes, this kind of testing could be easily done using Jest and Enzyme — Jest is a JS testing framework, while Enzyme offers testing tools for React. These tests also help guarantee that errors introduced by future changes in the component can be noticed fast.

Above all, because it’s a library, we understand that it is important covering a significant part of the code with unit tests. This makes us more confident that the code works, assures better product stability, and helps us make decisions that lead to the development of a code with good quality.

An interesting scenario to be tested is, for example, the case where the input is disabled:

it("renders as expected when passing disabled as true", async () => {
  const renderedComponent = mount(
    <AbstractTextInput
      id="testInput"
      label="Test Input"
      defaultValue="default"
      disabled
    />
  );
  expect(renderedComponent.find("input[disabled]")).toHaveLength(1);
});

This kind of test was important for us for two reasons. First, it guarantees that the component is able to be rendered correctly in a common case of final use. Second, we’re not testing the component “from the inside”; I mean, we are not validating that internal state keys are behaving in a specified way. On the opposite, we’re testing the component “from the outside”, guaranteeing that future refactoring of the component won’t break the tests unnecessarily.

This is not everything…

These were my two biggest learnings when implementing a complex design system component — but they were not the only ones. It was also great to find out more about how to unpack the components, or how to use the Storybook in the most effective way, and even how to document decisions that are not necessarily related to the code itself, improving the process.

However, I’ll leave those subjects to a future post, maybe more focused on one of them. What about you, what did you think of our tips? Wanna know more about some specific part of our process? Leave us a comment and let’s chat. Thank you for your attention, and see you soon!

If you want to read more about related topics, these were some of my references: