First of all, thank you for contributing. It’s appreciated.
-
Install node >=
>=14.0.0 -
Clone the repo and install dependencies with
npm install. -
Make sure the core team, including design, approves your idea. This step is really important because, if you skip it, your PR might be rejected.
-
Below are some important commands for developing. Don't commit before fixing all errors and warnings.
npm run devstarts Storybook for quick, iterative developmentnpm run watchruns tests and watches for changes, optimized for speed
- Ensure your changes work properly on the latest versions of Chrome, Firefox, Safari, and IE Edge. Currently this step is manual.
- Reference the issue's number in your commit. E.g.: "Did this #12".
- Make a pull request.
- All PRs should at minimum have a label semver-major/minor/patch/none. This helps identify what kind of version bump the PR represents.
- Branches should follow this convention (where 1234 is a GitHub issue):
- 1234-add-something
- 1234-fix-something
- release/2.0.0
- We use an Pull Request Template that provides a check list of tasks for every PR.
Run command:
npm run create-component MyNewComponent
Here are some basic steps to get you started building your first component.
Go to http://localhost:6006/ and navigate to the ExampleComponent for a starter template component.
-
Create your new component folder
src/components/MyNewComponent/ -
Create your component file(s). The required file are:
MyNewComponent.tsxMyNewComponent.spec.tsMyNewComponent.stories.ts
- Add an import and export for your component's JSX file in
/src/index.ts.
import MyNewComponent from './components/MyNewComponent/MyNewComponent';
export { MyNewComponent };- (optional) - Add your component's style file to
/src/styles/components.less
@import '../components/MyNewComponent/MyNewComponent';- Use
isXorhasXprops for boolean types even when it differs from native. - Make sure all components allow
classNameandstyleto be extended for consumer convenience. - Use
onXprops for all event handlers.- Use
onVerbNounstructure.
- Use
- Use
handleXmethods for event handlers. - Every component that wraps around native controls should callback with all its props for performance reasons. This allows consumers to avoid creating functions inside their
renderblocks by attaching identifiers to components that can then be extracted at callback time.- When the callback event is fired, the last argument should be an object with at least
eventandprops. - See this article for a more in depth explanation.
- There is a test within the
controlsfunction of "generic-tests" to help test this convention.
- When the callback event is fired, the last argument should be an object with at least
- If the component is a thin wrapper around a native control, be sure to pass through any extra props through to the native component. Usually this is called
passThroughsin the code. - Stateless by default, use the reducer pattern for stateful components. Don't use
setStatein our components unless there is a very good reason. - Prefer
spans for root level component elements.
- All css classes must be prefixed by the name of the component. E.g.
.lucid-Button-foo. Useconst cx = lucidClassNames.bind('&-ExampleComponent');to bind and auto-prefix classnames and include in your component with<MyComponent classNames={cx}>. - Css classes used for state, such as
.is-active, should also be prefixed with the component name. E.g.lucid-Button-is-active. - We have a naming convention for our props. Below is a list of examples that are valid. Please see the
variables.lessfile for real world examples.
// In `variables.less`
@type-propName
@type-subType-propName
// In `MyExampleComponent.less` specific variables should be scoped
.lucid-MyExampleComponent {
@type-propName
@type-subType-propName
}
- We use our own wrapper around
createClass. - Folders should use kebab case e.g. "this-is-a-folder/and-a-javascript-file.js".
- Components should use start case e.g.:
- "MyComponent"
- "AnotherComponentHere.jsx"
- We define component description & meta data in static property of components called
peek.
MyExampleComponent.peek = {
description: `...`,
isPrivate: true,
};- We set the
isPrivateboolean to indicate that the component isn't intended for external consumption yet. This will hide the component from the docs.
- Components should have a
Component.spec.tsxthat lives alongside the component.tsxfile. - Most tests should be shallow rendered using
enzyme. - Use
expectandjestrather thanassertorsinon, which we are moving away from. - Sometimes we have tests with flaky timing, i.e. we're using
setTimeout. In those rare cases we put the text[mostly stable]in the name of the test as a kind of tag for future reference. - Our test files have a certain structure to them, here's an example of it:
describe('MyComponent', () => {
common(MyComponent);
describe('render', () => {});
describe('props', () => {
describe('myProp', () => {});
describe('myOtherProp', () => {});
});
describe('child components', () => {
describe('Foo', () => {});
describe('Bar', () => {});
});
});To get statistics on the module dependencies, both internal to the project and external NPM packages, run the following command:
NODE_ENV=development npx webpack --config webpack.config.bundle.cjs --json --stats > stats.jsonSimilar errors include saying string, bool, boolean, node, etc. are not defined. If you get this error, double check that all your defined propTypes in your component class are also destructured from PropTypes.
import PropTypes from 'prop-types';
const {
bool,
func,
object, //<-- destructuring the appropriate prop-type from PropTypes will solve the problem.
} = PropTypes;