* Change to using arrow functions Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: Matheus <matheus_benini@hotmail.com> * Add lint rule --------- Co-authored-by: v1b3m <vibenjamin6@gmail.com> Co-authored-by: Matheus <matheus_benini@hotmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
236 lines
5.7 KiB
Plaintext
236 lines
5.7 KiB
Plaintext
---
|
||
sidebar_position: 3
|
||
sidebar_custom_props:
|
||
icon: TbPencil
|
||
---
|
||
|
||
# Style guide
|
||
|
||
We define here the rules to follow when writing code.
|
||
|
||
Our goal is to have a consistent codebase, easy to read and easy to maintain.
|
||
|
||
For this we prefer to tend towards being a bit more verbose than being too concise.
|
||
|
||
Always keep in mind that code is read more often than it is written, especially on an open source project, where anyone can contribute.
|
||
|
||
There are a lot of rules that are not defined here, but that are automatically checked by our linters.
|
||
|
||
## React
|
||
|
||
### Use functional components
|
||
|
||
Always use TSX functional components.
|
||
|
||
Do not use default import with const, because it's harder to read and harder to import with code completion.
|
||
|
||
```tsx
|
||
// ❌ Bad, harder to read, harder to import with code completion
|
||
const MyComponent = () => {
|
||
return <div>Hello World</div>;
|
||
};
|
||
|
||
export default MyComponent;
|
||
|
||
// ✅ Good, easy to read, easy to import with code completion
|
||
export function MyComponent() {
|
||
return <div>Hello World</div>;
|
||
};
|
||
```
|
||
|
||
### Props
|
||
|
||
Create the type of the props and call it `OwnProps` if there's no need to export it.
|
||
|
||
Use props destructuring.
|
||
|
||
```tsx
|
||
// ❌ Bad, no type
|
||
export const MyComponent = (props) => <div>Hello {props.name}</div>;
|
||
|
||
// ✅ Good, type
|
||
type OwnProps = {
|
||
name: string;
|
||
};
|
||
|
||
export const MyComponent = ({ name }: OwnProps) => <div>Hello {name}</div>;
|
||
```
|
||
|
||
#### Refrain from using `React.FC` or `React.FunctionComponent` to define prop types.
|
||
|
||
```tsx
|
||
/* ❌ - Bad, defines the component type annotations with `FC`
|
||
* - With `React.FC`, the component implicitly accepts a `children` prop
|
||
* even if it's not defined in the prop type. This might not always be
|
||
* desirable, especially if the component doesn't intend to render
|
||
* children.
|
||
*/
|
||
const EmailField: React.FC<{
|
||
value: string;
|
||
}> = ({ value }) => <TextInput value={value} disabled fullWidth />;
|
||
```
|
||
|
||
```tsx
|
||
/* ✅ - Good, a separate type (OwnProps) is explicitly defined for the
|
||
* component's props
|
||
* - This method doesn't automatically include the children prop. If
|
||
* you want to include it, you have to specify it in OwnProps.
|
||
*/
|
||
type OwnProps = {
|
||
value: string;
|
||
};
|
||
|
||
const EmailField = ({ value }: OwnProps) => (
|
||
<TextInput value={value} disabled fullWidth />
|
||
);
|
||
```
|
||
|
||
## JavaScript
|
||
|
||
### Use nullish-coalescing operator `??`
|
||
|
||
```tsx
|
||
// ❌ Bad, can return 'default' even if value is 0 or ''
|
||
const value = process.env.MY_VALUE || 'default';
|
||
|
||
// ✅ Good, will return 'default' only if value is null or undefined
|
||
const value = process.env.MY_VALUE ?? 'default';
|
||
```
|
||
|
||
### Use optional chaining `?.`
|
||
|
||
```tsx
|
||
// ❌ Bad
|
||
onClick && onClick();
|
||
|
||
// ✅ Good
|
||
onClick?.();
|
||
```
|
||
|
||
## TypeScript
|
||
|
||
### Use type instead of Interface
|
||
|
||
We decided to always use type instead of interface, because they almost always overlap, and type is more flexible.
|
||
|
||
```tsx
|
||
// ❌ Bad
|
||
interface MyInterface {
|
||
name: string;
|
||
}
|
||
|
||
// ✅ Good
|
||
type MyType = {
|
||
name: string;
|
||
};
|
||
```
|
||
|
||
### Use string literals instead of enums
|
||
|
||
[String literals](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#literal-types) are the go-to way to handle enum-like values in TypeScript. They are easier to extend with Pick and Omit, and offer a better developer experience, especially with code completion.
|
||
|
||
You can see why TypeScript recommend avoiding enums here : https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#enums
|
||
|
||
```tsx
|
||
// ❌ Bad, utilizes an enum
|
||
enum Color {
|
||
Red = "red",
|
||
Green = "green",
|
||
Blue = "blue",
|
||
}
|
||
|
||
let color = Color.Red;
|
||
```
|
||
|
||
```tsx
|
||
// ✅ Good, utilizes a string literal
|
||
let color: "red" | "green" | "blue" = "red";
|
||
```
|
||
|
||
#### GraphQL and internal libs
|
||
|
||
We recommend using enums that are generated by GraphQL codegen.
|
||
|
||
We also recommend using an enum when using an internal lib, so the internal lib doesn't have to expose a string literal type that is not related to the internal API.
|
||
|
||
Example :
|
||
|
||
```TSX
|
||
const {
|
||
setHotkeyScopeAndMemorizePreviousScope,
|
||
goBackToPreviousHotkeyScope,
|
||
} = usePreviousHotkeyScope();
|
||
|
||
setHotkeyScopeAndMemorizePreviousScope(
|
||
RelationPickerHotkeyScope.RelationPicker,
|
||
);
|
||
```
|
||
|
||
## Styling
|
||
|
||
### Use StyledComponents
|
||
|
||
Components should be styled with [styled-components](https://emotion.sh/docs/styled).
|
||
|
||
```tsx
|
||
// ❌ Bad
|
||
<div className="my-class">Hello World</div>
|
||
```
|
||
|
||
```tsx
|
||
// ✅ Good
|
||
const StyledTitle = styled.div`
|
||
color: red;
|
||
`;
|
||
```
|
||
|
||
Styled components should be prefixed with "Styled" to differentiate them from "real" components.
|
||
|
||
```tsx
|
||
// ❌ Bad
|
||
const Title = styled.div`
|
||
color: red;
|
||
`;
|
||
```
|
||
|
||
```tsx
|
||
// ✅ Good
|
||
const StyledTitle = styled.div`
|
||
color: red;
|
||
`;
|
||
```
|
||
|
||
### Theming
|
||
|
||
Utilizing the theme for the majority of component styling is the preferred approach.
|
||
|
||
#### Units of measurement
|
||
|
||
Avoid using `px` or `rem` values directly within the styled components. The necessary values are generally already defined in the theme, so it’s recommended to make use of the theme for these purposes.
|
||
|
||
#### Colors
|
||
|
||
Refrain from introducing additional colors; instead, utilize the existing palette from the theme. Should there be a situation where the palette does not align, kindly leave a comment so that it can be rectified.
|
||
|
||
|
||
```tsx
|
||
// ❌ Bad, directly specifies style values without utilizing the theme
|
||
const StyledButton = styled.button`
|
||
color: #333333;
|
||
font-size: 1rem;
|
||
font-weight: 400;
|
||
margin-left: 4px;
|
||
border-radius: 50px;
|
||
`;
|
||
```
|
||
|
||
```tsx
|
||
// ✅ Good, utilizes the theme
|
||
const StyledButton = styled.button`
|
||
color: ${({ theme }) => theme.font.color.primary};
|
||
font-size: ${({ theme }) => theme.font.size.md};
|
||
font-weight: ${({ theme }) => theme.font.weight.regular};
|
||
margin-left: ${({ theme }) => theme.spacing(1)};
|
||
border-radius: ${({ theme }) => theme.border.rounded};
|
||
`;
|