Introduce UI Components documentation (#1926)
* new contributor guide folder architecture * update content pass 1 * Prepare UI component folder to receive componentns * Add component doc example for button * Fix broken links * Fix broken links * Fix images
This commit is contained in:
4
docs/docs/contributor/_category_.json
Normal file
4
docs/docs/contributor/_category_.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"label": "Contributor guide",
|
||||
"position": 3
|
||||
}
|
||||
3
docs/docs/contributor/frontend/_category_.json
Normal file
3
docs/docs/contributor/frontend/_category_.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"position": 3
|
||||
}
|
||||
7
docs/docs/contributor/frontend/advanced/_category_.json
Normal file
7
docs/docs/contributor/frontend/advanced/_category_.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "Advanced",
|
||||
"position": 3,
|
||||
"customProps": {
|
||||
"icon": "TbTerminal2"
|
||||
}
|
||||
}
|
||||
302
docs/docs/contributor/frontend/advanced/best-practices.mdx
Normal file
302
docs/docs/contributor/frontend/advanced/best-practices.mdx
Normal file
@ -0,0 +1,302 @@
|
||||
---
|
||||
sidebar_position: 3
|
||||
sidebar_custom_props:
|
||||
icon: TbChecklist
|
||||
---
|
||||
|
||||
# Best practices
|
||||
|
||||
## State management
|
||||
|
||||
We use React and Recoil for state management.
|
||||
|
||||
### Use `useRecoilState` to store state
|
||||
|
||||
We recommend that you create as many atoms as you need to store your state.
|
||||
|
||||
Rule of thumb : It's better to be using too many atoms than trying to be too concise with props drilling.
|
||||
|
||||
```tsx
|
||||
export const myAtomState = atom({
|
||||
key: 'myAtomState',
|
||||
default: 'default value',
|
||||
});
|
||||
|
||||
export const MyComponent = () => {
|
||||
const [myAtom, setMyAtom] = useRecoilState(myAtomState);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<input
|
||||
value={myAtom}
|
||||
onChange={(e) => setMyAtom(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Do not use `useRef` to store state
|
||||
|
||||
We do not recommend using `useRef` to store state.
|
||||
|
||||
If you want to store state, you should use `useState` or `useRecoilState`.
|
||||
|
||||
We recommend seeing [how to manage re-renders](#managing-re-renders) if you feel like you need `useRef` to prevent some re-renders from happening.
|
||||
|
||||
## Managing re-renders
|
||||
|
||||
Re-renders can be hard to manage in React.
|
||||
|
||||
We provide you with some rules that we follow to avoid unnecessary re-renders.
|
||||
|
||||
Keep in mind that re-renders can **always** be avoided by understanding the cause of the re-render.
|
||||
|
||||
### Work at the root level
|
||||
|
||||
We made it easy for you to avoid re-renders in new features by taking care of eliminating them at the root level.
|
||||
|
||||
There's only one `useEffect` in the sidecar component `PageChangeEffect` that is holding all the logic that should be executed on a page change.
|
||||
|
||||
That way you know that there's only one place that can trigger a re-render.
|
||||
|
||||
### Always think twice before adding `useEffect` in your codebase
|
||||
|
||||
Re-renders are often caused by unnecessary `useEffect`.
|
||||
|
||||
You should think whether the useEffect is really needed, or if you can move the logic in a event handler function.
|
||||
|
||||
You'll find it generally easy to move the logic in a `handleClick` or `handleChange` function.
|
||||
|
||||
You can also find them in libraries like Apollo : `onCompleted`, `onError`, etc.
|
||||
|
||||
### Use a sibling component to extract useEffect or data fetching logic
|
||||
|
||||
If you feel like you need to add a `useEffect` in your root component, you should consider extracting it in a sidecar component.
|
||||
|
||||
The same can be applied for data fetching logic, with Apollo hooks.
|
||||
|
||||
```tsx
|
||||
// ❌ Bad, will cause re-renders even if data is not changing,
|
||||
// because useEffect needs to be re-evaluated
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
|
||||
useEffect(() => {
|
||||
if(someDependency !== data) {
|
||||
setData(someDependency);
|
||||
}
|
||||
}, [someDependency]);
|
||||
|
||||
return <div>{data}</div>;
|
||||
};
|
||||
|
||||
export const App = () => (
|
||||
<RecoilRoot>
|
||||
<PageComponent />
|
||||
</RecoilRoot>
|
||||
);
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, will not cause re-renders if data is not changing,
|
||||
// because useEffect is re-evaluated in another sibling component
|
||||
export const PageComponent = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
|
||||
return <div>{data}</div>;
|
||||
};
|
||||
|
||||
export const PageData = () => {
|
||||
const [data, setData] = useRecoilState(dataState);
|
||||
const [someDependency] = useRecoilState(someDependencyState);
|
||||
|
||||
useEffect(() => {
|
||||
if(someDependency !== data) {
|
||||
setData(someDependency);
|
||||
}
|
||||
}, [someDependency]);
|
||||
|
||||
return <></>;
|
||||
};
|
||||
|
||||
export const App = () => (
|
||||
<RecoilRoot>
|
||||
<PageData />
|
||||
<PageComponent />
|
||||
</RecoilRoot>
|
||||
);
|
||||
```
|
||||
|
||||
### Use recoil family states and recoil family selectors
|
||||
|
||||
Recoil family states and selectors are a great way to avoid re-renders.
|
||||
|
||||
They are especially useful when you need to store a list of items.
|
||||
|
||||
### You shouldn't use `React.memo(MyComponent)`
|
||||
|
||||
We do not recommend `React.memo()` usage because it does not solve the cause of the re-render, but instead breaks the re-render chain, which can lead to unexpected behavior and make the code really hard to refactor.
|
||||
|
||||
### Limit `useCallback` or `useMemo` usage
|
||||
|
||||
They are often not necessary and will make the code harder to read and maintain for a gain of performance that is unnoticeable.
|
||||
|
||||
## Console.logs
|
||||
|
||||
`console.log` statements are invaluable during development, offering real-time insights into variable values and code flow. However, leaving them in production code can lead to several issues:
|
||||
|
||||
1. **Performance**: Excessive logging can affect the runtime performance, especially on client-side applications.
|
||||
|
||||
2. **Security**: Logging sensitive data can expose critical information to anyone who inspects the browser's console.
|
||||
|
||||
3. **Cleanliness**: Filling up the console with logs can obscure important warnings or errors that developers or tools need to see.
|
||||
|
||||
4. **Professionalism**: End users or clients checking the console and seeing a myriad of log statements might question the code's quality and polish.
|
||||
|
||||
## Naming
|
||||
|
||||
### Variable Naming
|
||||
|
||||
Variable names ought to precisely depict the purpose or function of the variable.
|
||||
|
||||
#### The issue with generic names
|
||||
Generic names in programming are not ideal because they lack specificity, leading to ambiguity and reduced code readability. Such names fail to convey the variable or function's purpose, making it challenging for developers to understand the code's intent without deeper investigation. This can result in increased debugging time, higher susceptibility to errors, and difficulties in maintenance and collaboration. Descriptive naming, on the other hand, makes the code self-explanatory and easier to navigate, enhancing overall code quality and developer productivity.
|
||||
|
||||
```tsx
|
||||
// ❌ Bad, uses a generic name that doesn't communicate its
|
||||
// purpose or content clearly
|
||||
const [value, setValue] = useState('');
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, uses a descriptive name
|
||||
const [email, setEmail] = useState('');
|
||||
```
|
||||
|
||||
#### Some words to avoid in variable names
|
||||
|
||||
- dummy
|
||||
|
||||
### Event handlers
|
||||
|
||||
Event handler names should start with `handle`, `on` is a prefix used to name events in components props
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
const onEmailChange = (val: string) => {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good
|
||||
const handleEmailChange = (val: string) => {
|
||||
// ...
|
||||
};
|
||||
```
|
||||
|
||||
## Optional Props
|
||||
|
||||
Avoid supplying the default value for an optional prop, as it generally doesn’t contribute significantly.
|
||||
|
||||
EXAMPLE
|
||||
|
||||
Assume, we have the `EmailField` component defined below
|
||||
|
||||
```tsx
|
||||
type OwnProps = {
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
};
|
||||
|
||||
const EmailField = ({ value, disabled = false }: OwnProps) => (
|
||||
<TextInput value={value} disabled={disabled} fullWidth />
|
||||
);
|
||||
```
|
||||
|
||||
USAGE
|
||||
|
||||
```tsx
|
||||
// ❌ Bad, passing in the same value as the default value adds no value
|
||||
const Form = () => <EmailField value="username@email.com" disabled={false} />;
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, assumes the default value
|
||||
const Form = () => <EmailField value="username@email.com" />;
|
||||
```
|
||||
|
||||
## Component as props
|
||||
|
||||
Try as much as possible to pass uninstanciated components as props, so chilren can decide on their own of what props they need to pass.
|
||||
|
||||
The most common example for that is icon components :
|
||||
|
||||
```tsx
|
||||
const SomeParentComponent = () => <MyComponent Icon={MyIcon} />;
|
||||
|
||||
// In MyComponent
|
||||
const MyComponent = ({ MyIcon }: { MyIcon: IconComponent }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MyIcon size={theme.icon.size.md}>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
```
|
||||
|
||||
For React to understand that the component is a component, you need to use PascalCase, to later instanciate it with `<MyIcon>`
|
||||
|
||||
## Prop Drilling: Keep It Minimal
|
||||
|
||||
Prop drilling, in the React context, refers to the practice of passing state variables and their setters through multiple component layers, even if intermediary components don't use them. While sometimes necessary, excessive prop drilling can lead to:
|
||||
|
||||
1. **Decreased Readability**: Tracing where a prop originates or where it's utilized can become convoluted in a deeply nested component structure.
|
||||
|
||||
2. **Maintenance Challenges**: Changes in one component's prop structure might necessitate adjustments in several components, even if they don't directly use the prop.
|
||||
|
||||
3. **Reduced Component Reusability**: A component receiving numerous props solely for the purpose of passing them down becomes less general-purpose and harder to reuse in different contexts.
|
||||
|
||||
If you feel that you are using excessive prop drilling, see [state management best practices](/contributor/frontend/advanced/best-practices#state-management)
|
||||
|
||||
## Imports
|
||||
|
||||
When importing, opt for the designated aliases rather than specifying complete or relative paths.
|
||||
|
||||
THE ALIASES
|
||||
|
||||
```js
|
||||
{
|
||||
alias: {
|
||||
"~": path.resolve(__dirname, "src"),
|
||||
"@": path.resolve(__dirname, "src/modules"),
|
||||
"@testing": path.resolve(__dirname, "src/testing"),
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
USAGE
|
||||
```tsx
|
||||
// ❌ Bad, specifies the entire relative path
|
||||
import {
|
||||
CatalogDecorator
|
||||
} from '../../../../../testing/decorators/CatalogDecorator';
|
||||
import {
|
||||
ComponentDecorator
|
||||
} from '../../../../../testing/decorators/ComponentDecorator';
|
||||
```
|
||||
|
||||
```tsx
|
||||
// ✅ Good, utilises the designated aliases
|
||||
import { CatalogDecorator } from '~/testing/decorators/CatalogDecorator';
|
||||
import { ComponentDecorator } from '~/testing/decorators/ComponentDecorator';=
|
||||
```
|
||||
|
||||
## Breaking Changes
|
||||
|
||||
Prioritize thorough manual testing before proceeding to guarantee that modifications haven’t caused disruptions elsewhere, given that tests have not yet been extensively integrated.
|
||||
|
||||
24
docs/docs/contributor/frontend/advanced/hotkeys.mdx
Normal file
24
docs/docs/contributor/frontend/advanced/hotkeys.mdx
Normal file
@ -0,0 +1,24 @@
|
||||
---
|
||||
sidebar_position: 11
|
||||
sidebar_custom_props:
|
||||
icon: TbKeyboard
|
||||
---
|
||||
|
||||
# Hotkeys
|
||||
|
||||
You can intercept any hotkey combination and execute a custom action.
|
||||
|
||||
We added a thin wrapper on top of [react-hotkeys-hook](https://react-hotkeys-hook.vercel.app/docs/intro) to make it more performant and to avoid unnecessary re-renders.
|
||||
|
||||
We also created a wrapper hook `useScopedHotkeys` to make it easy to manage scopes.
|
||||
|
||||
```ts
|
||||
useScopedHotkeys(
|
||||
'ctrl+k,meta+k',
|
||||
() => {
|
||||
openCommandMenu();
|
||||
},
|
||||
AppHotkeyScope.CommandMenu,
|
||||
[openCommandMenu],
|
||||
);
|
||||
```
|
||||
292
docs/docs/contributor/frontend/advanced/style-guide.mdx
Normal file
292
docs/docs/contributor/frontend/advanced/style-guide.mdx
Normal file
@ -0,0 +1,292 @@
|
||||
---
|
||||
sidebar_position: 4
|
||||
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 />
|
||||
);
|
||||
```
|
||||
|
||||
#### No Single Variable Prop Spreading in JSX Elements
|
||||
|
||||
We discourage the use of single variable prop spreading in JSX elements, e.g., `{...props}`. This practice often leads to less readable and maintainable code as it's unclear what props are being passed down to the component.
|
||||
|
||||
```tsx
|
||||
/* ❌ - Bad, spreads a single variable prop into the underlying component
|
||||
*/
|
||||
const MyComponent = (props: OwnProps) => {
|
||||
return <OtherComponent {...props} />;
|
||||
}
|
||||
```
|
||||
|
||||
```tsx
|
||||
/* ✅ - Good, Explicitly lists all props
|
||||
* - Enhances readability and maintainability
|
||||
*/
|
||||
const MyComponent = ({ prop1, prop2, prop3 }: OwnProps) => {
|
||||
return <OtherComponent {...{ prop1, prop2, prop3 }} />;
|
||||
};
|
||||
```
|
||||
|
||||
Rationale:
|
||||
- It's clearer to see at a glance which props are being passed down, making the code easier to understand and maintain.
|
||||
- It helps to prevent tight coupling between components via their props.
|
||||
- It's easier to catch misspelled or unused props with linting tools when props are listed explicitly
|
||||
|
||||
## 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};
|
||||
`;
|
||||
```
|
||||
## Enforcing No-Type Imports
|
||||
|
||||
In our codebase, we've adopted a coding standard to disallow type imports. This helps maintain consistency and readability in our TypeScript code. To enforce this standard, we've added an ESLint rule that checks for and reports any type imports.
|
||||
|
||||
```tsx
|
||||
// ❌ Bad
|
||||
import { type Meta, type StoryObj } from '@storybook/react';
|
||||
|
||||
// ❌ Bad
|
||||
import type { Meta, StoryObj } from '@storybook/react';
|
||||
|
||||
// ✅ Good
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
```
|
||||
|
||||
### Why No-Type Imports?
|
||||
|
||||
- **Consistency**: By avoiding type imports, our codebase remains consistent in its module import style. We use a single approach for both type and value imports.
|
||||
|
||||
- **Readability**: No-type imports improve code readability by making it clear when you're importing values or types. This reduces ambiguity and makes it easier to understand the purpose of imported symbols.
|
||||
|
||||
- **Maintainability**: Codebase maintainability is enhanced because developers can quickly identify and locate type-only imports when reviewing or modifying code.
|
||||
|
||||
### ESLint Rule
|
||||
|
||||
We've configured an ESLint rule, `@typescript-eslint/consistent-type-imports`, to enforce the no-type import standard. This rule will generate errors or warnings for any type import violations.
|
||||
|
||||
Please note that this rule is intended to address rare edge cases where type imports might be used unintentionally. TypeScript itself discourages this practice, as mentioned in the [TypeScript 3.8 release notes](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-8.html). In most situations, you should not need to use type-only imports.
|
||||
|
||||
To ensure your code complies with this rule, make sure to run ESLint as part of your development workflow.
|
||||
7
docs/docs/contributor/frontend/basics/_category_.json
Normal file
7
docs/docs/contributor/frontend/basics/_category_.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "Basics",
|
||||
"position": 1,
|
||||
"customProps": {
|
||||
"icon": "TbTerminal2"
|
||||
}
|
||||
}
|
||||
10
docs/docs/contributor/frontend/basics/design-system.mdx
Normal file
10
docs/docs/contributor/frontend/basics/design-system.mdx
Normal file
@ -0,0 +1,10 @@
|
||||
---
|
||||
sidebar_position: 7
|
||||
sidebar_custom_props:
|
||||
icon: TbPaint
|
||||
---
|
||||
|
||||
# Design System
|
||||
|
||||
We rely on our internal and custom design system, that is built on top of styled-components.
|
||||
|
||||
116
docs/docs/contributor/frontend/basics/folder-architecture.mdx
Normal file
116
docs/docs/contributor/frontend/basics/folder-architecture.mdx
Normal file
@ -0,0 +1,116 @@
|
||||
---
|
||||
sidebar_position: 5
|
||||
sidebar_custom_props:
|
||||
icon: TbFolder
|
||||
---
|
||||
|
||||
# Folder Architecture
|
||||
|
||||
In this guide, you will explore the details of the project directory structure and how it contributes to the organization and maintainability of Twenty.
|
||||
|
||||
By following this folder architecture convention, it is easier to find the files related to specific features and ensure that the application is scalable and maintainable.
|
||||
|
||||
```
|
||||
front
|
||||
└───modules
|
||||
│ └───module1
|
||||
│ │ └───submodule1
|
||||
│ └───module2
|
||||
│ └───ui
|
||||
│ │ └───buttons
|
||||
└───pages
|
||||
└───...
|
||||
```
|
||||
|
||||
## Pages
|
||||
|
||||
Contains the top-level components that are defined by the application routes. They import more low-level components from the modules folder (more details below).
|
||||
|
||||
## Modules
|
||||
|
||||
Each module represents a feature or a group of feature, comprising its specific components, states, and operational logic.
|
||||
They should all follow the structure below. You can nest modules within modules; We often refer to them as submodules but the same rules apply.
|
||||
|
||||
```
|
||||
module1
|
||||
└───components
|
||||
│ └───component1
|
||||
│ └───component2
|
||||
└───constants
|
||||
└───contexts
|
||||
└───graphql
|
||||
│ └───fragments
|
||||
│ └───queries
|
||||
│ └───mutations
|
||||
└───hooks
|
||||
│ └───internal
|
||||
└───states
|
||||
│ └───recoil-scope-contexts
|
||||
│ └───selectors
|
||||
└───types
|
||||
└───utils
|
||||
```
|
||||
|
||||
### Contexts
|
||||
|
||||
A context is a way to pass data through the component tree without having to pass props down manually at every level.
|
||||
|
||||
See [React Context](https://react.dev/reference/react#context-hooks) for more details
|
||||
|
||||
### States
|
||||
|
||||
Contains the state management logic. We use [RecoilJS](https://recoiljs.org) for this.
|
||||
|
||||
- Selectors
|
||||
|
||||
See [RecoilJS Selectors](https://recoiljs.org/docs/basic-tutorial/selectors) for more details.
|
||||
|
||||
- Recoil Scope Contexts
|
||||
|
||||
More details will be added soon.
|
||||
|
||||
We still use React's built-in state management for state that is only used within a component.
|
||||
|
||||
### Hooks
|
||||
|
||||
See [Hooks](https://react.dev/learn/reusing-logic-with-custom-hooks) for more details.
|
||||
|
||||
### Utils
|
||||
|
||||
Should only contain reusable pure functions. Otherwise, create custom hooks in the `hooks` folder.
|
||||
|
||||
### GraphQL
|
||||
|
||||
Includes fragments, queries, and mutations.
|
||||
|
||||
See [GraphQL](https://graphql.org/learn/) for more details.
|
||||
|
||||
- Fragments
|
||||
|
||||
A fragment is a reusable piece of a query, which can be used in multiple places. By using fragments, it is easier to avoid duplicating code.
|
||||
|
||||
See [GraphQL Fragments](https://graphql.org/learn/queries/#fragments) for more details.
|
||||
|
||||
- Queries
|
||||
|
||||
See [GraphQL Queries](https://graphql.org/learn/queries/) for more details.
|
||||
|
||||
- Mutations
|
||||
|
||||
See [GraphQL Mutations](https://graphql.org/learn/queries/#mutations) for more details.
|
||||
|
||||
## UI
|
||||
|
||||
Contains all of the reusable UI components used in the application.
|
||||
|
||||
This folder can contain subfolders for specific types of components, such as `buttons`, `inputs`, or `modals`. Each component should be self-contained and reusable, so that it can be used in multiple parts of the application.
|
||||
|
||||
By separating the UI components from the other components in the `modules` folder, it is easier to maintain a consistent design and to make changes to the UI without affecting other parts (business logic) of the codebase.
|
||||
|
||||
## Interface and dependencies
|
||||
|
||||
You can import other module code from any module except for the `ui` folder. This will keep its code easy to test.
|
||||
|
||||
### Internal
|
||||
|
||||
Each part (hooks, states, ...) of a module can have an `internal` folder, which contains parts that are only used within the module.
|
||||
52
docs/docs/contributor/frontend/basics/overview.mdx
Normal file
52
docs/docs/contributor/frontend/basics/overview.mdx
Normal file
@ -0,0 +1,52 @@
|
||||
---
|
||||
sidebar_position: 0
|
||||
sidebar_custom_props:
|
||||
icon: TbEyeglass
|
||||
---
|
||||
|
||||
# Overview
|
||||
|
||||
## Tech Stack
|
||||
|
||||
We took care of having a clean and simple stack, with minimal boilerplate code.
|
||||
|
||||
**App**
|
||||
|
||||
- [React](https://react.dev/)
|
||||
- [Apollo](https://www.apollographql.com/docs/)
|
||||
- [GraphQL Codegen](https://the-guild.dev/graphql/codegen)
|
||||
- [Recoil](https://recoiljs.org/docs/introduction/core-concepts)
|
||||
- [TypeScript](https://www.typescriptlang.org/)
|
||||
|
||||
**Testing**
|
||||
|
||||
- [Jest](https://jestjs.io/)
|
||||
- [Storybook](https://storybook.js.org/)
|
||||
|
||||
**Tooling**
|
||||
|
||||
- [Yarn](https://yarnpkg.com/)
|
||||
- [Craco](https://craco.js.org/docs/)
|
||||
- [ESLint](https://eslint.org/)
|
||||
|
||||
## Architecture
|
||||
|
||||
### Routing
|
||||
|
||||
We use [React Router](https://reactrouter.com/) for routing.
|
||||
|
||||
To avoid unnecessary [re-renders](/contributor/frontend/advanced/best-practices#managing-re-renders) we handle all the routing logic in a `useEffect` in `PageChangeEffect`.
|
||||
|
||||
### State Management
|
||||
|
||||
We use [Recoil](https://recoiljs.org/docs/introduction/core-concepts) for state management.
|
||||
|
||||
See our [best practices](/contributor/frontend/advanced/best-practices#state-management) for more managing state.
|
||||
|
||||
## Testing
|
||||
|
||||
We use [Jest](https://jestjs.io/) for unit testing and [Storybook](https://storybook.js.org/) for component testing.
|
||||
|
||||
Jest is mainly used for testing utility functions, and not components themselves.
|
||||
|
||||
Storybook is used for testing the behavior of isolated components, as well as displaying our [design system](/contributor/frontend/basics/design-system).
|
||||
59
docs/docs/contributor/frontend/basics/work-with-figma.mdx
Normal file
59
docs/docs/contributor/frontend/basics/work-with-figma.mdx
Normal file
@ -0,0 +1,59 @@
|
||||
---
|
||||
sidebar_position: 2
|
||||
sidebar_custom_props:
|
||||
icon: TbBrandFigma
|
||||
---
|
||||
|
||||
# Work with figma
|
||||
|
||||
Figma is a collaborative interface design tool that aids in bridging the communication barrier between designers and developers.
|
||||
In this guide, we'll go over how to collaborate with Twenty’s Figma.
|
||||
|
||||
## Access
|
||||
|
||||
1. **Access the shared link:** you can access the Twenty Figma file with this link: https://www.figma.com/file/xt8O9mFeLl46C5InWwoMrN/Twenty
|
||||
2. **Sign in:** If you're not already signed in to Figma, you'll be prompted to do so.
|
||||
Key features are only available to logged-in users, such as the developer mode and the ability to select a dedicated frame.
|
||||
**You will not be able to collaborate effectively without an account.**
|
||||
|
||||
## Figma structure
|
||||
|
||||
On the left sidebar, you can access the different pages of our Figma. They are organised as such:
|
||||
|
||||
- **Components page:** This is the first page. The designer uses it to create and organize the reusable design elements that are used throughout the design file. For example, buttons, icons, symbols, or any other reusable components. It serves to maintain consistency across the design.
|
||||
- **Main page:** The second page is the main page, where the complete user interface of the project is shown. You can press ***Play*** to use the full app prototype.
|
||||
- **Features pages:** The subsequent pages are typically dedicated to features being worked on. They contain the design of specific features or modules of the application or website. They are usually still being worked on.
|
||||
|
||||
## Useful Tips
|
||||
|
||||
With read-only access, you can't edit the design but you can access all features that will be useful to implement the designs into code.
|
||||
|
||||
### Use the Dev mode
|
||||
|
||||
Figma's Dev Mode enhances developers' productivity by providing easy design navigation, effective asset management, efficient communication tools, toolbox integrations, quick code snippets, and key layer information, bridging the gap between design and development. learn more at https://www.figma.com/dev-mode/
|
||||
|
||||
Switch to the "Developer" mode in the right part of the toolbar to see design specs, copy CSS, and access assets.
|
||||
|
||||
### Use the Prototype
|
||||
|
||||
Click on any element on the canvas and press the “Play” button at the top right edge of the interface to access the prototype view. Prototype mode allows you to interact with the design as if it were the final product. It demonstrates the flow between screens and how interface elements like buttons, links, or menus behave when interacted with.
|
||||
|
||||
1. **Understanding transitions and animations:** In the Prototype mode, any transitions or animations added by a designer between screens or UI elements can be viewed, providing clear visual instructions to developers on the intended behavior and style.
|
||||
2. **Implementation Clarification:** A prototype can also be used to reduce ambiguities. Developers can interact with it to gain a better understanding of the functionality or appearance of particular elements.
|
||||
|
||||
For more comprehensive details and guidance on learning the Figma platform, you can visit the official Figma Documentation: https://help.figma.com/hc/en-us
|
||||
|
||||
### Measure distances
|
||||
|
||||
Select an element, hold `Option` key (Mac) or `Alt` key (Windows), then hover over another element to see the distance between them.
|
||||
|
||||
### Figma extension for VSCode (Recommended)
|
||||
|
||||
[Figma for VS Code](https://marketplace.visualstudio.com/items?itemName=figma.figma-vscode-extension)
|
||||
lets you navigate and inspect design files, collaborate with designers, track changes, and speed up implementation - all without leaving your text editor.
|
||||
It is part of our recommended extensions.
|
||||
|
||||
## Collaboration
|
||||
|
||||
1. **Using Comments:** You are welcome to use the comment feature by clicking on the bubble icon in the left part of the toolbar.
|
||||
2. **Cursor chat:** A nice feature of Figma is the Cursor chat. Just press `;` on Mac and `/` on Windows to send a message if you see someone else using Figma as the same time as you.
|
||||
43
docs/docs/contributor/frontend/basics/workflows.mdx
Normal file
43
docs/docs/contributor/frontend/basics/workflows.mdx
Normal file
@ -0,0 +1,43 @@
|
||||
---
|
||||
title: Contributing
|
||||
sidebar_position: 1
|
||||
sidebar_custom_props:
|
||||
icon: TbTopologyStar
|
||||
---
|
||||
|
||||
## Pre-requesites
|
||||
|
||||
Make sure that your [IDE is correctly setup](/contributor/local-setup/ide-setup) and that your backend is running on `localhost:3000`.
|
||||
|
||||
|
||||
## Starting a new feature
|
||||
|
||||
Make sure your database is running on the URL provided in your `server/.env` file.
|
||||
|
||||
```
|
||||
cd front
|
||||
yarn
|
||||
|
||||
yarn start
|
||||
```
|
||||
|
||||
## Regenerate graphql schema based on API graphql schema
|
||||
|
||||
```
|
||||
yarn graphql:generate
|
||||
```
|
||||
|
||||
## Lint
|
||||
|
||||
```
|
||||
yarn lint
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```
|
||||
yarn test # run jest tests
|
||||
yarn storybook:dev # run storybook
|
||||
yarn storybook:test # run tests (needs yarn storybook:dev to be running)
|
||||
yarn storybook:coverage # run tests (needs yarn storybook:dev to be running)
|
||||
```
|
||||
12
docs/docs/contributor/frontend/frontend.mdx
Normal file
12
docs/docs/contributor/frontend/frontend.mdx
Normal file
@ -0,0 +1,12 @@
|
||||
---
|
||||
title: Frontend Development
|
||||
displayed_sidebar: frontendSidebar
|
||||
sidebar_position: 0
|
||||
sidebar_custom_props:
|
||||
icon: TbTerminal2
|
||||
isSidebarRoot: true
|
||||
---
|
||||
|
||||
Welcome to the Frontend Development section of the documentation.
|
||||
Here you will find information about the frontend development process, the tools we use, and the best practices we follow.
|
||||
|
||||
@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "UI Components",
|
||||
"position": 1,
|
||||
"customProps": {
|
||||
"icon": "TbTerminal2"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
{
|
||||
"label": "Forms",
|
||||
"position": 2,
|
||||
"collapsible": true,
|
||||
"collapsed": true,
|
||||
"customProps": {
|
||||
"icon": "TbForms"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
---
|
||||
title: Button
|
||||
sidebar_position: 1
|
||||
---
|
||||
|
||||
import IframeResizer from 'iframe-resizer-react';
|
||||
|
||||
<IframeResizer
|
||||
checkOrigin={false}
|
||||
inPageLinks
|
||||
src="http://storybook.twenty.com/iframe.html?id=ui-button-button--docs&viewMode=docs&singleStory=true"
|
||||
style={{ width: '1px', minWidth: '100%'}}
|
||||
/>
|
||||
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Overview
|
||||
sidebar_position: 0
|
||||
sidebar_custom_props:
|
||||
icon: TbBrandFigma
|
||||
---
|
||||
|
||||
WIP
|
||||
|
||||
20
docs/docs/contributor/glossary.mdx
Normal file
20
docs/docs/contributor/glossary.mdx
Normal file
@ -0,0 +1,20 @@
|
||||
---
|
||||
title: Glossary
|
||||
sidebar_position: 2
|
||||
sidebar_custom_props:
|
||||
icon: TbVocabulary
|
||||
---
|
||||
|
||||
### Workspace
|
||||
A `Workspace` usually represents a company using Twenty.
|
||||
It is attached to a single domain name, which is usually the domain name your company uses for employee email addresses.
|
||||
|
||||
### Company & People
|
||||
They are the two fundamental types of records that the CRM is built around:
|
||||
- A `Company` represents a business or organization.
|
||||
- `People` represent your company's current and prospective customers or clients.
|
||||
|
||||
### Pipelines
|
||||
A `Pipeline` is a way to track a business process. Pipelines are categorized within a *module* and have *stages*:
|
||||
- A **module** contains the logic for a certain business process (e.g. sales, recruiting).
|
||||
- **Stages** map the steps in your process (e.g. new, ongoing, won, lost).
|
||||
9
docs/docs/contributor/local-setup/_category_.json
Normal file
9
docs/docs/contributor/local-setup/_category_.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"label": "Local setup",
|
||||
"position": 1,
|
||||
"collapsible": true,
|
||||
"collapsed": true,
|
||||
"customProps": {
|
||||
"icon": "TbDeviceDesktop"
|
||||
}
|
||||
}
|
||||
107
docs/docs/contributor/local-setup/docker-setup.mdx
Normal file
107
docs/docs/contributor/local-setup/docker-setup.mdx
Normal file
@ -0,0 +1,107 @@
|
||||
---
|
||||
title: Docker Setup
|
||||
sidebar_position: 2
|
||||
sidebar_custom_props:
|
||||
icon: TbBrandDocker
|
||||
---
|
||||
|
||||
You can also provision the project with Docker. This comes with a few advantages:
|
||||
- It provides the exact same environment as our core developer team.
|
||||
- It includes some additional dependencies (such as `playwright`) that you might need if you wish to contribute to some advanced areas of the project.
|
||||
- It provisions a PostgreSQL database.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Make sure you have the latest `Docker` and [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) versions installed on your computer.
|
||||
|
||||
You can run `docker --version` to verify the installation.
|
||||
|
||||
## Step #1: Git Clone
|
||||
In your terminal, run the following command:
|
||||
|
||||
```bash
|
||||
git clone git@github.com:twentyhq/twenty.git
|
||||
```
|
||||
|
||||
## Step #2: Setup env variables
|
||||
|
||||
Twenty requires a few environment variables to be set. Locally, we recommend setting them through `.env` files.
|
||||
|
||||
```bash
|
||||
cp ./front/.env.example ./front/.env
|
||||
cp ./server/.env.example ./server/.env
|
||||
```
|
||||
|
||||
The default values should work out of the box, except for the postgres URL, which requires a small modification.
|
||||
|
||||
Open `./server/.env` and change to the following:
|
||||
|
||||
```bash
|
||||
PG_DATABASE_URL=postgres://twenty:twenty@postgres:5432/default?connection_limit=1
|
||||
```
|
||||
|
||||
|
||||
## Step #3: Build
|
||||
|
||||
We provide an environment containerized with Docker and orchestrated with `docker-compose`.
|
||||
This installation method will also provision a PostgreSQL container.
|
||||
|
||||
**Note:** The configuration for the build is stored in the `infra/dev` folder, but you can run `make` commands directly from the root folder.
|
||||
|
||||
```bash
|
||||
make build
|
||||
```
|
||||
|
||||
## Step #4: Migrate & seed
|
||||
|
||||
Before running the project, you need to initialize the database by running the migrations and seed.
|
||||
|
||||
Start the containers:
|
||||
```bash
|
||||
make up
|
||||
```
|
||||
|
||||
Run database migrations, generate prisma client, and seed:
|
||||
```bash
|
||||
make server-prisma-reset
|
||||
```
|
||||
|
||||
## Step #5: Start Twenty
|
||||
|
||||
Run the project with the following commands from the `root folder`:
|
||||
|
||||
```bash
|
||||
make server-start
|
||||
```
|
||||
|
||||
```bash
|
||||
make front-start
|
||||
```
|
||||
|
||||
You should now have:
|
||||
- **Frontend** available on: [http://localhost:3001](http://localhost:3001)
|
||||
- **Server** available on: [http://localhost:3000/graphql](http://localhost:3000/graphql)
|
||||
- **Postgres** available on [http://localhost:5432](http://localhost:5432) and containing database named `default`
|
||||
|
||||
Sign in using our seeded demo account `tim@apple.dev` (password: `Applecar2025`) to start using Twenty
|
||||
|
||||
### Optional
|
||||
|
||||
If you don't want to use the `make` command and work directly from the container, you can also ssh directly into the container:
|
||||
|
||||
```bash
|
||||
make sh
|
||||
```
|
||||
Then run commands through yarn:
|
||||
```bash
|
||||
cd server
|
||||
yarn prisma:reset
|
||||
```
|
||||
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
#### Docker throws errors while setting up local environment
|
||||
|
||||
If by any chance you will run into problems with Docker, you should change the `docker-compose` to `docker compose` in `./infra/dev/Makefile` as `docker-compose` is old version
|
||||
becoming slowly obsolete. (More info can be found [here](https://docs.docker.com/compose/migrate/))
|
||||
51
docs/docs/contributor/local-setup/ide-setup.mdx
Normal file
51
docs/docs/contributor/local-setup/ide-setup.mdx
Normal file
@ -0,0 +1,51 @@
|
||||
---
|
||||
title: IDE Setup
|
||||
sidebar_position: 4
|
||||
sidebar_custom_props:
|
||||
icon: TbBrandVscode
|
||||
---
|
||||
|
||||
This section will help you setup your IDE for the project. If you haven't setup your development environment, please refer to [Development Environment](/contributor/local-setup) section.
|
||||
|
||||
You can obviously use any IDE you want but we recommend using Visual Studio Code as our internal team uses it and we have a lot of extensions and settings that we can share with you.
|
||||
|
||||
|
||||
## Visual Studio Code
|
||||
|
||||
### Installation
|
||||
|
||||
You can download Visual Studio Code from [here](https://code.visualstudio.com/download). Depending on your operating system, you can download the appropriate version.
|
||||
|
||||
### Open Project
|
||||
|
||||
Once you have installed Visual Studio Code, you can open the project by clicking on `File > Open Folder` and selecting `twenty` project root folder.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/img/contributor/ide-project-open.png" alt="Visual Studio Code: Open Twenty project" width="90%" />
|
||||
</div>
|
||||
|
||||
### Extensions
|
||||
|
||||
You can use the recommended extensions for the project. You will find them in `.vscode/extensions.json` file. VSCode should prompt you to install the recommended extensions when you open the project.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/img/contributor/ide-extensions.png" alt="Visual Studio Code: Install recommended extensions" width="90%" />
|
||||
</div>
|
||||
|
||||
|
||||
### Docker Setup
|
||||
|
||||
If you are using a [Docker setup](/contributor/local-setup#docker-install), you will need to run VSCode in the container. You can do that by opening the project, clicking on `Remote Explorer` icon on the left sidebar and then clicking on `Attach in New window` on `dev-twenty-dev` container.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/img/contributor/ide-start-dev-container.png" alt="Visual Studio Code: Open in container" width="90%" />
|
||||
</div>
|
||||
|
||||
<br />
|
||||
VSCode will open a new window and you will be able to use it as you would normally do. The only difference is that you will be running VSCode inside the container and you will have access to all the tools and dependencies that are installed in the container.
|
||||
|
||||
If you stop your containers, you will need to start them again before opening the project in VSCode again.
|
||||
|
||||
### Conclusion
|
||||
|
||||
You are all set to start developing the project. If you have any questions, feel free to reach out to us on [Discord](https://discord.com/invite/cx5n4Jzs57).
|
||||
42
docs/docs/contributor/local-setup/local-setup.mdx
Normal file
42
docs/docs/contributor/local-setup/local-setup.mdx
Normal file
@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Local Setup
|
||||
sidebar_position: 0
|
||||
sidebar_custom_props:
|
||||
icon: TbDeviceDesktop
|
||||
---
|
||||
import ThemedImage from '@theme/ThemedImage';
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
Twenty is designed to be developer-friendly, and your local installation should be up and running in a few minutes.
|
||||
|
||||
## Discord
|
||||
|
||||
If you have any questions or need help, you can join our [Discord](https://discord.com/invite/cx5n4Jzs57) server.
|
||||
|
||||
## MacOS and Linux users
|
||||
|
||||
We recommend using [yarn installation](/contributor/local-setup/yarn-setup) as this is the easiest way to get started.
|
||||
We also provide an easy way to run the project with [Docker](/contributor/local-setup/yarn-setup) that you can use if you are familiar with containerized environments.
|
||||
|
||||
## Windows users
|
||||
|
||||
Windows users can install install the project through WSL2. We provide a [guide](/contributor/local-setup/wsl-setup) to help you get started.
|
||||
|
||||
## Project structure
|
||||
|
||||
The repository is structured as follows:
|
||||
```
|
||||
twenty
|
||||
└───docs // contains this documentation
|
||||
└───front // contains the frontend code for the application
|
||||
└───server // contains the backend code for the application
|
||||
└───infra // contains docker configurations for development and production deployments
|
||||
```
|
||||
|
||||
## IDE Setup
|
||||
|
||||
Once Twnenty is running on your computer, you will get the best experience by using an IDE that supports TypeScript and ESLint.
|
||||
You will find a guide for [VSCode](/contributor/local-setup/ide-setup) further in the documentation.
|
||||
___
|
||||
|
||||
21
docs/docs/contributor/local-setup/troubleshooting.mdx
Normal file
21
docs/docs/contributor/local-setup/troubleshooting.mdx
Normal file
@ -0,0 +1,21 @@
|
||||
---
|
||||
title: Troubleshooting
|
||||
sidebar_position: 5
|
||||
sidebar_custom_props:
|
||||
icon: TbExclamationCircle
|
||||
---
|
||||
|
||||
|
||||
## Windows setup eslint prettier error: `CR` line breaks found
|
||||
|
||||
This is due to the linebreak characters of windows and the git configuration. Try running:
|
||||
```
|
||||
git config --global core.autocrlf false
|
||||
```
|
||||
|
||||
Then delete the repository and clone it again.
|
||||
|
||||
|
||||
## Yarn lock file changed and new files are created (`yarn.lock`, `.yarnrc.yml`, `.yarn`)
|
||||
|
||||
Maybe you are using yarn 3? Try installing [yarn classic](https://classic.yarnpkg.com/lang/en/)!
|
||||
58
docs/docs/contributor/local-setup/wsl-setup.mdx
Normal file
58
docs/docs/contributor/local-setup/wsl-setup.mdx
Normal file
@ -0,0 +1,58 @@
|
||||
---
|
||||
title: Windows WSL Setup
|
||||
sidebar_position: 3
|
||||
sidebar_custom_props:
|
||||
icon: TbBrandWindows
|
||||
---
|
||||
|
||||
## Install WSL
|
||||
|
||||
Open PowerShell as Administrator and run:
|
||||
|
||||
Install WSL. Follow https://learn.microsoft.com/en-us/windows/wsl/install
|
||||
|
||||
```powershell
|
||||
wsl --install
|
||||
```
|
||||
|
||||
You should be prompted to restart your computer. If not, restart it manually.
|
||||
|
||||
Upon restart, a powershell window will open and install Ubuntu. This may take a few minutes.
|
||||
You will be prompted to create a username and password for your Ubuntu installation.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src="/img/contributor/wsl-complete.png" alt="Visual Studio Code: Open in container" width="90%" />
|
||||
</div>
|
||||
|
||||
## Setup your developer environment
|
||||
|
||||
### Install Git
|
||||
|
||||
Follow: https://learn.microsoft.com/en-us/windows/wsl/tutorials/wsl-git
|
||||
|
||||
```
|
||||
sudo apt-get install git
|
||||
```
|
||||
Then, configure your git user name and email using the following commands, replacing name and email with your own. These details will be associated with any commits that you create:
|
||||
|
||||
```
|
||||
git config --global user.name "Your Name"
|
||||
git config --global user.email "youremail@domain.com"
|
||||
```
|
||||
|
||||
**Note:$$ If you don't have a Github account, create one now with the corresponding email address. We recommend that you setup a SSH key for your Github account. Follow the instructions here: https://docs.github.com/fr/authentication/connecting-to-github-with-ssh/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent
|
||||
|
||||
### Install Node.js, nvm, Yarn
|
||||
|
||||
```bash
|
||||
sudo apt-get install curl
|
||||
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/master/install.sh | bash
|
||||
curl -o- -L https://yarnpkg.com/install.sh | bash
|
||||
```
|
||||
|
||||
Close and reopen your terminal to start using nvm or run the following to use it now:
|
||||
|
||||
### Install Twenty project
|
||||
|
||||
You are ready to install Twenty project. Follow the [Yarn install guide](/contributor/local-setup#yarn-install-recommended) instructions.
|
||||
We don't recommend to use Docker on WSL as it adds an extra layer of complexity.
|
||||
133
docs/docs/contributor/local-setup/yarn-setup.mdx
Normal file
133
docs/docs/contributor/local-setup/yarn-setup.mdx
Normal file
@ -0,0 +1,133 @@
|
||||
---
|
||||
title: Yarn Setup
|
||||
sidebar_position: 1
|
||||
sidebar_custom_props:
|
||||
icon: TbScript
|
||||
---
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
|
||||
**Note:** `npm` currently does not support local packages satisfactorily. We strongly recommend using `yarn` instead.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you can install and use Twenty, make sure you install the following on your computer:
|
||||
- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
||||
- [Node](https://nodejs.org/en/download)
|
||||
- [yarn](https://classic.yarnpkg.com/lang/en/docs/install/)
|
||||
|
||||
---
|
||||
|
||||
## Step #1: Git Clone
|
||||
|
||||
In your terminal, run the following command:
|
||||
|
||||
```
|
||||
git clone git@github.com:twentyhq/twenty.git
|
||||
```
|
||||
|
||||
|
||||
## Step #2: Set up PostgreSQL Database
|
||||
You need to have a PostgreSQL database available to be able to use Twenty. If you already have one available, you can skip this step.
|
||||
|
||||
If you don't, you can provision one through `docker` using the following commands:
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="docker" label="Docker" default>
|
||||
|
||||
```bash
|
||||
cd twenty
|
||||
make provision-postgres
|
||||
```
|
||||
|
||||
This will create a Docker container, exposing a PostgresSQL instance at [http://localhost:5432](http://localhost:5432).
|
||||
|
||||
This instance contains two databases: `default` and `test`
|
||||
You can access them using `twenty` postgres user (password: `twenty`)
|
||||
</TabItem>
|
||||
<TabItem value="linux-wsl" label="Linux / Windows WSL">
|
||||
|
||||
To install PostgresSQL on WSL2, use the following commands:
|
||||
|
||||
```bash
|
||||
sudo apt update
|
||||
sudo apt install postgresql postgresql-contrib
|
||||
```
|
||||
|
||||
Start postgresql service and connect to the database using default `postgres` user:
|
||||
|
||||
```bash
|
||||
sudo service postgresql start
|
||||
sudo -u postgres psql
|
||||
```
|
||||
|
||||
Create two databases:
|
||||
|
||||
```sql
|
||||
CREATE DATABASE "default";
|
||||
CREATE DATABASE "test";
|
||||
```
|
||||
|
||||
Create a user `twenty` with password `twenty`:
|
||||
|
||||
```sql
|
||||
CREATE USER twenty PASSWORD 'twenty';
|
||||
ALTER USER twenty CREATEDB;
|
||||
```
|
||||
|
||||
Create `metadata` schema:
|
||||
```sql
|
||||
CREATE SCHEMA IF NOT EXISTS "metadata";
|
||||
GRANT ALL ON SCHEMA metadata TO twenty;
|
||||
```
|
||||
|
||||
Activate `uuid-ossp` extension:
|
||||
```sql
|
||||
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
|
||||
## Step #3: Setup env variables
|
||||
|
||||
Twenty requires a few environment variables to be set. Locally, we recommend setting them through a `.env` file.
|
||||
|
||||
To do so, make copies of the `.env.example` files in `/front` and `/server`:
|
||||
```bash
|
||||
cp ./front/.env.example ./front/.env
|
||||
cp ./server/.env.example ./server/.env
|
||||
```
|
||||
|
||||
## Step #4: Server setup
|
||||
|
||||
**Note:** We recommend that you use `nvm` to install the correct `node` version. We have added a `server/.nvmrc` to ensure all contributors are using the same version.
|
||||
|
||||
To build Twenty server and seed some data into your database, run the following commands:
|
||||
```bash
|
||||
cd server
|
||||
nvm install #recommended
|
||||
nvm use #recommended
|
||||
yarn
|
||||
yarn prisma:reset
|
||||
yarn start:dev
|
||||
```
|
||||
|
||||
Twenty's server will be up and running at [http://localhost:3000](http://localhost:3000).
|
||||
|
||||
## Step #5: Frontend setup
|
||||
|
||||
**Note:** For the frontend setup, too, we recommend using `nvm` to install the right node version.
|
||||
To set up the frontend, run the following commands in your terminal:
|
||||
|
||||
```bash
|
||||
cd ../front
|
||||
nvm install #recommended
|
||||
nvm use #recommended
|
||||
yarn
|
||||
yarn start
|
||||
```
|
||||
|
||||
Twenty's frontend will be running at [http://localhost:3001](http://localhost:3001). Simply login using our seeded demo account: `tim@apple.dev` to start using Twenty.
|
||||
|
||||
3
docs/docs/contributor/server/_category_.json
Normal file
3
docs/docs/contributor/server/_category_.json
Normal file
@ -0,0 +1,3 @@
|
||||
{
|
||||
"position": 4
|
||||
}
|
||||
7
docs/docs/contributor/server/basics/_category_.json
Normal file
7
docs/docs/contributor/server/basics/_category_.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"label": "Basics",
|
||||
"position": 1,
|
||||
"customProps": {
|
||||
"icon": "TbTerminal2"
|
||||
}
|
||||
}
|
||||
9
docs/docs/contributor/server/basics/overview.mdx
Normal file
9
docs/docs/contributor/server/basics/overview.mdx
Normal file
@ -0,0 +1,9 @@
|
||||
---
|
||||
title: Overview
|
||||
sidebar_position: 0
|
||||
sidebar_custom_props:
|
||||
icon: TbEyeglass
|
||||
---
|
||||
|
||||
|
||||
WIP
|
||||
44
docs/docs/contributor/server/basics/workflows.mdx
Normal file
44
docs/docs/contributor/server/basics/workflows.mdx
Normal file
@ -0,0 +1,44 @@
|
||||
---
|
||||
title: Development workflow
|
||||
sidebar_position: 2
|
||||
sidebar_custom_props:
|
||||
icon: TbTopologyStar
|
||||
---
|
||||
|
||||
## First time setup
|
||||
|
||||
```
|
||||
cd server
|
||||
yarn # install dependencies
|
||||
|
||||
yarn prisma:migrate # run migrations
|
||||
yarn prisma:generate # generate prisma and nestjs-graphql schemas
|
||||
yarn prisma:seed # provision database with seeds
|
||||
|
||||
# alternatively, you can run
|
||||
yarn prisma:reset # all-in-one command to reset, migrate, seed and generate schemas
|
||||
```
|
||||
|
||||
## Starting a new feature
|
||||
|
||||
Make sure your database is running on the URL provided in your `server/.env` file.
|
||||
|
||||
```
|
||||
cd server
|
||||
yarn
|
||||
yarn prisma:migrate && yarn prisma:generate
|
||||
|
||||
yarn start:dev
|
||||
```
|
||||
|
||||
## Lint
|
||||
|
||||
```
|
||||
yarn lint
|
||||
```
|
||||
|
||||
## Test
|
||||
|
||||
```
|
||||
yarn test
|
||||
```
|
||||
8
docs/docs/contributor/server/server.mdx
Normal file
8
docs/docs/contributor/server/server.mdx
Normal file
@ -0,0 +1,8 @@
|
||||
---
|
||||
title: Backend Development
|
||||
displayed_sidebar: backendSidebar
|
||||
sidebar_position: 0
|
||||
sidebar_custom_props:
|
||||
icon: TbTerminal2
|
||||
isSidebarRoot: true
|
||||
---
|
||||
Reference in New Issue
Block a user