Skip to content
πŸ’°πŸ“˜Application component library for United Income.
JavaScript CSS Other
Branch: develop
Clone or download
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.circleci Increase resource class for build job Aug 20, 2019
.github Version Bump (#225) Apr 9, 2019
.storybook Bump sass-loader from 7.3.1 to 8.0.0 (#689) Oct 10, 2019
_templates/component/new [APP-2981] README Changes (#456) Jun 12, 2019
assets/icons
components
constants [APP-3973] Create data table component (#768) Oct 10, 2019
nightwatch IE11 smoke test May 8, 2019
proptypes Update file naming and move unit tests to be along side the component (… Mar 12, 2019
utils-for-tests Update branch.js Jun 14, 2019
.babelrc
.browserslistrc Reduce output bundle size (#668) Aug 19, 2019
.codecov.yml Rework build to fail if any one step fails (#310) May 3, 2019
.eslintrc.json Removes no-undefined (#706) Sep 11, 2019
.gitignore Update master (#381) May 28, 2019
.npmignore Ignore files when publishing to NPM May 8, 2019
.nvmrc Use Node v10.16 Jun 10, 2019
.nycrc Update file naming and move unit tests to be along side the component (… Mar 12, 2019
.prettierrc.json Initial Linting Setup Jan 28, 2019
.sass-lint.yml Sass auto prefix fix Jun 3, 2019
LICENSE
README.md [APP-3225] Font Sizing Helper Classes (#664) Aug 19, 2019
package.json [APP-3973] Create data table component (#768) Oct 10, 2019
publishStorybook.sh Moving to Circle Jun 3, 2019
webpack.config.js Bump sass-loader from 7.3.1 to 8.0.0 (#689) Oct 10, 2019
yarn.lock Bump postcss-prefixer from 2.1.1 to 2.1.2 (#781) Oct 17, 2019

README.md

United Income logo

Storybook CircleCI codecov This project is using Percy.io 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.md
β”‚ β”œβ”€β”€ 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 existence 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.
  9. Do not remove the browser default outline focus state unless you're replacing it with something else.

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 './TrashIcon.md';

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

stories.addDecorator(withReadme(TrashIconReadme));

// 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.
stories.addDecorator(StateDecorator(store));

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

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', () => (
  <RadioButtons
    {...defaultProps({
      formName: 'yesNo',
      options: [
        {
          label: 'Yes',
          value: 'yes',
        },
        {
          label: 'No',
          value: 'no',
        },
        {
          label: "I don't know",
          value: 'idk',
          disabled: true,
        },
      ],
    })}
  />
));

Accessibility πŸ’¬

Components are linted for accessibility using the eslint-plugin-jsx-a11y and validated using storybook-addon-a11y.

Additionally included in the component library preview is a polyfill for the :focus-visible CSS pseudo selector, which turns off the outline CSS property off for users who are not using a keyboard to navigate. You can include the polyfill in your own project by including the script file and the required CSS on the page.

<!-- Turns off the outline for those not using a keyboard -->
<style>
  .js-focus-visible :focus:not(.focus-visible) {
    outline: none;
  }
</style>

<script src="https://cdn.jsdelivr.net/npm/focus-visible@5.0.2/dist/focus-visible.min.js"></script>

You can learn more about the :focus-visible polyfill here.


rem Sizing

This component library uses CSS rem sizing for spacing and font sizes. In order to get the intended effect your application should use the following sizing base.

html {
  font-size: 10px;
}

@media (max-width: 991px) {
  html {
    font-size: 8px;
  }
}

Additional Resources πŸ•

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

You can’t perform that action at this time.