Skip to content
This repository has been archived by the owner on Feb 29, 2024. It is now read-only.

WestpacGEL/GEL

Repository files navigation

This repository is no longer maintained. The new GEL design system can be found here

GEL CircleCI

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

Content

Getting Started

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

Development Workflow

Here you'll find our contributing guidelines as well as how to add or edit a component.

Contributing

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.

Adding a new component

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.

Editing an existing component

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/.

File structure

👉 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)

NPM scripts

👉 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.

Design System

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:

  1. Component build
  2. Docs build

Some high level decisions

  • 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 on emotion 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

Theming / Multi-brand

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

Overrides

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 -

Data driven API

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' },
	]}
/>

How we handle focus state

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

Component build

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]

Blender support

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.