2019-01-23 04:37:46 -06:00
# Frontend Style Guide
2019-03-18 01:55:44 -05:00
Generally we follow the Airbnb [React Style Guide ](https://github.com/airbnb/javascript/tree/master/react ).
2019-01-23 04:37:46 -06:00
## Table of Contents
2019-09-20 11:45:06 -05:00
- [Frontend Style Guide ](#frontend-style-guide )
2021-08-31 05:55:05 -05:00
2019-09-20 11:45:06 -05:00
- [Table of Contents ](#table-of-contents )
- [Basic rules ](#basic-rules )
2020-09-30 14:45:07 -05:00
- [Naming conventions ](#naming-conventions )
- [Use `PascalCase` for: ](#use-pascalcase-for )
- [Typescript class names ](#typescript-class-names )
- [Types and interfaces ](#types-and-interfaces )
- [Enums ](#enums )
- [Use `camelCase` for: ](#use-camelcase-for )
- [Functions ](#functions )
- [Methods ](#methods )
- [Variables ](#variables )
- [React state and properties ](#react-state-and-properties )
- [Emotion class names ](#emotion-class-names )
- [Use `ALL_CAPS` for constants. ](#use-all_caps-for-constants )
- [Use BEM convention for SASS styles. ](#use-bem-convention-for-sass-styles )
- [Typing ](#typing )
- [File and directory naming conventions ](#file-and-directory-naming-conventions )
- [Code organization ](#code-organization )
- [Exports ](#exports )
- [Comments ](#comments )
- [Linting ](#linting )
2020-02-21 00:35:54 -06:00
- [React ](#react )
- [Props ](#props )
2021-08-31 05:55:05 -05:00
- [Name callback props and handlers with an "on" prefix. ](#name-callback-props-and-handlers-with-an-on-prefix )
- [React Component definitions ](#react-component-definitions )
- [React Component constructor ](#react-component-constructor )
- [React Component defaultProps ](#react-component-defaultprops )
2019-10-03 07:13:58 -05:00
- [State management ](#state-management )
2021-08-31 05:55:05 -05:00
2020-06-05 06:54:27 -05:00
- [Proposal for removing or replacing Angular dependencies ](https://github.com/grafana/grafana/pull/23048 )
2019-01-23 04:37:46 -06:00
## Basic rules
2019-09-20 11:45:06 -05:00
- Try to keep files small and focused.
- Break large components up into sub-components.
2020-02-21 00:35:54 -06:00
- Use spaces for indentation.
2019-01-23 04:37:46 -06:00
2020-02-21 00:35:54 -06:00
### Naming conventions
2019-01-23 04:37:46 -06:00
2020-02-21 00:35:54 -06:00
#### Use `PascalCase` for:
2019-01-23 04:37:46 -06:00
2020-02-21 00:35:54 -06:00
##### Typescript class names
2019-01-23 04:37:46 -06:00
2020-02-21 00:35:54 -06:00
```typescript
// bad
class dataLink {
//...
}
2019-01-23 04:37:46 -06:00
// good
2020-02-21 00:35:54 -06:00
class DataLink {
//...
}
```
##### Types and interfaces
```
// bad
interface buttonProps {
//...
}
// bad
interface button_props {
//...
}
// bad
interface IButtonProps {
//...
}
// good
interface ButtonProps {
//...
}
// bad
type requestInfo = ...
// bad
type request_info = ...
// good
type RequestInfo = ...
```
##### Enums
```
// bad
enum buttonVariant {
//...
}
// good
enum ButtonVariant {
//...
}
```
#### Use `camelCase` for:
##### Functions
```typescript
// bad
const CalculatePercentage = () => { ... }
// bad
const calculate_percentage = () => { ... }
// good
const calculatePercentage = () => { ... }
```
##### Methods
```typescript
class DateCalculator {
// bad
CalculateTimeRange () {...}
}
class DateCalculator {
// bad
2020-06-30 16:42:50 -05:00
calculate_time_range () {...}
2020-02-21 00:35:54 -06:00
}
class DateCalculator {
// good
calculateTimeRange () {...}
}
```
##### Variables
```typescript
// bad
const QueryTargets = [];
// bad
const query_targets = [];
// good
const queryTargets = [];
```
##### React state and properties
```typescript
interface ModalState {
// bad
IsActive: boolean;
// bad
is_active: boolean;
// good
isActive: boolean;
}
```
##### Emotion class names
```typescript
2022-09-23 01:22:16 -05:00
const getStyles = (theme: GrafanaTheme2) => ({
2020-02-21 00:35:54 -06:00
// bad
2020-06-30 16:42:50 -05:00
ElementWrapper: css`...`,
2020-02-21 00:35:54 -06:00
// bad
2022-09-23 01:22:16 -05:00
['element-wrapper']: css`...`,
2020-02-21 00:35:54 -06:00
// good
2022-09-23 01:22:16 -05:00
elementWrapper: css({
padding: theme.spacing(1, 2),
background: theme.colors.background.secondary,
}),
2020-02-21 00:35:54 -06:00
});
```
2024-02-29 04:47:22 -06:00
Use hook useStyles2(getStyles) to memoize the styles generation and try to avoid passing props to the getStyles function and instead compose classes using emotion cx function.
2022-09-23 01:22:16 -05:00
2020-02-21 00:35:54 -06:00
#### Use `ALL_CAPS` for constants.
```typescript
// bad
const constantValue = "This string won't change";
// bad
const constant_value = "This string won't change";
// good
const CONSTANT_VALUE = "This string won't change";
```
#### Use [BEM](http://getbem.com/) convention for SASS styles.
_SASS styles are deprecated. Please migrate to Emotion whenever you need to modify SASS styles._
2020-05-05 10:36:28 -05:00
### Typing
2021-08-31 05:55:05 -05:00
In general, you should let Typescript infer the types so that there's no need to explicitly define type for each variable.
2020-05-05 10:36:28 -05:00
There are some exceptions to this:
```typescript
2021-08-31 05:55:05 -05:00
// Typescript needs to know type of arrays or objects otherwise it would infer it as array of any
2020-05-05 10:36:28 -05:00
// bad
const stringArray = [];
// good
const stringArray: string[] = [];
```
2021-08-31 05:55:05 -05:00
Specify function return types explicitly in new code. This improves readability by being able to tell what a function returns just by looking at the signature. It also prevents errors when a function's return type is broader than expected by the author.
2020-05-05 10:36:28 -05:00
2020-09-30 14:45:07 -05:00
> **Note:** We don't have linting for this enabled because of lots of old code that needs to be fixed first.
2020-05-05 10:36:28 -05:00
```typescript
// bad
function transform(value?: string) {
if (!value) {
2021-08-31 05:55:05 -05:00
return undefined;
2020-05-05 10:36:28 -05:00
}
2021-08-31 05:55:05 -05:00
return applyTransform(value);
}
2020-05-05 10:36:28 -05:00
// good
function transform(value?: string): TransformedValue | undefined {
if (!value) {
2021-08-31 05:55:05 -05:00
return undefined;
2020-05-05 10:36:28 -05:00
}
2021-08-31 05:55:05 -05:00
return applyTransform(value);
}
2020-05-05 10:36:28 -05:00
```
2020-02-21 00:35:54 -06:00
### File and directory naming conventions
Name files according to the primary export:
- When the primary export is a class or React component, use PascalCase.
- When the primary export is a function, use camelCase.
For files exporting multiple utility functions, use the name that describes the responsibility of grouped utilities. For example, a file exporting math utilities should be named `math.ts` .
- Use `constants.ts` for files exporting constants.
- Use `actions.ts` for files exporting Redux actions.
- Use `reducers.ts` Redux reducers.
- Use `*.test.ts(x)` for test files.
2021-08-27 01:27:56 -05:00
- Use kebab case for directory names: lowercase, words delimited by hyphen ( `-` ). For example, `features/new-important-feature/utils.ts` .
2020-02-21 00:35:54 -06:00
### Code organization
Organize your code in a directory that encloses feature code:
- Put Redux state and domain logic code in `state` directory (i.e. `features/my-feature/state/actions.ts` ).
- Put React components in `components` directory (i.e. `features/my-feature/components/ButtonPeopleDreamOf.tsx` ).
- Put test files next to the test subject.
- Put containers (pages) in feature root (i.e. `features/my-feature/DashboardPage.tsx` ).
2021-12-23 02:28:41 -06:00
- Put API function calls that isn't a redux thunk in an `api.ts` file within the same directory.
2020-02-21 00:35:54 -06:00
- Subcomponents can live in the component folders. Small component do not need their own folder.
- Component SASS styles should live in the same folder as component code.
For code that needs to be used by external plugin:
- Put components and types in `@grafana/ui` .
- Put data models and data utilities in `@grafana/data` .
- Put runtime services interfaces in `@grafana/runtime` .
#### Exports
- Use named exports for all code you want to export from a file.
- Use declaration exports (i.e. `export const foo = ...` ).
2021-10-19 01:30:43 -05:00
- Avoid using default exports (for example, `export default foo` ).
2020-02-21 00:35:54 -06:00
- Export only the code that is meant to be used outside the module.
### Comments
- Use [TSDoc ](https://github.com/microsoft/tsdoc ) comments to document your code.
- Use [react-docgen ](https://github.com/reactjs/react-docgen ) comments (`/** ... */`) for props documentation.
- Use inline comments for comments inside functions, classes etc.
2020-02-25 06:59:11 -06:00
- Please try to follow the [code comment guidelines ](./code-comments.md ) when adding comments.
2020-02-21 00:35:54 -06:00
### Linting
Linting is performed using [@grafana/eslint-config ](https://github.com/grafana/eslint-config-grafana ).
## React
Use the following conventions when implementing React components:
### Props
##### Name callback props and handlers with an "on" prefix.
```tsx
// bad
handleChange = () => {
2019-01-23 04:37:46 -06:00
};
render() {
return (
2020-02-21 00:35:54 -06:00
< MyComponent changed = {this.handleChange} / >
2019-01-23 04:37:46 -06:00
);
}
2020-02-21 00:35:54 -06:00
// good
onChange = () => {
2019-01-23 04:37:46 -06:00
};
render() {
return (
2020-02-21 00:35:54 -06:00
< MyComponent onChange = {this.onChange} / >
2019-01-23 04:37:46 -06:00
);
}
2020-02-21 00:35:54 -06:00
2019-01-23 04:37:46 -06:00
```
2020-02-21 00:35:54 -06:00
##### React Component definitions
2019-01-23 04:37:46 -06:00
2019-03-18 01:55:44 -05:00
```jsx
// bad
export class YourClass extends PureComponent { ... }
2020-02-21 00:35:54 -06:00
// good
export class YourClass extends PureComponent< {},{}> { ... }
2019-03-18 01:55:44 -05:00
```
2019-01-23 04:37:46 -06:00
2020-02-21 00:35:54 -06:00
##### React Component constructor
2019-03-18 01:55:44 -05:00
```typescript
// bad
constructor(props) {...}
2020-02-21 00:35:54 -06:00
// good
constructor(props: Props) {...}
2019-03-18 01:55:44 -05:00
```
2020-02-21 00:35:54 -06:00
##### React Component defaultProps
2019-03-18 01:55:44 -05:00
```typescript
// bad
static defaultProps = { ... }
2020-02-21 00:35:54 -06:00
// good
static defaultProps: Partial< Props > = { ... }
2019-03-18 01:55:44 -05:00
```
2019-08-09 06:15:52 -05:00
2021-10-19 01:30:43 -05:00
### How to declare functional components
2023-03-16 03:57:29 -05:00
We prefer using function declarations over function expressions when creating a new react functional component.
2021-10-19 01:30:43 -05:00
```typescript
2023-03-16 03:57:29 -05:00
// bad
export const Component = (props: Props) => { ... }
// bad
export const Component: React.FC< Props > = (props) => { ... }
// good
2022-07-19 12:12:11 -05:00
export function Component(props: Props) { ... }
2021-10-19 01:30:43 -05:00
```
2023-03-16 03:57:29 -05:00
Some interesting readings on the topic:
- [Create React App: Remove React.FC from typescript template ](https://github.com/facebook/create-react-app/pull/8177 )
- [Kent C. Dodds: How to write a React Component in Typescript ](https://kentcdodds.com/blog/how-to-write-a-react-component-in-typescript )
- [Kent C. Dodds: Function forms ](https://kentcdodds.com/blog/function-forms )
- [Sam Hendrickx: Why you probably shouldn't use React.FC? ](https://medium.com/raccoons-group/why-you-probably-shouldnt-use-react-fc-to-type-your-react-components-37ca1243dd13 )
2019-10-03 07:13:58 -05:00
## State management
2019-08-09 06:15:52 -05:00
- Don't mutate state in reducers or thunks.
2020-06-05 06:54:27 -05:00
- Use `createSlice` . See [Redux Toolkit ](https://redux-toolkit.js.org/ ) for more details.
2019-10-03 07:13:58 -05:00
- Use `reducerTester` to test reducers. See [Redux framework ](redux.md ) for more details.
- Use state selectors to access state instead of accessing state directly.