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:
Charles Bochet
2023-10-08 13:21:54 +02:00
committed by GitHub
parent 7b6ee4e0bf
commit edc060fce7
61 changed files with 872 additions and 1011 deletions

View File

@ -0,0 +1,4 @@
{
"label": "Contributor guide",
"position": 3
}

View File

@ -0,0 +1,3 @@
{
"position": 3
}

View File

@ -0,0 +1,7 @@
{
"label": "Advanced",
"position": 3,
"customProps": {
"icon": "TbTerminal2"
}
}

View 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 doesnt 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 havent caused disruptions elsewhere, given that tests have not yet been extensively integrated.

View 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],
);
```

View 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 its 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.

View File

@ -0,0 +1,7 @@
{
"label": "Basics",
"position": 1,
"customProps": {
"icon": "TbTerminal2"
}
}

View 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.

View 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.

View 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).

View 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 Twentys 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.

View 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)
```

View 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.

View File

@ -0,0 +1,7 @@
{
"label": "UI Components",
"position": 1,
"customProps": {
"icon": "TbTerminal2"
}
}

View File

@ -0,0 +1,9 @@
{
"label": "Forms",
"position": 2,
"collapsible": true,
"collapsed": true,
"customProps": {
"icon": "TbForms"
}
}

View File

@ -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%'}}
/>

View File

@ -0,0 +1,9 @@
---
title: Overview
sidebar_position: 0
sidebar_custom_props:
icon: TbBrandFigma
---
WIP

View 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).

View File

@ -0,0 +1,9 @@
{
"label": "Local setup",
"position": 1,
"collapsible": true,
"collapsed": true,
"customProps": {
"icon": "TbDeviceDesktop"
}
}

View 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/))

View 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).

View 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.
___

View 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/)!

View 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.

View 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.

View File

@ -0,0 +1,3 @@
{
"position": 4
}

View File

@ -0,0 +1,7 @@
{
"label": "Basics",
"position": 1,
"customProps": {
"icon": "TbTerminal2"
}
}

View File

@ -0,0 +1,9 @@
---
title: Overview
sidebar_position: 0
sidebar_custom_props:
icon: TbEyeglass
---
WIP

View 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
```

View File

@ -0,0 +1,8 @@
---
title: Backend Development
displayed_sidebar: backendSidebar
sidebar_position: 0
sidebar_custom_props:
icon: TbTerminal2
isSidebarRoot: true
---