Annie's new and improved ✨ example component lab.
This is a living style guide and component lab that provides a toolbox of UI components for reuse within React apps.
- Implementing the Lab Components
- Lab Component Development
- Lab Skeleton (packages and dependencies)
To include the component lab's components into your project, you must manually add this git repository's address into your project's package.json
dependencies, as follows:
"dependencies": {
"annies-component-lab": "git@github.com:apennell/annies-component-lab.git#main"
}
This will pull from the main
branch of the annies-component
repository, although you can replace main
in the link with a version number, a tag name, or a commit hash.
Note: If you are using a branch name, new commits will not be automatically pulled into your project when you perform npm install / updates. Instead, you will need to manually remove the annies-component-lab
package from your project component(s)'s node_modules
and then run npm install
again.
Once you have the dependency installed, you'll need to import the specific individual components into your project's component. For example, to add the <Button />
component from the lab to a React component in the app, add at the top of your .jsx
file:
import { Button } from 'annies-component-lab';
You will now be able to use <Button />
like any other jsx component.
Follow the code standards of Airbnb's language style guides, so please consult them as you code and ensure that anything you write adheres to the code style defined there.
Install dependencies; run first before getting started!
Starts a styleguide dev server then watches for and compiles changes.
Open http://localhost:6060 to view the styleguide in the browser.
Builds a static version of the component lab.
Runs latest tests since last commit. npm test -- --coverage
will show overall test coverage.
Although being able to see all green passes, "Chasing 100% coverage is usually a misleading goal, and" they don't want to give people the feeling that [they] encourage that." So, suppress your inner A+ student.
-
Create a directory for the component within
src/components
in the proper section, e.g. a button component is in the Element section, sosrc/components/Elements/Button
.- If you're making a new section within
/components
(like ifElements
didn't already exist in the previous example), add the new section tostyleguide.config.js
to have it included. Give it aname
and assign thecomponents
key to the location where the components can be found. You can also direct it to a content path to include a markdown file at the top of the section.module.exports = { title: "Annie's Component Lab", sections: [ { name: 'Elements', content: './docs/Elements.md', components: './src/components/Elements/**/[A-Z]*.jsx' } ] };
- If you're making a new section within
-
Add a
.jsx
file for your component in that directory, e.g./Button/Button.jsx
. You will build your component here.- Include
import React from 'react';
andimport PropTypes from 'prop-types';
at the top of your file. - You can add a description (using the syntax noted below) at the top of the component, just below the
import
statements. - Construct your component, making sure to include
propTypes
anddefaultProps
. - Add comments above each propType to give it a description for the style guide output.
Basic structure of
.jsx
component file:/** src/components/Elements/Button/Button.jsx */ import React from 'react'; import PropTypes from 'prop-types'; /** Construct stateless functional Button component */ const Button = ({ children, onClick, buttonStyle, size }) => { /** * Get buttonStyle and size css CSS Modules styles object * Combine them into one class using classnames package */ let className = cx(styles[buttonStyle], styles[`${size}Size`]); return ( <button onClick={onClick} className={className}> {children} </button> ); }; /** Define all available proptypes with description */ Button.propTypes = { /** Button label */ children: PropTypes.string.isRequired, /** Function to call when button is clicked */ onClick: PropTypes.func, /** Button's style type */ buttonStyle: PropTypes.oneOf(['normal', 'primary', 'inverse', 'textLink']), /** Size of the button */ size: PropTypes.oneOf(['normal', 'large']), /** Set true if button should be full-width, else leave blank */ isFullWidth: PropTypes.bool }; /** Set all default props for non-required props */ Button.defaultProps = { buttonStyle: 'normal', size: 'normal', isFullWidth: false }; export default Button;
- Include
-
Add a component
README.md
titled after the component in its directory, e.g./Button/Button.md
. This is where you will document the component and display examples of it to render in the component lab. Any code block without a language tag will be rendered as a React component (the examples) with a live preview and its code. The code block in the component lab is editable in the browser!- Using markdown, start with any necessary notes or descriptions provided by Design.
- Provide code examples showing the use of available props, options and variations.
- For example, for
<Button />
, there is a section with an example of each of the following: options (button style), size, tags, and states. In the size example, there are default, large, and full-width buttons shown. - Add any additional instructions, descriptions, or notes from Design along with each of these examples so their purpose and implementation can be clearly understood without further explanation.
Styleguidist searches and parses components following the global structure of src/components/**/*.{js, jsx}
. It ignores test files (__tests__
directory and files names containing .test.js, .test.jsx, .spec.js and .spec.jsx
).
The general file structure should be as follows:
styleguide-1.0
|__docs // additional md docs that can be included as a component but are necessary for the style guide
|__{Identity}.md
|__src
|__components
|__Elements
|__Button
|__Button.jsx
|__Button.md
|__Modal
|__Modal.jsx
|__Modal.md
|__Pages
|__SomePage
|__styleguide // style guide build files--gitignore these
|__build
|__styleguide.config.js // configuration for styleguidist
The style guide components build into styleguide/build
, however the styleguide
directory should be included in the .gitignore
file.
Format for documentation to be parsed and appear in component lab:
/**
* Documentation text here. Markdown is *supported*.
*/
-
By default, any methods your components have are considered to be private and are not published. Mark your public methods with JSDoc
@public
tag to get them published in the docs. -
By default, all props in components are considered public and published in component lab docs. If you need to remove a prop from the documentation but keep in code, mark with JSDoc
@ignore
tag in the comment/description are directly above that prop. -
Available JSDocs tags for documenting components, props, and methods: @deprecated, @see, @link, @author, @since, and @version. You can also use @param, @arg, @argument for documenting props.
-
Code examples in Markdown files use ES6+JSX syntax and can access all components in the component lab using global variables (although ES6
import
syntax isn't supported within these files), e.g.:<Panel> <p>Using the Button component in the example of the Panel component:</p> <Button>Push Me</Button> </Panel>
-
Each example (in the
ComponentName.md
file) has its own state, accessible with thestate
variable and changed with thesetState
function:initialState = { isOpen: false }; <div> <button onClick={() => setState({ isOpen: true })}>Open</button> <Modal isOpen={state.isOpen}> <h1>Hallo!</h1> <button onClick={() => setState({ isOpen: false })}>Close</button> </Modal> </div>
View more documentation in Styleguidist's Cookbook.
To modularize our styles and classes and reduce the risk of global namespacing and clashing, we are using styled-components.
Todo :)
Todo! :)
We are using Jest as the test runner with React Testing Library.
- Create a file
*.test.js
within the directory of the component to test, i.e.src/components/Elements/Button/Button.test.js
for a<Button />
test. - Start with a simple smoke test to make sure the component simply mounts and doesn't throw during rendering.
- Write more tests, focusing on the user's perspective. Example of testing a button's
onClick
event:
import React from 'react';
import userEvent from '@testing-library/user-event';
import { render, screen } from 'test-utils';
import Button from './Button';
describe('Button', () => {
test('should render button with text', () => {
render(<Button>Click Me!</Button>);
const button = screen.getByRole('button', { name: /click me/i });
expect(button).toBeInTheDocument();
});
test('should handle click event', () => {
const onClickMock = jest.fn();
render(<Button onClick={onClickMock}>Click Me</Button>);
const button = screen.getByRole('button', { name: /click me/i });
userEvent.click(button);
expect(onClickMock).toBeCalled();
});
});
- Run
npm test
to run the latest tests since the last commit.npm test -- --coverage
will show overall test coverage. Runnpm run jest --watch
to run the tests in watch mode as you develop.
Testing Resources and Docs
The component lab/style guide was built using React Styleguidist. Usage of this package in our component lab is pretty well-documented here, but checkout their documents and cookbook for more information.
To modularize our styles and classes and reduce the risk of global namespacing and clashing, we are using styled-components. See the section on styling components for more on our implementation.
TODO :)