npm package: @nxtdtc/dtc-component-library
Description: A shared component library for common components shared across micro-frontends. These components have been created in conjunction with the Design team, using our universal design system. This enables us to create, update, and maintain storefront component
Latest version: 4.6.1

## DTC Component Library

A shared component library for common components shared across micro-frontends. These components have been created in conjunction with the Design team, using our universal design system. This enables us to create, update, and maintain storefront components in a centralized place. Component styles specific to a particular brand are primarily configured via that brand's design system and translated into a config file.

### Table of Contents

[Getting Started](#getting-started)<br/>
[Brand Style Config File](#style-config)<br/>
[Styling Components with Styled Components](#styled-components)<br/>
[Storybook](#storybook)<br/>
[Landing Page Components](#lp-components)<br/>
[Development Process](#dev-process)

- [Coding Standards](#coding-standards)
- [Useful Commands](#useful-commands)
- [To Link With an Existing Project](#npm-link)
  <br/>

[QA/Integration Process](#qa-integration)

- [New Component](#new-components)
- [Existing Components](#existing-components)
  <br/>

[Versioning/Publishing](#version-publish)<br/>
[Useful Libraries](#useful-libraries)<br/>

<!----><a name="getting-started"></a>

### Getting Started

---

`npm install` will install all necessary packages - be sure you are using Node >= 10

`npm start` will start webpack and evaluate changes in real time. This can be useful when linking with another app to test a new component or change.

`npm run storybook` will open a tab with storybook running on port 6006. Storybook is especially useful for building components in isolation and testing different permutations of props.

<!----><a name="style-config"></a>

### Brand Style Config File

---

Each brand has it's own config file, which lives in the `config` directory, and the shape of this config file should be uniform across all brands. This enables the components to transform their style based on the config file defined via the `ThemeProvider` component that should wrap the entire application that wishes to use these components.The config file selected depends on the value passed into the `ThemeProvider` (see example below).

```jsx
import { ThemeProvider } from '@nxtdtc/dtc-component-library';

const Main = () => {
  return (
    <ThemeProvider theme="bri">
      <App />
    </ThemeProvider>
  );
};
```

The config file is composed of one large object that houses properties that define specific styles for that component. Each property within the larger object should correspond to the name of the component that it serves (see example below). All brands should have an identical config file layout so that components can access values in a uniform way.

```js
import { colorPalette } from './colors';
const { ...colors } = colorPalette;
import { fonts, backupFontStack } from './fonts';

const config = {
  Button: {
    sizes: {
      default: {
        fontSize: '18px',
        padding: '11px 32px'
      }
    },
    primary: {
      backgroundColor: colors.CTA_BLUE,
      color: colors.WHITE,
      borderRadius: '8px',
      fontFamily: `${fonts.bodyMed}, ${backupFontStack.sans}`,
      fontWeight: 500,
      border: `2px solid ${colors.PRIMARY}`,
      lineHeight: '23.4px',
      letterSpacing: '0px',
      disabledBackgroundColor: colors.DARK_GRAY,
      disabledBorder: `2px solid ${colors.DARK_GRAY}`,
      cursor: 'pointer',
      hoverBackgroundColor: colors.CTA_BLUE_HOVER
    }
  },
  Link: {
    small: {
      fontFamily: `${fonts.bodyMed}, ${backupFontStack.sans}`,
      fontWeight: 500,
      fontSize: '12px',
      lineHeight: '18px',
      textDecoration: 'underline',
      color: colors.SECONDARY,
      hoverTextColor: colors.CTA_BLUE_HOVER
    },
    large: {
      fontFamily: `${fonts.bodyMed}, ${backupFontStack.sans}`,
      fontWeight: 500,
      fontSize: '16px',
      lineHeight: '24px',
      textDecoration: 'underline',
      color: colors.SECONDARY,
      hoverTextColor: colors.CTA_BLUE_HOVER
    }
  }
};
```

This object is consumable in each component via the `withTheme` HOC. This HOC grabs the config file provided by the `ThemeProvider` context and maps the name of the component to the corresponding property in the config file object. Any component that uses this HOC will have the `themeProps` prop accessible to them, which will equate to the object contained in the config file that corresponds to that specific component. One can then iterate through the `themeProps` object to extract the values needed for that component (see example below).

```jsx
import React from 'react';
import withTheme from '../withTheme';

const Button = ({ themeProps }) => (
  <button
    style={{ backgroundColor: themeProps?.primary?.backgroundColor }}
  ></button>
);

export default withTheme(Button);
```

<!----><a name="styled-components"></a>

### Styling Components with Styled Components

---

`styled-components` is used to style our components. This allows each component to have their own style file (`Component.style.js`) within the component's directory. The `themeProps` prop is typically passed into the component created by `styled-components` so that those style attributes can be applied to that component (see example below). Learn more about `styled-components` here: https://styled-components.com/

`Button.js`

```jsx
import React from 'react';
import withTheme from '../withTheme';
import { StyledButton, TextContainer } from './Button.style';

const Button = ({ themeProps, disabled, variant }) => (
  <StyledButton
    disabled={disabled}
    variant={variant}
    themeProps={themeProps}
  ></StyledButton>
);

export default withTheme(Button);
```

`Button.style.js`

```jsx
import styled from 'styled-components';

export const StyledButton = styled.button`
  background: ${({ disabled, themeProps, variant }) => {
    if (variant !== 'primary' && disabled) {
      return themeProps?.[variant]?.backgroundColor;
    }
    if (disabled) {
      return themeProps?.[variant]?.disabledBackgroundColor;
    }
    return themeProps?.[variant]?.backgroundColor;
  }};
`;
```

<!----><a name="storybook"></a>

### Storybook

---

Storybook allows us to develop and document the functionality of our components in isolation while also providing an interface in which developers and members from design or product can interact with these components. Each component should have a corresponding `Component.stories.js` file in it's directory. You can run storybook locally by running `npm run storybook` in the terminal.

#### Storybook Environments:

**Stage:** http://stage-clx-dtc-comp-lib.s3-website.us-east-2.amazonaws.com

**Prod:** http://clx-dtc-comp-lib.s3-website.us-east-2.amazonaws.com

#### Controls

Configurations for each component's Storybook stories take place in the component's directory under the file `Component.stories.js`. `controls` are Storybook's way to make the component's props interactive and dynamic, so that the person interacting with the component can see the different variations of this component. If a variation can not be made accessible through the component's `controls`, then an additional story for that component should be created to showcase that variation. You can read more about controls here: https://storybook.js.org/docs/react/essentials/controls

#### Stories

A story captures the rendered state of a UI component. It's a function that returns a component's state given a set of arguments. Stories are generated by creating an instance of the component with pre-populated `args` or props (see the example below). You can read more about stories here: https://storybook.js.org/docs/react/writing-stories/introduction

```jsx
import React from 'react';

import { Button } from './Button';

export default {
  title: 'Button',
  component: Button
};

export const Main = (args) => <Button {...args}>Button</Button>;

Main.args = {
  variant: 'primary',
  size: 'large',
  noHover: true
};
```

<!----><a name="dev-process"></a>

### Development Process

---

Create a new feature branch from the master branch. Follow the coding standards defined below:

<!----><a name="coding-standards"></a>

#### Coding Standards

---

Each component will have it's own directory. Each directory should contain the following files:

- index.js (where the component is exported)
- Component.js (where the component is created)
- Component.style.js (where the component is styled)
- Component.spec.js (where unit tests for the component are written)
- Component.stories.js (where Storybook stories are created)
- README.md (documentation for the component)

##### `index.js`

As an optimization tehcnique, we use the `@loadable/component` library to split each component into it's own chunk when webpack bundles and serves the code to the storefront. Each component should be "chunked", then exported (see example below)

```jsx
import React from 'react';
import loadable from '@loadable/component';

const ComponentTemp = loadable(() =>
  import(/* webpackChunkName: "ComponentChunk" */ './Component')
);

const Component = (props) => <ComponentTemp {...props} />;

export { Component };
```

##### `Component.js`

This file is where the component is created. Most components will use the `withTheme` HOC. This file should only contain logic specific to that component. Anything having to do with styling the component should remain in the corresponding `Component.style.js` file. All components' prop types should be verified via the `prop-types` library. All required and default props should be defined with this library as well. Flexibility to override styles when needed is necessary with most components. Please ensure that there is a `style` prop that can override key elements in the component.

##### `Component.style.js`

This file is where styling via `styled-components` should take place. All components created here should be exported and consumed in the `Component.js` file.

##### `Component.spec.js`

All components are required to have unit tests and should meet the following standards:

- Each component needs a test and our pipeline will fail if any test fails or if test coverage is not met
- Tests should replicate behavior in consumers
- Tests should cover edge cases and non-happy paths (how could this break?)
- Developers need to flag components that need manual QA and which sites should be tested
- npm-link to consumer to make sure a component is working as expected

If the component uses the `useMediaQuery` hook, the `mockMatchMedia` function should be imported and executed before each test suite is run (see example below).

```jsx
import Component from './Component';
import { render, mockMatchMedia } from '../../../testHelpers';

describe('Component', () => {
  beforeEach(() => {
    mockMatchMedia();
  });

  it('renders in the DOM correctly', async () => {
    const { getByTestId } = render(<Component text="hello" />);

    const myComponent = await getByTestId('my-component');
    expect(myComponent).toBeTruthy();
  });
});
```

##### `README.md`

All components are required to have a corresponding README, which documents what the component does, all of it's props (along with what the prop is used for and it's data type), the default props the component may have, an example of how the component is used, and any other additional information that may be useful for devs and members of other teams. The README will be brought into Storybook as a story (see example below).

##### `Component.stories.js`

All components will have controls and stories configured in this file. Please refer to the section above in this doc about Storybook for more information.

<!----><a name="useful-commands"></a>

#### Useful Commands

`npm install` will install all necessary packages - be sure you are using Node >= 10

`npm start` will start webpack and evaluate changes in real time. This can be useful when linking with another app to test a new component or change.

`npm run storybook` will open a tab with storybook running on port 6006. Storybook is especially useful for building components in isolation and testing different permutations of props.

`npm test` will run the test suite

`npm run coverage` will run the test suite and output a coverage file at `/coverage/lcov-report/index.html`

`npm run generate lib-component` will take you through a series of questions that will result in a new directory being created for a new component containing all the necessary files for that component.

---

<!----><a name="npm-link"></a>

#### To Link With an Existing Project

You will want to test your changes as they would appear in the front end using this library. You can achieve this using `npm link`

In the component library terminal - `npm link`

In the front end application terminal - `npm link @nxtdtc/dtc-component-library`

If you run into issues it's best to nuke all node_modules in your front end app, reinstall and then re-link the library.

<!----><a name="qa-integration"></a>

### QA / Integration Process

---

<!----><a name="new-components"></a>

#### New Components

If a new component has been created, please go through following steps for QA:

- `npm link` your component into the storefront throughout development to ensure it is working as expected in the storefront
- Submit a PR to stage and assign the PR to the dev assigned to review the corresponding ticket.
- Once the dev has approved the PR, merge the branch into the stage environment.
- Verify that your branch has been merged and deployed to the stage environment.
- Notify members of the design team in the `#designsystems` Slack channel that this component is ready for design QA - provide a link to the component story on stage. Assign the Jira ticket to the member of the design team that wishes to review the work.
- Once your work is approved by design, merge your branch into the master branch. Ensure that it has been successfully and merged and deployed.
- Publish a new version of the component library in the master branch (view versioning instructions below).
- Create a new ticket in Jira to integrate this new library version and to replace all necessary components in the storefront with the component library component.
- Move this new ticket to the top of the backlogs in Jira. It will be assigned to a dev in the upcoming sprint.

In the ticket created for the consumer:

- Update the package.json of the appropriate consumer
- Include references to the new component
- Update/create any integration tests to ensure component is working as expected in the consumer
- Once the code is approved by a developer, the component is ready for manual QA in the consumer

<!----><a name="existing-components"></a>

#### Update Existing Components

If you are updating an existing component, please go through the following steps for QA:

- `npm link` your component into the storefront throughout development to ensure it is working as expected in the storefront
- Submit a PR to stage and assign the PR to the dev assigned to review the corresponding ticket.
- Once the dev has approved the PR, merge the branch into the stage environment.
- Verify that your branch has been merged and deployed to the stage environment and that it is working as expected.
- Merge your branch into the master branch. Ensure that it has been successfully and merged and deployed.
- Publish a new version of the component library in the master branch (view versioning instructions below).
- Go into the storefront you wish to update the component in and create a feature branch. Update the component library version and ensure the component is working as expected.
- Follow the normal development process for storefront features.

<!----><a name="version-publish"></a>

### Versioning and Publishing

---

After your PR is approved and merged to master, cut a new version of the library and publish to our npm repository.

`npm version minor|patch|major`

`minor` should be used for the majority of changes which should be any NON-BREAKING change

`major` should be used for any BREAKING change and you and the reviewer should agree on whether this version upgrade is appropriate or not

`patch` should be reserved for hotfixes. For example, if we have recently published v1.3.0 but our front end app is using v1.2.0 and has a breaking error but we do not want the changes included in v1.3.0 then we will need to branch off v1.2.0 and make a patch to fix the issue introduced in this version without introducing unwanted changes in the most recent version.

After successfully creating the new version, publish it to npm using the following command:
`npm publish`

To check that npm is now up to date with the most recent version, [view this package on NPM](https://www.npmjs.com/package/@nxtdtc/dtc-component-library).
The **Last Publish** and **Version** sections should reflect recent changes.

<!----><a name="useful-libraries"></a>

### Useful Libraries

---

- `@bit/mui-org.material-ui.grid` is a library that only contains the `Grid` component from `material-ui`. Read more about this library here: https://bit.dev/mui-org/material-ui/grid