Skip to content

Commit 6ffdd08

Browse files
Merge pull request #2459 from zetkin/release-250107
250107 Release
2 parents 580885e + 4b61034 commit 6ffdd08

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+1003
-189
lines changed

.gitignore

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,8 @@ yarn-error.log*
4343
.idea
4444

4545
# typescript
46-
tsconfig.tsbuildinfo
46+
tsconfig.tsbuildinfo
47+
48+
# generated docs
49+
public/docs
50+
public/storybook
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
---
2+
title: Environment Variables
3+
category: Environment Variables
4+
---
5+
6+
# Environment Variables
7+
8+
## Build-Time vs Runtime Environment Variables
9+
10+
Next.js is designed around Vercel's Platform-as-a-Service (PaaS) hosting
11+
business. Apps on Vercel can have multiple deployment environments such as
12+
staging and production, and a different build of the app is compiled for each
13+
environment. Next.js includes functionality to support Vercel's
14+
one-environment-per-build deployment model, where any environment variables
15+
needed by client-side code are [inlined directly into the compiled JavaScript](https://nextjs.org/docs/pages/building-your-application/configuring/environment-variables#bundling-environment-variables-for-the-browser)
16+
bundle at build time.
17+
18+
Outside of Vercel's hosting platform, Docker images are a common way to
19+
distribute and deploy software. Self-hosted open source software is often
20+
distributed as pre-built Docker images whose operators apply environment
21+
variables to them at runtime. This is a deployment model with multiple
22+
environments per build, where environment variables cannot be known at build
23+
time. It also happens to be Zetkin's deployment model.
24+
25+
The emphasis that Next.js places on the Vercel platform's deployment
26+
model in its architecture and documentation can easily lead to confusion when
27+
working on an app like Zetkin with a different deployment model. The fact that
28+
handling runtime environment variables looks different under the newer app
29+
router paradigm compared to the older pages router code also adds to the sense
30+
of complexity here. So here's how Zetkin's runtime environment variables work in
31+
both Next.js routing paradigms.
32+
33+
## Runtime Environment Variables In App Router Code
34+
35+
The `<RootLayout>` component in `src/app/layout.tsx` makes a set of runtime
36+
environment variables from the Node.js process on the server available to
37+
client-side code by adding them as values to a `ClientContext` provider.
38+
39+
The [`EnvVars`](../types/EnvVars.html) type alias is a good starting point when
40+
adding a new runtime environment variable to app router code. Add your variable
41+
there and then fix any resulting type errors and you'll be up and running.
42+
43+
## Runtime Environment Variables In Pages Router Code
44+
45+
Just about every page component in `src/pages/` uses the `scaffold()` function
46+
to generate its [`getServerSideProps()`](https://nextjs.org/docs/pages/building-your-application/data-fetching/get-server-side-props) function.
47+
48+
The [`EnvVars`](../types/EnvVars.html) type alias is also the place to look
49+
first when adding a new runtime environment variable to pages router code.
50+
51+
## Accessing Runtime Environment Variables In Components
52+
53+
The [`useEnv`](../functions/useEnv.html) hook returns an
54+
[`Environment`](../classes/Environment.html) instance which provides reads access to runtime environment variables via its [`vars` property](../classes/Environment.html#vars).
55+
56+
```typescript
57+
const Component = () => {
58+
const env = useEnv();
59+
return (
60+
<a href={env.vars.ZETKIN_GEN2_ORGANIZE_URL}>Go to the old interface</a>
61+
);
62+
};
63+
```
64+
65+
## Further Reading
66+
67+
Zetkin's far from the only project to have solved this problem.
68+
69+
- [NextJS on Docker: Managing Environment Variables Across Different Environments](https://medium.com/@ihcnemed/nextjs-on-docker-managing-environment-variables-across-different-environments-972b34a76203)
70+
- [Next.js with Public Environment Variables in Docker](https://dev.to/vorillaz/nextjs-with-public-environment-variables-in-docker-4ogf)
71+
- [Runtime Environment Variables in Next.js](https://notes.dt.in.th/NextRuntimeEnv)

docs/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import Environment from 'core/env/Environment';
2+
import { useEnv } from 'core/hooks';
3+
4+
export { type RemoteItem, type RemoteList } from 'utils/storeUtils';
5+
export {
6+
loadItemIfNecessary,
7+
loadListIfNecessary,
8+
} from 'core/caching/cacheUtils';
9+
export { default as shouldLoad } from 'core/caching/shouldLoad';
10+
export { default as ZUIFuture, type ZUIFutureProps } from 'zui/ZUIFuture';
11+
export { type IFuture } from 'core/caching/futures';
12+
export { removeOffset } from 'utils/dateUtils';
13+
export { type EnvVars } from 'core/env/Environment';
14+
export { Environment, useEnv };

docs/testing/jest.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
---
2+
title: Jest
3+
category: Testing
4+
---
5+
6+
# Jest
7+
8+
## Running Jest
9+
10+
### Run all tests
11+
12+
```
13+
yarn test
14+
```
15+
16+
It can be useful to run the whole test suite if you've changed a few files as part of whatever you're working on.
17+
18+
### Run one test
19+
20+
```
21+
yarn test src/utils/dateUtils.spec.ts
22+
```
23+
24+
When you're working on one particular file, you can run its tests by putting the path to them after `yarn test`.
25+
26+
### Watch mode
27+
28+
```
29+
yarn test --watch src/utils/dateUtils.spec.ts
30+
```
31+
32+
During focused work on a single file, it can be helpful to use the `--watch` flag to re-run the tests automatically every time you change something.

docs/testing/playwright.md

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
---
2+
title: Playwright
3+
category: Testing
4+
---
5+
6+
# Playwright
7+
8+
## Running Playwright
9+
10+
### Run all tests
11+
12+
```
13+
yarn playwright
14+
```
15+
16+
It can be useful to run the whole playwright suite if you've changed a few features as part of whatever you're working on.
17+
18+
### Run one test
19+
20+
```
21+
yarn playwright tests/organize/views/detail/display.spec.ts
22+
```
23+
24+
When you're working on one particular feature, you can run its playwright tests by putting the path to them after `yarn playwright`.
25+
26+
### Skip the build
27+
28+
```
29+
yarn playwright:skipbuild tests/organize/views/detail/display.spec.ts
30+
```
31+
32+
The `yarn playwright` script builds the Next.js app before running the tests. This takes several minutes, and isn't useful if you're only changing test code (code in a `.spec.ts` file). You can tell playwright not to include this build step with the `playwright:skipbuild` script.
33+
34+
### Debug mode
35+
36+
```
37+
yarn playwright:skipbuild --debug tests/organize/views/detail/display.spec.ts
38+
```
39+
40+
In its default mode of execution, Playwright runs the tests in a headless browser. This means you can't see them running or interact with the browser's developer tools to debug any problems that arise. By adding the `--debug` flag, you can tell Playwright to run the tests visually in a Chrome window so you can see what's happening and debug any problems.
41+
42+
## Writing Playwright tests
43+
44+
### Moxy
45+
46+
Zetkins Playwright tests isolate the Next.js app from the back end very thoroughly by using [moxy](https://github.com/zetkin/moxy) to set up placeholder responses. This means the tests can run without the app ever actually communicating with the real back end. The purpose of this is to make the tests more deterministic, which means they should only break because of a problem with the code, because nobody can break them by mistake by changing something in the dev server's database that they depend on.
47+
48+
The [code search results for `moxy.setZetkinApiMock`](https://github.com/search?q=repo%3Azetkin%2Fapp.zetkin.org%20moxy.setZetkinApiMock&type=code) are a great starting point to learn about setting up these API mocks.
49+
50+
### waitForNavigation
51+
52+
A common step in a Playwright test is to trigger a click on a link and then continue after the page load has completed. The intuitive way to write this code would be like so.
53+
54+
```typescript
55+
await page.click(`text=Next Page`);
56+
await page.waitForNavigation();
57+
```
58+
59+
You won't find any examples of code written that way in Zetkin though. The problem with writing it like that is that the navigation can sometimes complete before the `await page.waitForNavigation()` has even happened, leaving the test stranded waiting for a navigation that's already happened. Instead, we set up both steps simultaneously like this.
60+
61+
```typescript
62+
await Promise.all([
63+
page.waitForNavigation(),
64+
page.click(`text=${AllMembers.title}`),
65+
]);
66+
```
67+
68+
### Locators
69+
70+
Browser automation tests like these are notorious for sometimes failing randomly. It's difficult to avoid making subtle mistakes and introducing race conditions like the `waitForNavigation` issue above. One general-purpose technique that can help with this is to prefer using [locators](https://playwright.dev/docs/locators) instead of `page.click()`. The first thing Playwright's own [documentation for `page.click`](https://playwright.dev/docs/api/class-page#page-click) says is not to use it.
71+
72+
> **Discouraged**<br />
73+
> Use locator-based [locator.click()](https://playwright.dev/docs/api/class-locator#locator-click) instead. Read more about [locators](https://playwright.dev/docs/locators).
74+
75+
Using locators instead of `page.click()` maximizes our chances of Playwright's auto-waiting and auto-retrying saving us from a meaningless random test failure resulting from a race condition.
76+
77+
There are lots of examples to learn from in the [code search results for `page.locator`](https://github.com/search?q=repo%3Azetkin%2Fapp.zetkin.org%20page.locator&type=code).

docs/time/time.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
title: Time
3+
category: Time
4+
---
5+
6+
# Time
7+
8+
## Day.js
9+
10+
Zetkin uses [Day.js](https://day.js.org/) to work with times and dates. Plugins used include [UTC](https://day.js.org/docs/en/plugin/utc), [IsoWeek](https://day.js.org/docs/en/plugin/iso-week), and [CustomParseFormat](https://day.js.org/docs/en/plugin/custom-parse-format).
11+
12+
## Serialization Format
13+
14+
The most commonly used date serialization format around the Zetkin front end is `2024-07-23T12:55:14.279Z`. A code search for [`new Date().toISOString()`](<https://github.com/search?q=repo%3Azetkin%2Fapp.zetkin.org%20%22new%20Date().toISOString()%22&type=code>) shows all the places where the current date & time are being serialized using this format by the front end code.

next.config.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@ module.exports = {
1313
},
1414
async redirects() {
1515
return [
16+
{
17+
source: '/storybook',
18+
destination: '/storybook/index.html',
19+
permanent: true,
20+
},
21+
{
22+
source: '/docs',
23+
destination: '/docs/index.html',
24+
permanent: true,
25+
},
1626
{
1727
source: '/my',
1828
destination: '/my/home',

package.json

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
"build": "next build",
88
"start": "next start -p 80",
99
"devserver": "next dev -p 3000",
10+
"docs:build": "storybook build -o public/storybook && typedoc",
1011
"lint:eslint": "eslint src integrationTesting",
1112
"lint:prettier": "prettier --check src integrationTesting",
1213
"lint:translations": "ts-node src/tools/lint-translations",
@@ -21,8 +22,7 @@
2122
"playwright:skipbuild": "cross-env NODE_ENV=production SKIP_BUILD=1 playwright test",
2223
"playwright:ci": "cross-env NODE_ENV=production playwright test",
2324
"storybook": "storybook dev -p 6006",
24-
"make-yaml": "ts-node src/tools/make-yaml.ts",
25-
"build-storybook": "storybook build"
25+
"make-yaml": "ts-node src/tools/make-yaml.ts"
2626
},
2727
"dependencies": {
2828
"@date-io/date-fns": "1.x",
@@ -155,6 +155,7 @@
155155
"ts-mockito": "^2.6.1",
156156
"ts-node": "^10.9.1",
157157
"tsconfig-paths-webpack-plugin": "^3.5.2",
158+
"typedoc": "^0.26.5",
158159
"typescript": "^5.4.3"
159160
},
160161
"engines": {

src/app/layout.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,14 @@ export default async function RootLayout({
3232
<AppRouterCacheProvider>
3333
<ClientContext
3434
envVars={{
35-
MUIX_LICENSE_KEY: process.env.MUIX_LICENSE_KEY || null,
36-
ZETKIN_APP_DOMAIN: process.env.ZETKIN_APP_DOMAIN || null,
35+
FEAT_AREAS: process.env.FEAT_AREAS,
36+
INSTANCE_OWNER_HREF: process.env.INSTANCE_OWNER_HREF,
37+
INSTANCE_OWNER_NAME: process.env.INSTANCE_OWNER_NAME,
38+
MUIX_LICENSE_KEY: process.env.MUIX_LICENSE_KEY,
39+
ZETKIN_APP_DOMAIN: process.env.ZETKIN_APP_DOMAIN,
40+
ZETKIN_GEN2_ORGANIZE_URL: process.env.ZETKIN_GEN2_ORGANZE_URL,
41+
ZETKIN_PRIVACY_POLICY_LINK:
42+
process.env.ZETKIN_PRIVACY_POLICY_LINK,
3743
}}
3844
headers={headersObject}
3945
lang={lang}

0 commit comments

Comments
 (0)