@nxtdtc/dtc-component-library
v4.6.1
Published
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
Downloads
224
Keywords
Readme
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 Brand Style Config File Styling Components with Styled Components Storybook Landing Page Components Development Process
Versioning/Publishing Useful Libraries
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.
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).
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.
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).
import React from 'react';
import withTheme from '../withTheme';
const Button = ({ themeProps }) => (
<button
style={{ backgroundColor: themeProps?.primary?.backgroundColor }}
></button>
);
export default withTheme(Button);
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
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
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;
}};
`;
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
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
};
Development Process
Create a new feature branch from the master branch. Follow the coding standards defined below:
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)
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).
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.
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.
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.
QA / Integration Process
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
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.
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. The Last Publish and Version sections should reflect recent changes.
Useful Libraries
@bit/mui-org.material-ui.grid
is a library that only contains theGrid
component frommaterial-ui
. Read more about this library here: https://bit.dev/mui-org/material-ui/grid