8
8
9
9
## Running the UI
10
10
11
- You can run the UI and access the dashboard in two ways:
11
+ You can run the UI and access the Coder dashboard in two ways:
12
12
13
13
- Build the UI pointing to an external Coder server:
14
14
` CODER_HOST=https://mycoder.com pnpm dev ` inside of the ` site ` folder. This is
15
15
helpful when you are building something in the UI and already have the data on
16
16
your deployed server.
17
17
- Build the entire Coder server + UI locally: ` ./scripts/develop.sh ` in the root
18
- folder. It is useful when you have to contribute with features that are not
19
- deployed yet or when you have to work on both, frontend and backend.
18
+ folder. This is useful for contributing to features that are not deployed yet
19
+ or that involve both the frontend and backend.
20
20
21
21
In both cases, you can access the dashboard on ` http://localhost:8080 ` . If you
22
- are running the ` ./scripts/develop.sh ` you can log in using the default
23
- credentials: ` admin@coder.com ` and ` SomeSecurePassword! ` .
22
+ are running ` ./scripts/develop.sh ` you can log in using the default credentials.
23
+
24
+ ### Default Credentials: ` admin@coder.com ` and ` SomeSecurePassword! ` .
24
25
25
26
## Tech Stack
26
27
27
- All our dependencies are described in ` site/package.json ` but here are the most
28
- important ones :
28
+ All our dependencies are described in ` site/package.json ` but the following are
29
+ the most important :
29
30
30
31
- [ React] ( https://reactjs.org/ ) as framework
31
32
- [ Typescript] ( https://www.typescriptlang.org/ ) to keep our sanity
@@ -43,8 +44,7 @@ important ones:
43
44
44
45
## Structure
45
46
46
- All the code related to the UI is inside the ` site ` folder and we defined a few
47
- conventions to help people to navigate through it.
47
+ All the code related to the UI resides in the ` site ` folder.
48
48
49
49
- ** e2e** - End-to-end (E2E) tests
50
50
- ** src** - Source code
@@ -53,49 +53,51 @@ conventions to help people to navigate through it.
53
53
(largely code that has no server-side equivalent)
54
54
- ** api** - API code as function calls and types
55
55
- ** queries** - react-query queries and mutations
56
- - ** components** - UI components
57
- - ** hooks** - Hooks that can be used across the application
56
+ - ** components** - Generic UI components without Coder specific business logic
57
+ - ** hooks** - React hooks that can be used across the application
58
+ - ** modules** - Coder specific UI components
58
59
- ** pages** - Page components
59
60
- ** testHelpers** - Helper functions to help with integration tests
61
+ - ** theme** - configuration and color definitions for the color themes
60
62
- ** util** - Helper functions that can be used across the application
61
63
- ** static** - Static UI assets like images, fonts, icons, etc
62
64
63
65
## Routing
64
66
65
- We use [ react-router] ( https://reactrouter.com/en/main ) as our routing engine and
66
- adding a new route is very easy. If the new route needs to be authenticated, put
67
- it under the ` <RequireAuth> ` route and if it needs to live inside of the
68
- dashboard, put it under the ` <DashboardLayout> ` route.
67
+ We use [ react-router] ( https://reactrouter.com/en/main ) as our routing engine.
69
68
70
- The ` RequireAuth ` component handles all the authentication logic for the routes
71
- and the ` DashboardLayout ` wraps the route adding a navbar and passing down
72
- common dashboard data.
69
+ - Authenticated routes - routes needing authentication should be placed inside
70
+ the ` <RequireAuth> ` route. The ` RequireAuth ` component handles all the
71
+ authentication logic for the routes.
72
+ - Dashboard routes - routes that live in the dashboard should be placed under
73
+ the ` <DashboardLayout> ` route. The ` DashboardLayout ` adds a navbar and passes
74
+ down common dashboard data.
73
75
74
76
## Pages
75
77
76
78
Pages are the top-level components of the app. The page component lives under
77
- the ` src/pages ` folder and each page should have its own folder so we can better
78
- group the views, tests, utility functions and so on. We use a structure where
79
- the page component is responsible for fetching all the data and passing it down
80
- to the view. We explain this decision a bit better in the next section.
79
+ the ` src/pages ` folder and each page should have its own folder to better group
80
+ the views, tests, utility functions and so on. The code is structured so that a
81
+ page component is responsible for fetching all the data and passing it down to
82
+ the view. We explain this decision a bit better in the next section.
81
83
82
84
> ℹ️ Code that is only related to the page should live inside of the page folder
83
85
> but if at some point it is used in other pages or components, you should
84
- > consider moving it to the ` src ` level in the ` utils ` , ` hooks ` or ` components `
85
- > folder.
86
+ > consider moving it to the ` src ` level in the ` utils ` , ` hooks ` , ` components ` ,
87
+ > or ` modules ` folder.
86
88
87
89
### States
88
90
89
91
A page usually has at least three states: ** loading** , ** ready** /** success** ,
90
92
and ** error** , so always remember to handle these scenarios while you are coding
91
- a page. We also encourage you to add visual testing for these three states using
92
- a ` *.stories.ts ` file.
93
+ a page. Visual testing is expected for these three states using a ` *.stories.ts `
94
+ file.
93
95
94
96
## Fetching data
95
97
96
98
We use [ TanStack Query v4] ( https://tanstack.com/query/v4/docs/react/quick-start )
97
99
to fetch data from the API. The queries and mutation should be placed inside of
98
- the api/queries folder when it is possible .
100
+ the api/queries folder.
99
101
100
102
### Where to fetch data
101
103
@@ -141,12 +143,14 @@ export const WithQuota: Story = {
141
143
142
144
### API
143
145
144
- We are using [ axios] ( https://github.com/axios/axios ) as our fetching library and
145
- writing the API functions in the ` site/src/api/api.ts ` files. We also have
146
- auto-generated types from our Go server on ` site/src/api/typesGenerated.ts ` .
147
- Usually, every endpoint has its own ` Request ` and ` Response ` types, but
148
- sometimes you need to pass extra parameters to make the call, like in the
149
- example below:
146
+ Our project utilizes [ axios] ( https://github.com/axios/axios ) as the HTTP client
147
+ for making API requests. The API functions are centralized in
148
+ ` site/src/api/api.ts ` . We leverage auto-generated TypeScript types derived from
149
+ our Go server, which are located in ` site/src/api/typesGenerated.ts ` .
150
+
151
+ Typically, each API endpoint corresponds to its own ` Request ` and ` Response `
152
+ types. However, some endpoints require additional parameters for successful
153
+ execution. Here's an illustrative example:"
150
154
151
155
``` ts
152
156
export const getAgentListeningPorts = async (
@@ -159,8 +163,8 @@ export const getAgentListeningPorts = async (
159
163
};
160
164
```
161
165
162
- Sometimes, a frontend operation can have multiple API calls, so it is okay to
163
- wrap it as a single function.
166
+ Sometimes, a frontend operation can have multiple API calls which can be wrapped
167
+ as a single function.
164
168
165
169
``` ts
166
170
export const updateWorkspaceVersion = async (
@@ -171,10 +175,13 @@ export const updateWorkspaceVersion = async (
171
175
};
172
176
```
173
177
174
- If you need more granular errors or control, you may should consider keep them
175
- separated and use XState for that.
178
+ ## Components and Modules
179
+
180
+ Components should be atomic, generic and should not describe any specific
181
+ business logic. Modules are similar to components except that they can be more
182
+ complex and they do contain business logic specific to the product.
176
183
177
- ## Components
184
+ ### MUI
178
185
179
186
The codebase is currently using MUI v5. Please see the
180
187
[ official documentation] ( https://mui.com/material-ui/getting-started/ ) . In
@@ -184,8 +191,9 @@ out of the box.
184
191
185
192
### Structure
186
193
187
- Each component gets its own folder. Make sure you add a test and Storybook
188
- stories for the component as well. By keeping these tidy, the codebase will
194
+ Each component and module gets its own folder. Module folders may group multiple
195
+ files in a hierarchical structure. Storybook stories and component tests using
196
+ Storybook interactions are required. By keeping these tidy, the codebase will
189
197
remain easy to navigate, healthy and maintainable for all contributors.
190
198
191
199
### Accessibility
@@ -221,13 +229,30 @@ import { visuallyHidden } from "@mui/utils";
221
229
</Button >;
222
230
```
223
231
224
- ### Should I create a new component?
232
+ ### Should I create a new component or module?
233
+
234
+ Components could technically be used in any codebase and still feel at home. A
235
+ module would only make sense in the Coder codebase.
236
+
237
+ - Component
238
+ - Simple
239
+ - Atomic, used in multiple places
240
+ - Good Examples: ` Badge ` , ` Form ` , ` Timeline `
241
+ - Module
242
+ - Simple or Complex
243
+ - Used in multiple places
244
+ - Good Examples: ` Provisioner ` , ` DashboardLayout ` , ` DeploymentBanner `
245
+
246
+ Do not assume existing components and modules have been categorized correctly.
225
247
226
- As with most things in the world, it depends. If you are creating a new
227
- component to encapsulate some UI abstraction like ` UsersTable ` it is ok but you
228
- should always try to use the base components that are provided by the library or
229
- from the codebase. It's recommended that you always do a quick search before
230
- creating a custom primitive component like dialogs, popovers, buttons, etc.
248
+ ## Styling
249
+
250
+ We use [ Emotion] ( https://emotion.sh/ ) to handle css styles.
251
+
252
+ ## Forms
253
+
254
+ We use [ Formik] ( https://formik.org/docs ) for forms along with
255
+ [ Yup] ( https://github.com/jquense/yup ) for schema definition and validation.
231
256
232
257
## Testing
233
258
@@ -293,10 +318,9 @@ that:
293
318
294
319
### Tests getting too slow
295
320
296
- A few times you can notice tests can take a very long time to get done.
297
- Sometimes it is because the test itself is complex and runs a lot of stuff, and
298
- sometimes it is because of how we are querying things. In the next section, we
299
- are going to talk more about them.
321
+ You may have observed that certain tests in our suite can be notably
322
+ time-consuming. Sometimes it is because the test itself is complex and sometimes
323
+ it is because of how the test is querying elements.
300
324
301
325
#### Using ` ByRole ` queries
302
326
@@ -326,12 +350,6 @@ const form = screen.getByTestId("form");
326
350
user .click (within (form ).getByRole (" button" ));
327
351
```
328
352
329
- #### ` jest.spyOn ` with the API is not working
330
-
331
- For some unknown reason, we figured out the ` jest.spyOn ` is not able to mock the
332
- API function when they are passed directly into the services XState machine
333
- configuration.
334
-
335
353
❌ Does not work
336
354
337
355
``` ts
0 commit comments