Please take a moment to review this document in order to make the contribution process easy and effective for everyone involved.
Following these guidelines helps to get issues organised and PRs merged faster!
- Components should work across all platforms (web) to the same level of minimum functionality (as determined by UX). This is to ensure that master is always in a releasable state and app projects can use our components with confidence. To enable this we're using react-native-web
- Components should provide a suite of sensible events for their interactions. This will allow metric components to report back for a given context
- Screenshots are required for visual changes on web. Pictures are worth a thousand words
- Only add components that are wanted. They should form part of a larger feature and not be added in isolation because they might be useful in the future
- Use React perf and Chrome dev tools to identify issues AFTER the code is functionally complete
- In general we use yarn, add a yarn.lock file and keep it up to date for faster builds
- We use Prettier to ensure code consistency and reliability, this pattern should also be followed to avoid typical dev bike-shedding
We're using lerna for the monorepo with each
component in it's own package that should stand alone with it's own tests and
react story etc. A component is published in two ways. A compiled dist
version
for Native, so the platform doesn't need to worry about various Babel configs,
and a rnw.js
bundle for web. The package.json
main
points at the dist
entrypoint and relative paths are used to access the rnw
bundle.
For ease of use there is a CLI for creating a component. This is the quickest
way to create a package with the required scaffolding which is; a
component,package.json
, stubbed test and showcase/story. Note that the stubbed
test will fail until a snapshot is created withjest --updateSnapshot
or a test
run is made without the--CI
flag.
To use this, in the root of the project run: ./times-components create component ComponentName "Component Description"
When developing a component it's easiest to use the storybooks with hot reloading. Make sure you follow the React Native instructions to get up and running first. See README.md for commands to run the storybook.
We use styled-components to give us the ability to use media queries and keyframes to improve both UX and DX. This allows us to server-side-render pages but provide different experiences without the need for JavaScript.
It is possible to dynamically create a styled-component
at runtime but this
leads to poor SSR performance. On the server, one instance of the component will
be created, and on the client another, which breaks the benefits of hydration
and would result in another render. In addition, if you provide props to a
component that does this, you can't use lifecycle hooks such as
componentDidUpdate
because these components are always torn down (unmounted).
Avoid this pattern and use control flow to choose a specific styled-component
instead.
We use react-native-web
to compile our React Native components to web friendly
React. This relies on aggregating the given React Native styles and converting
them into CSS classes. Not only is it best practice to
use a stylesheet
whenever creating styles, it's also mandatory for hydration of SSR code. When
using object literals, the styles can be incorrectly aggregated on the server
and the client due to the different JS objects.
There are some problems regarding the usage of native storybooks with the Android simulator, mainly with hot module reloading (HMR). To take full advantage of HMR while developing components while testing with storybooks use the iOS simulator instead.
npm run storybook:build
will output the built web storybook into the default
storybook-static
folder that is synced to the gh_pages
branch to demo the
components in the web
When the CI passes packages:publish
will be run that uses
conventional commits to bump to the correct
semver version, create a CHANGELOG and push to the @times-components
org on
npm
When creating a new component you should specify the most suitable category in the showcase file. The current categories are:
- Primitive - components that are the basic building blocks from which other components can be composed
- Composed - components that are composed of Primitives
- Pages - complex page level components made up from multiple Composed and Primitive components
- Helpers - tools, utilities and helpers
For example to add a Slider
component to the the Composed
category you just
prefix the category name in the slider.showcase.js
file.
{
name: "Composed/Slider",
children: [
...showcases here
]
}
Good pull requests, such as patches, improvements, and new features, are a fantastic help. They should remain focused in scope and avoid containing unrelated commits. There should be a single PR for each component/package
Please ask first if somebody else is already working on this or the core developers think your feature is in-scope. Generally always have a related issue with discussions for whatever you are including.
Every component should have a XXXX.test.js
file with the component's Jest
tests split into the relevant platform folder e.g. android
, ios
, web
.
- TimesDigitalW04.ttf
- TimesDigitalW04_italic.ttf
- TimesDigitalW04_bold.ttf
EXCEPT FOR REGULAR eg
TimesDigitalW04.ttf for a TimesDigitalW04-Regular.ttf font
- TimesDigitalW04-Regular
- TimesDigitalW04-Italic
- TimesDigitalW04-Bold
- TimesDigitalW04
The filename, font name and family name should refer to the complete font file name
- Filename format:
<fontname>-<weight>
- Font Name / Full name format :
<fontname>-<weight>
- Family Name format:
<fontname>-<weight>
eg
- Filename = TimesDigitalW04-Regular
- FontName / Full name = TimesDigitalW04-Regular
- FamilyName = TimesDigitalW04-Regular
An example component/package looks like this:
.
└── card
├── __tests__
│ └── android
│ └── __snapshots__
│ └── card.android.test.js.snap
│ └── card-with-style.android.test.js.snap
│ └── card.android.test.js
│ └── card-with-style.android.test.js
│ └── ios
│ └── web
│ └── shared.test.js
│ └── shared.native.js
│ └── shared.web.js
├── src
│ └── card.js
├── card.showcase.js
├── card.stories.js
├── CHANGELOG.md
├── package.json
├── README.md
├── rnw.js
└── webpack.config.js
- .storybook houses the web storybook config that dynamically loads the component stories and aliases the native imports to web
- .storybook.native is home to the generated
story-loader.js
from theprestorybook-native
npm script, and the storybook bootstrapping - .babelrc, .buckconfig, .gitattributes, .watchmanconfig, app.json are all from a stock react-native project in order to run the native storybook
- lerna.json specifies that the packages are independent so different platforms can control which versions they consume and allows them to develop organically
-
Clone the repo with
https://github.com/newsuk/times-components.git
-
Run
yarn
in the root folder. Set aGRAPHQL_ENDPOINT
envar for linting.
Once it is done, you can run npm run storybook
and/or npm run storybook-native
The Provider Queries package
is home to any GraphQL queries that should be
shared across the various platforms. The
Times Public API schema is
generated in the schema
package which should be updated when any fields are
changed. The packages linting will then check that each of the gql
queries
within the package are properly formed
Each component should be bumped and published correctly when it is merged to master and has passed the CI process
This was heavily sourced from CRA