Skip to content
πŸ’°πŸ“˜Application component library for United Income.
Branch: master
Clone or download
Pull request Compare This branch is 6 commits ahead, 1 commit behind develop.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.

United Income logo

Storybook CircleCI codecov This project is using for visual regression testing.

This library aims to standardize the implementation and appearance of React components throughout United Income's properties. This library has a series of exportable components that can be reused. It also utilizes Storybook to create a pseudo-design system manager that is based on the principals of Atomic Design.

Getting Started πŸš€

This component library can be installed and initialized by running yarn installfollowed by yarn start. This will start a local instance of Storybook, allowing you to view a series of stories that represent each component.

Using a Component πŸ“š

You can install the component library into your project by running the following command.

yarn add @unitedincome/components

We utilize peer dependencies as we often require packages that already exist in our other applications. Because of this you will also need to install the required peer dependencies into your project. You can learn more about peer dependencies here.

You can then decide if you want to use this library by importing components individually or by importing them all together which will result in a larger bundle size.

For individual importing, you can pull in separate files from the dist folder.

import Button from '@unitedincome/components/dist/Button';

To import all components together, you can import more simply:

import {Button, CardShell, Slider} from '@unitedincome/components';

You will also need to import the relevant CSS files. You have the choice between importing them individually into your style manifest or in bulk. For both methods you'll need to also include the util.css file which includes a prefixed grid layout and utility classes from Bootstrap.

For individual imports you can use the following:

@import '@unitedincome/components/dist/util.css';
@import '@unitedincome/components/dist/Button.css';

To import all styles you can use:

@import '@unitedincome/components/dist/util.css';
@import '@unitedincome/components/dist/index.css';

Building a Component πŸ”©

Running yarn generate will create the folder and files you need to start building out your component. Each component at the very least should have an export, documentation, testing, and a story file. The general structure should look something like the following.

└── components
β”œβ”€β”€ atoms
β”‚ └── Input
β”‚ β”œβ”€β”€ Input.js
β”‚ β”œβ”€β”€
β”‚ β”œβ”€β”€ Input.spec.js
β”‚ └── Input.story.js
└── molecules

To make utilizing other components within your component easier, the library includes a number of directory aliases which allow for easier importing, you can utilize ~components, ~constants and ~proptypes to access the root directories.

Testing Changes πŸ’Š

As you are developing new components or updating existing ones, testing these components in the context of an existing front-end repository can be useful. Instead of going through the life cycle of publishing new versions, the easier way of handling this is utilizing package linking.

You can mimic publishing this repository locally by running yarn link in the directory for this library. To use it in another library you can mimic installing it by running yarn link @unitedincome/components.

At this point, whenever you make changes to the component library and run yarn build, the code running in the other repository will automatically change.

Best Practices 🏁

While there are always going to be special cases, the following guidelines should be considered when contributing to the library.

  1. Do not include external margins or padding on the individual components by default.
  2. Create strong PropTypes for all props on all components, even if that necessitates custom PropTypes.
  3. Comment each PropType, this will allow for React doc gen to properly document what each one does.
  4. Components should have 100% test coverage.
  5. Keep external dependencies to an absolute minimum and, when used, most likely add them as peer dependencies in the package.json and as external dependencies in webpack.config.js.
  6. While components can utilize internal state, do not make them reliant on a global state (i.e., redux).
  7. The Bootstrap 4 grid and utility classes are included, and prefixed with uic--, these should be used as much as possible.
  8. Check for the existance of global variables such as those exposed by window in a browser before accessing them. We use this library in non-browser environments, such as react-static which requires code to be node-safe.

Creating a .story.js file πŸ“’

The .story.js file should represent a staged version of your component which will display in the Storybook interface. Each story is snapshot tested with a visual regression testing tool called Percy, so capturing different visual variations of your component in a story is preferred.

This component library utilizes a number of Storybook addons, such as knobs and storybook-readme to allow users to play around with the relevant PropTypes, and to see inline documentation. To simplify your story creation, adding a defaultProps helper function that configures the component props is preferred.

You can find a basic example of a Story below.

import React from 'react';
import {storiesOf} from '@storybook/react';
import TrashIcon from './TrashIcon';
import {text} from '@storybook/addon-knobs';
import {withReadme} from 'storybook-readme';
import TrashIconReadme from './';

const stories = storiesOf('Atoms/Icons/TrashIcon', module);


// Sets up the default props for multiple different story variations.
const defaultProps = () => ({
  fill: text('fill', '#000'),
  height: text('height', '2rem'),
  width: text('width', '2rem'),

stories.add('default', () => <TrashIcon {...defaultProps()} />);

State Wrapper πŸ—½

If your component is controlled by state, you'll need to add a state wrapper to your story. For this you can use the storybook-state addon. You can find more information about the state wrapper we utilize here.

import React from 'react';
import {storiesOf, forceReRender} from '@storybook/react';
import {StateDecorator, Store} from '@sambego/storybook-state';

const stories = storiesOf('Molecules/RadioButtons', module);

// Default state is stored in a Store object.
const store = new Store({
  yesNo: '',
  followup: 'custom',
  input: '',
  bank: '',

// You'll need to add the StateDecorator as a Storybook decorator using addDecorator.

// Subscribes to store changes and forces the component to re-render.
store.subscribe(() => {

const defaultProps = ({formName, table}) => ({
  name: formName,
  table: boolean('table', table),
  onChange: (name, value) => store.set({[name]: value}),
  value: store.get(formName),
  key: formName,

// You can set/get state using state.set() and state.get().
stories.add('default', () => (
      formName: 'yesNo',
      options: [
          label: 'Yes',
          value: 'yes',
          label: 'No',
          value: 'no',
          label: "I don't know",
          value: 'idk',
          disabled: true,

Additional Resources πŸ•

When learning about this library, the following resources may come in handy.

You can’t perform that action at this time.