For the docs for the design system please go to
https://gel.westpacgroup.com.au/design-system
This repository consists of the following parts:
/brands/*
and/components/*
- The react design system components (published to npm)/website
- The nextjs website/website-backend
- The keystonejs instance/helpers/*
- Some utility scripts/nginx/*
- The backend nginx configuration
This repo is a monorepo. Learn more about monorepos at https://monorepo.guide/.
To run this repo locally please use yarn.
cd path/to/repo
yarn && yarn prepare
Here you'll find our contributing guidelines as well as how to add or edit a component.
To contribute to this repo please follow the steps listed below:
1. Fork the repository.
Fork the repo and checkout the develop branch.
2. Install the Dependencies.
Install the project dependencies with:
cd path/to/repo
yarn
3. Implement changes.
When implementing your changes please ensure that you follow modern web development best practices.
4. Format & Generate prop-types
Format your changes and Generate your Prop-Types for documentation:
yarn prop-types && yarn format
5. Test
Run the Format, Unit and Integration Tests with:
yarn test
6. Add a Changeset
Any Component changes will require a changeset (This is how we manage versioning and changelogs).
To create a changeset run:
yarn changeset add
Make sure to follow SemVer convention and write a meaningful description of the change in the changelog.
7. Create a Pull Request
- Create a PR and target the "develop" branch.
- Write a meaningful title and description for your PR.
- Ensure the PR passess the GitHub Workflow.
8. Review
Wait for your changes to be reviewed and approved.
9. Merge
The Maintainer will merge the Pull Request into the Develop branch.
10. Publish
The Maintainer will publish changes to NPM.
To add a new component (aka package), run the below command from the root of the project and then follow the prompts:
yarn new my-component-name
Note: replace my-component-name
with the component you wish to add.
This will create a new entry in the /components
directory with all of the files you need to get started and with all dependencies installed. Please note that this component will be TypeScript.
To edit a component, including if you've just added one using the steps above, use the below command from the root of the project:
yarn dev my-component-name
Note: replace my-component-name
with the component you wish to run.
This component can now be viewed on http://localhost:8080/
.
👉 The file structure of this monorepo
.
├── LICENSE
├── README.md
├── package.json
├── yarn.lock
│
├── helper/
│ ├── .component-template/ # the starter files for when a new component is created via cli
│ │
│ ├── example/
│ │ ├── components/ # an assortment of components helping us build the example pages
│ │ ├── _utils.js # shared logic
│ │ ├── docs.js # entry file for `yarn docs` task
│ │ ├── docs.webpack.config.js # dynamic webpack config to run the `yarn docs` task
│ │ ├── index.html # the index.html file as entry point
│ │ ├── start.js # entry file for `yarn start` task
│ │ └── start.webpack.config.js # dynamic webpack config to run the `yarn start` task
│ │
│ ├── public/ # files in this folder will be moved into the docs/ folder
│ │
│ ├── tests/ # test specific files for reuse between component tests
│ │
│ ├── transformer/ # the transfomer files to convert tokens into platform data
│ │ ├── _utils.js # shared logic
│ │ └── web.js # transforms tokens to web
│ │
│ ├── buildExports.js # helps cypress testing of each component
│ ├── cli.js # helper file for cli like adding a new module
│ ├── ds-info.js # helper file to generate the GEL.json
│ └── tester.js # helps cypress testing of each component
│
├── components/ # all ds components that are published
│ ├── component1/ # more on the structure of components below
│ ├── component2/
│ └── component3/
│
├── brands/ # all brand components
│ ├── BOM/
│ │ ├── dist/ # distribution files (build artifact)
│ │ ├── overrides/ # all overrides files specific to this brand
│ │ ├── src/ # our source files
│ │ ├── tokens/ # token files
│ │ ├── CHANGELOG.md
│ │ ├── LICENSE
│ │ ├── package.json
│ │ └── README.md
│ ├── BSA/
│ │ └── etc
│ └── WBC/
│ └── etc
│
├── website/ # files for the website
│
├── website-backend/ # files for the website backend
│
└── docs/ # the static files for the documentation (build artifact)
👉 Script names and descriptions
script | description |
---|---|
yarn |
install all dependencies |
yarn prop-types |
generates the prop-types according to the interface created |
yarn nuke |
removes all node_modules for fresh start |
yarn fresh |
removes all node_modules and reinstalls them |
yarn build |
build all dist folders for production |
yarn build:dev |
build all dist for local consumption |
yarn docs |
build docs for all components and run server |
yarn docs:build |
build docs for all components to ./docs/ folder |
yarn new [package-name] |
create a new specified empty component |
yarn dev [package-name] |
start the example server of a component |
yarn test |
runs tests |
lint:format:fix |
runs prettier and eslint to format and lint all code |
See the Website README for details on it's deployment.
The design system components are spread between two folders:
.
├── brands/ # The brand components
└── components/ # The design system components including core
There are two different ways you can run the components locally:
- All components use named exports as the default, no default exports
- All brands components will have a default export containing the "tokens" objects in addition to the named exports of each.
- Fonts can't be shipped with npm so the tokens only define the css declarations for the fonts
- For css-in-js we use
jsx
imported from@westpac/core
and never depend onemotion
directly other than inside core itself
👉 The file structure of components
.
├── CHANGELOG.md
├── README.md
├── LICENSE
├── package.json # scope: `@westpac/`
│
├── src/ # all the source
│ ├── overrides/ # all overrides files for each sub-component
│ ├── _util.js # for reused logic within the component [optional]
│ ├── index.js # only for exports
│ └── ComponentX.js # only for the components, can be multiple files
│
├── examples/ # the demo folder is for seeing the components in action
│ ├── _util.js # for reused logic within the examples [optional]
│ ├── 00-example.js # show-case props and variations
│ ├── 10-example.js # all files not starting with a dot or an underscore
│ └── 20-example.js # will be processes with `yarn start`
│
├── demos/ # the examples that can be embedded into the website
│ ├── example-x.js
│ ├── example-y.js
│ └── example-z.js
│
└── tests/ # test includes all tests
├── integration/
│ └── test.cypress.js # cypress test file for integration tests
└── unit/
└── unit.spec.js # jest test file for unit tests
👉 Script names and descriptions
script | description |
---|---|
yarn start |
start the example server |
yarn test |
runs test headless |
yarn test:dev |
runs test by opening cypress app |
yarn test:integration |
runs integration tests |
Multi-brand will be achieved by added a brand package that will be passed to the <GEL/>
component
import { GEL } from '@westpac/core';
import brand from '@westpac/WBC';
export const App = () => <GEL brand={brand}>Your app</GEL>;
The brand package includes tokens and overrides and will be available to all packages inside the <GEL/>
wrapper via context.
This allows us to be as consistent as can be within the same app.
It also separates out all things to do with theming into a single package.
The brand package does not need to know anything about its components.
But it can opt in.
👉 Tokens
name | purpose |
---|---|
COLORS |
Our colors including tints |
LAYOUT |
Only breakpoints so far |
PACKS |
Mostly typography packs for reuse and consistency |
SPACING |
A function with minor scale to allow you to hit the grid |
TYPE |
Font files and definitions |
BRAND |
The current brand string |
A consumer may choose to override a component in its build. This can happen by adding the overrides into the same namespace as the component you wish to override.
import { Tabcordion } from '@westpac/tabcordion';
import { GEL } from '@westpac/core';
import brand from '@westpac/wbc';
brand['@westpac/tabcordion'].TabItem.styles = ( styles, state ) => { ...styles, border: 'red solid 2px' };
brand['@westpac/tabcordion'].TabRow.component = <TabRow />;
export const App = () => (
<GEL brand={brand}>
All instances of the <Tabcordion/> now include the override specified above.
No matter how many there are.
</GEL>
);
There may also a need to add an override only to a single instance of the component.
In that case you may add the override to the component directly via the overrides
prop each component has.
import { Tabcordion } from '@westpac/tabcordion';
import { GEL } from '@westpac/core';
import brand from '@westpac/wbc';
brand['@westpac/tabcordion'].Tabcordion.styles = ( styles, state ) => { ...styles, border: 'red solid 2px' };
const overrides = {
Tabcordion: {
styles: ( styles, state ) => { ...styles, border: 'blue solid 2px' },
},
};
export const App = () => (
<GEL brand={brand}>
<Tabcordion/> with red border
<Tabcordion overrides={overrides} /> with blue border
<Tabcordion/> with red border
</GEL>
);
(💡 Overrides will be reconciled as a cascade from less-specific to most-specific.)
Every single component (including root component) have three items in their override object:
overrides = {
[name]: {
styles: (base-styles, state-props) => styles,
component: <React.Component/>
attributes: (base-attributes, state-props) => Object,
}
}
👉 Overrides
Key | Type | Description | Function arguments |
---|---|---|---|
styles |
function => Object |
A function that returns an object of css properties for emotion | (base-styles ,state/props ) base-styles = the styles that would have been applied to the component, state/props = all props and all known state (without setters) |
attributes |
function => Object |
A function that returns an object of attributes that will be spread onto the component | (base-attributes ,state/props ) base-attributes = the attributes that would have been applied to the component, state/props = all props and all known state (without setters) |
component |
react component |
A react component which will receive all props | - |
Components that are made up by other components like list
, breadcrumb
, button-group
, input-group
etc can be solely driven by the data
prop.
We also offer declarative APIs in-case a consumer wants to wrap a component.
<Breadcrumb>
<Crumb href="#/" text="Home" />
<Crumb href="#/personal-banking/" text="Personal" />
<Crumb href="#/credit-cards/" text="Credit cards" />
</Breadcrumb>
Is the same as:
<Breadcrumb
data={[
{ href: '#/', text: 'Home' },
{ href: '#/personal-banking/', text: 'Personal' },
{ href: '#/credit-cards/', text: 'Credit cards' },
]}
/>
We use the useFocus hook in the GEL
component.
You can read about the focus hook on medium.
The GEL
also adds the global focus styling from our PACKS.focus
token pack.
If you need to add it to something use:
':focus': {
...PACKS.focus,
},
However if you need a focus state that will persist across mouse users do something like this:
const { PACKS } = useBrand();
const focus = { ...PACKS.focus };
focus.outline += ' !important'; // adding `!important` will make sure the focus persists
<Component
css={{
':focus': { ...focus },
}}
/>;
👉 Naming convention for files inside components
name | purpose |
---|---|
index.js |
Export only public API |
_utils.js |
For code shared between components (ignored in examples) [optional] |
ComponentX.js |
All component files are named after the exported component and pascal cased |
00-*.js |
All files inside the examples/ folder are sorted by file name |
*.js |
All jsx files are postfixed with .js |
*.spec.js |
All (jest) unit tests are postfixed with .spec.js |
👉 Props API vocabulary
Prop | Description |
---|---|
tag |
When a component can be rendered as different tags |
look |
When talking about the look of a component like success or hero |
href |
When something points at a thing via a link |
icon iconLeft iconRight |
For passing in an icon |
disabled or noBorder |
For passing boolean flags we use natural language and not is or has prefixes |
size |
For the physical size of a component, should be: 'small', 'medium', 'large', 'xlarge' |
spacing |
For the whitespace size of a component, should be: 'small', 'medium', 'large', 'xlarge' |
value |
For when a component shows a value, often numbers but not only |
selected |
For things inside lists that are being targeted. Like ButtonGroups or CheckGroup . Takes string or array |
assistiveText |
For labeling things for assistive technology (generally renders using VisuallyHidden or aria-label depending on use case) |
xsmall small medium large xlarge |
For t-shirt sizing |
data |
A prop to drive a component-group from data alone |
This build is running all the components examples/demos directly and nothing else. It's for development of a component and for testing it.
You run it via:
cd path/to/root/of/repo
yarn dev [component name]
The blender can generate human readable html and css from react and emotion components.
Read more about how you add blender support in the blender README.md.