This document explains what practices to follow while working in the frontend codebase.
Solve the problems at hand. Avoid premature optimization and architecture. Refactoring is and should be simple.
Javascript is only used where absolutely necessary, ie. code shared between app/jest, and jest configuration files (jest runs on vanilla js).
Avoid using global/window unless absolutely necessary. Wrap integrations in a reusable modules (Example: buildOptions.ts)
Warning: We have little to no linting support to control this.
Excessive use of globals can make unit testing very difficult.
Module imports that have side effects is strictly forbidden. This makes our code more flexible and the flow of code easier to follow.
Warning: We have little to no linting support to control this.
A common problem with module side effects is when a module has both side effects and exports. A programmer could then easily make a mistake by importing without anticipating the side effects.
Default exports is strictly forbidden. This makes our code easier to refactor and the flow of code easier to follow.
Heads up: Code style rule enabled!
Default exports force you do give imports a local name, which may differ from place to place.
We use tslint to enforce a code style. The rules come from tslint-config-airbnb, with a few exceptions.
Heads up: Linting is done automatically pre-commit.
See tslint.json for full list of rules.
Unit tests and linting runs automatically on your machine as pre-commit hooks, but also per commit in our CI. Failing either tests or lints will report errors in the CI. Builds won't pass unless tests and linting pass. You are responsible.
We use react-intl and WebTranslateIt for internationalization.
i18n state originates from I18nStore.ts and is passed on on to react-intl. Thus can you also use I18nStore for formatting when that is more convenient than react-intl.
<rootDir>/src/assets/i18n/reference.json
This is the reference i18n file. Make i18n changes here.
Your day-to-day involvement in i18n ends here. The rest of this section exist for transparency only.
i18n files are automatically downloaded and generated when you run the development server (See sync.ts for more information). i18n files are generated to <rootDir>/src/assets/i18n/generated
.
Do not modify these files. Any changes will be overwritten.
- Changes will be automatically uploaded to
WebTranslateIt
by our CI. - You can manually run
sync.ts upload
to upload changes (this is what the CI does), but this is not the intended workflow.
See state.md.
We manage forms using a custom library Form.ts and FormTransport.ts. We leverage validatorjs for validation.
All forms code should be located in src/forms
(see application structure guidelines)
We have a custom route configuration API.
Justification: We rely on static route configuration to align sitemap, unit test and menu generation for common routes. We tried to leverage router5 or react-router, but due to the lack of support for static route configuration or custom configuration options or route traversal in the end that created more complexity than creating a custom solution.
All routes are defined in routes.tsx.
For configuration options see RouteConfig.ts.
We use route-parser to parse route paths and allow its full syntax.
- Plain http requests are made with axios.
All UI is designed and programmed following the atomic design method using React and Material-UI.
While Material-UI provides us with almost all atoms, molecules and organisms we need, there are still common components across many features that require specific components.
It is expected of you to collaborate with the team and together to the best of your ability identify common patterns and re-use or create re-usable components across features.
See application structure documentation for how to structure ui components
We use CssBaseline to reset css.
All CSS is to be to written inline according to Material-UI standards
We have one exception: We have a custom withStyles.ts
:
Why?
Material-UI 1 is still in beta and the typescript definitions for working with styles has a limiting workflow (Material-UI admits so themselves, albeit this is due to current typescript limitations). We redefine the types of withStyles.ts without changing the behavior to improve this workflow.
We have made the following type changes:
withStyles()
(thefunction
) can (and should) be used asdecorator
WithStyles
(thetype
) can be used without generics (ie.React.Component<WithStyles>
).
Example:
// Import from 'ui' instead of 'material-ui'
import {WithStyles, withStyles} from 'ui/withStyles';
const styles = (theme: Theme) => ({
thing: {
padding: theme.spacing.unit,
background: theme.palette.background,
color: theme.palette.primary,
},
});
@withStyles(styles)
export class MyComponent extends React.Component<WithStyles> {
render () {
return <div className={this.props.classes.thing}/>;
}
}
Our hope is that material-ui will improve the typescript support as the beta finalizes, in which case we'll be able to easily refactor from using our override to the one from material-ui.
In depth: "CSS-in-JS" has many benefits
All images should be rendered using the SmartImage component. It automates loading state, transitions and gives us a centralized control over all images.
We use jest for testing.
- Tests should follow the naming convention
<filename>.test.ts
. - Everything in
/src/state
and ``/src/lib` should be covered, anything else is optional. - We configure jest to collect coverage automatically.
Running
npm run test
(or justjest
) will run all tests and collect and present coverage status.
Heads up: Testing is done automatically pre-commit and by the CI.
It is your responsibility to fix tests you break and to submit tests for code that needs coverage.