Skip to content

Commit 86eafcd

Browse files
committed
chore(upgrade): upgrade to React 19, React Router v7, vite 6 and more
Fix breaking changes: - React 19: - RefObject<T|null>, - Explicit useRef values, - JSX type imports - Compiler optimizations - Obsolete type usage of ElementRef - React Router v7: remove fallbackElement from RouterProvider - Sentry v10: move replayIntegration to @sentry/react - @Turf v7: stricter AllGeoJSON types, guard undefined buffer return - powerbi-models v2: LayoutType.Custom + DisplayOption.FitToWidth - postcss-preset-env v11: convert config to async dynamic imports
1 parent 17f7c61 commit 86eafcd

113 files changed

Lines changed: 6178 additions & 4860 deletions

File tree

Some content is hidden

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

app/eslint.config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ const appConfigs = compat.config({
101101
'react-hooks/rules-of-hooks': 'error',
102102
'react-hooks/exhaustive-deps': 'warn',
103103

104+
'react-hooks/preserve-manual-memoization': 'error',
105+
'react-hooks/refs': 'error',
106+
'react-hooks/set-state-in-effect': 'error',
107+
'react-hooks/immutability': 'error',
108+
104109
'react/require-default-props': ['warn', { ignoreFunctionalComponents: true }],
105110
'simple-import-sort/imports': 'warn',
106111
'simple-import-sort/exports': 'warn',

app/package.json

Lines changed: 63 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -43,83 +43,83 @@
4343
"dependencies": {
4444
"@ifrc-go/icons": "^2.0.1",
4545
"@ifrc-go/ui": "workspace:^",
46-
"@sentry/react": "^7.81.1",
47-
"@tinymce/tinymce-react": "^5.1.1",
48-
"@togglecorp/fujs": "^2.1.1",
46+
"@sentry/react": "^10.0.0",
47+
"@tinymce/tinymce-react": "^6.0.0",
48+
"@togglecorp/fujs": "^2.2.0",
4949
"@togglecorp/re-map": "^0.3.0",
5050
"@togglecorp/toggle-form": "^2.0.4",
51-
"@togglecorp/toggle-request": "^1.0.0-beta.3",
52-
"@turf/bbox": "^6.5.0",
53-
"@turf/buffer": "^6.5.0",
51+
"@togglecorp/toggle-request": "1.0.0-beta.3",
52+
"@turf/bbox": "^7.0.0",
53+
"@turf/buffer": "^7.0.0",
5454
"exceljs": "^4.4.0",
5555
"file-saver": "^2.0.5",
56-
"html-to-image": "^1.11.11",
57-
"mapbox-gl": "^1.13.0",
58-
"papaparse": "^5.4.1",
59-
"powerbi-client": "^2.23.1",
60-
"react": "^18.2.0",
61-
"react-dom": "^18.2.0",
62-
"react-router-dom": "^6.18.0",
63-
"sanitize-html": "^2.10.0",
56+
"html-to-image": "^1.11.13",
57+
"mapbox-gl": "^1.13.3",
58+
"papaparse": "^5.5.3",
59+
"powerbi-client": "^2.23.10",
60+
"react": "^19.0.0",
61+
"react-dom": "^19.0.0",
62+
"react-router-dom": "^7.0.0",
63+
"sanitize-html": "^2.17.2",
6464
"xlsx": "^0.18.5"
6565
},
6666
"devDependencies": {
67-
"@eslint/eslintrc": "^3.2.0",
68-
"@eslint/js": "^9.20.0",
69-
"@eslint/json": "^0.5.0",
70-
"@julr/vite-plugin-validate-env": "^1.0.1",
71-
"@types/file-saver": "^2.0.5",
72-
"@types/mapbox-gl": "^1.13.0",
73-
"@types/node": "^20.17.19",
74-
"@types/papaparse": "^5.3.8",
75-
"@types/react": "^18.0.28",
76-
"@types/react-dom": "^18.0.11",
77-
"@types/sanitize-html": "^2.9.0",
78-
"@types/yargs": "^17.0.32",
79-
"@typescript-eslint/eslint-plugin": "^8.11.0",
80-
"@typescript-eslint/parser": "^8.11.0",
81-
"@typescript-eslint/typescript-estree": "^8.43.0",
82-
"@typescript-eslint/visitor-keys": "^8.44.0",
83-
"@vitejs/plugin-react-swc": "^3.5.0",
84-
"@vitest/coverage-v8": "^1.2.2",
85-
"autoprefixer": "^10.4.14",
86-
"eslint": "^9.20.1",
67+
"@eslint/eslintrc": "^3.3.5",
68+
"@eslint/js": "^9.39.4",
69+
"@eslint/json": "^1.0.0",
70+
"@julr/vite-plugin-validate-env": "^2.0.0",
71+
"@types/file-saver": "^2.0.7",
72+
"@types/mapbox-gl": "^1.13.10",
73+
"@types/node": "^20.19.37",
74+
"@types/papaparse": "^5.5.2",
75+
"@types/react": "^19.0.0",
76+
"@types/react-dom": "^19.0.0",
77+
"@types/sanitize-html": "^2.16.1",
78+
"@types/yargs": "^17.0.35",
79+
"@typescript-eslint/eslint-plugin": "^8.58.0",
80+
"@typescript-eslint/parser": "^8.58.0",
81+
"@typescript-eslint/typescript-estree": "^8.58.0",
82+
"@typescript-eslint/visitor-keys": "^8.58.0",
83+
"@vitejs/plugin-react-swc": "^4.0.0",
84+
"@vitest/coverage-v8": "^4.0.0",
85+
"autoprefixer": "^10.4.27",
86+
"eslint": "^9.39.4",
8787
"eslint-config-airbnb": "^19.0.4",
88-
"eslint-import-resolver-typescript": "^3.6.3",
89-
"eslint-plugin-import": "^2.31.0",
88+
"eslint-import-resolver-typescript": "^4.0.0",
89+
"eslint-plugin-import": "^2.32.0",
9090
"eslint-plugin-import-exports-imports-resolver": "^1.0.1",
91-
"eslint-plugin-import-newlines": "^1.3.4",
92-
"eslint-plugin-jsx-a11y": "^6.10.1",
93-
"eslint-plugin-react": "^7.37.4",
94-
"eslint-plugin-react-hooks": "^5.0.0",
95-
"eslint-plugin-react-refresh": "^0.4.13",
91+
"eslint-plugin-import-newlines": "^1.4.1",
92+
"eslint-plugin-jsx-a11y": "^6.10.2",
93+
"eslint-plugin-react": "^7.37.5",
94+
"eslint-plugin-react-hooks": "^7.0.0",
95+
"eslint-plugin-react-refresh": "^0.4.26",
9696
"eslint-plugin-simple-import-sort": "^12.1.1",
97-
"fast-glob": "^3.3.2",
98-
"happy-dom": "^9.18.3",
99-
"jiti": "^2.5.1",
97+
"fast-glob": "^3.3.3",
98+
"happy-dom": "^20.0.0",
99+
"jiti": "^2.6.1",
100100
"openapi-typescript": "6.5.5",
101-
"postcss": "^8.5.3",
101+
"postcss": "^8.5.8",
102102
"postcss-nested": "^7.0.2",
103103
"postcss-normalize": "^13.0.1",
104-
"postcss-preset-env": "^10.1.5",
105-
"rollup-plugin-visualizer": "^5.9.0",
106-
"stylelint": "^16.17.0",
104+
"postcss-preset-env": "^11.0.0",
105+
"rollup-plugin-visualizer": "^7.0.0",
106+
"stylelint": "^17.0.0",
107107
"stylelint-config-concentric": "^2.0.2",
108-
"stylelint-config-recommended": "^15.0.0",
109-
"stylelint-value-no-unknown-custom-properties": "^6.0.1",
108+
"stylelint-config-recommended": "^18.0.0",
109+
"stylelint-value-no-unknown-custom-properties": "^6.1.1",
110110
"surge": "^0.23.1",
111-
"ts-md5": "^1.3.1",
112-
"tsx": "^4.7.2",
113-
"typescript": "^5.5.2",
114-
"typescript-eslint": "^8.26.0",
115-
"vite": "^5.0.10",
116-
"vite-plugin-checker": "^0.7.0",
117-
"vite-plugin-compression2": "^0.11.0",
118-
"vite-plugin-radar": "^0.9.2",
119-
"vite-plugin-svgr": "^4.2.0",
120-
"vite-plugin-webfont-dl": "^3.9.4",
121-
"vite-tsconfig-paths": "^4.2.2",
122-
"vitest": "^1.2.2",
123-
"yargs": "^17.7.2"
111+
"ts-md5": "^2.0.0",
112+
"tsx": "^4.21.0",
113+
"typescript": "^5.9.3",
114+
"typescript-eslint": "^8.58.0",
115+
"vite": "^6.0.0",
116+
"vite-plugin-checker": "^0.12.0",
117+
"vite-plugin-compression2": "^2.0.0",
118+
"vite-plugin-radar": "^0.10.1",
119+
"vite-plugin-svgr": "^5.0.0",
120+
"vite-plugin-webfont-dl": "3.12.0",
121+
"vite-tsconfig-paths": "^6.0.0",
122+
"vitest": "^4.0.0",
123+
"yargs": "^18.0.0"
124124
}
125125
}

app/postcss.config.cjs

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
module.exports = {
2-
plugins: [
3-
require('postcss-preset-env'),
4-
require('postcss-nested'),
5-
require('postcss-normalize'),
6-
require('autoprefixer'),
7-
],
1+
module.exports = async () => {
2+
const [
3+
{ default: postcssPresetEnv },
4+
{ default: postcssNested },
5+
{ default: postcssNormalize },
6+
{ default: autoprefixer },
7+
] = await Promise.all([
8+
import('postcss-preset-env'),
9+
import('postcss-nested'),
10+
import('postcss-normalize'),
11+
import('autoprefixer'),
12+
]);
13+
return {
14+
plugins: [postcssPresetEnv, postcssNested, postcssNormalize, autoprefixer],
15+
};
816
};

app/src/App/index.tsx

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,7 @@ import {
2424
} from '@togglecorp/fujs';
2525
import mapboxgl from 'mapbox-gl';
2626

27-
import goLogo from '#assets/icons/go-logo-2020.svg';
28-
import {
29-
appTitle,
30-
mbtoken,
31-
} from '#config';
27+
import { mbtoken } from '#config';
3228
import RouteContext from '#contexts/route';
3329
import UserContext, {
3430
type UserAuth,
@@ -53,15 +49,13 @@ import {
5349

5450
import wrappedRoutes, { unwrappedRoutes } from './routes';
5551

56-
import styles from './styles.module.css';
57-
5852
const requestContextValue = {
5953
transformUrl: processGoUrls,
6054
transformOptions: processGoOptions,
6155
transformResponse: processGoResponse,
6256
transformError: processGoError,
6357
};
64-
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouter(createBrowserRouter);
58+
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(createBrowserRouter);
6559
const router = sentryCreateBrowserRouter(unwrappedRoutes);
6660
mapboxgl.accessToken = mbtoken;
6761
mapboxgl.setRTLTextPlugin(
@@ -202,6 +196,8 @@ function Application() {
202196

203197
// Hydration
204198
useEffect(() => {
199+
// FIXME(frozenhelium): hydrateUserAuth calls setState internally; one-time hydration
200+
// eslint-disable-next-line react-hooks/set-state-in-effect
205201
hydrateUserAuth();
206202

207203
const language = getFromStorage<Language>(KEY_LANGUAGE_STORAGE);
@@ -246,16 +242,6 @@ function Application() {
246242
<RequestContext.Provider value={requestContextValue}>
247243
<RouterProvider
248244
router={router}
249-
fallbackElement={(
250-
<div className={styles.fallbackElement}>
251-
<img
252-
className={styles.goLogo}
253-
alt="IFRC GO"
254-
src={goLogo}
255-
/>
256-
{`${appTitle} loading...`}
257-
</div>
258-
)}
259245
/>
260246
</RequestContext.Provider>
261247
</AlertContext.Provider>

app/src/components/GradientBar/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import styles from './styles.module.css';
66
interface Props extends Omit<React.HTMLProps<HTMLDivElement>, 'ref' | 'size'> {
77
startColor: string;
88
endColor: string;
9-
elementRef?: RefObject<HTMLDivElement>;
9+
elementRef?: RefObject<HTMLDivElement | null>;
1010
}
1111

1212
function GradientBar(props: Props) {

app/src/components/MapPopup/styles.module.css

Lines changed: 16 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,24 @@
88
font-family: var(--go-ui-font-family-sans-serif);
99
font-size: var(--go-ui-font-size-md);
1010

11-
:global {
12-
.mapboxgl-tip {
13-
flex-shrink: 0;
14-
}
11+
& :global(.mapboxgl-tip) {
12+
flex-shrink: 0;
13+
}
1514

16-
.mapboxgl-popup-content {
17-
display: flex;
18-
flex-direction: column;
19-
flex-grow: 1;
20-
padding: 0;
21-
overflow: auto;
15+
& :global(.mapboxgl-popup-content) {
16+
display: flex;
17+
flex-direction: column;
18+
flex-grow: 1;
19+
padding: 0;
20+
overflow: auto;
21+
}
2222

23-
>div {
24-
display: flex;
25-
flex-direction: column;
26-
flex-grow: 1;
27-
padding: 0;
28-
overflow: auto;
29-
}
30-
}
23+
& :global(.mapboxgl-popup-content > div) {
24+
display: flex;
25+
flex-direction: column;
26+
flex-grow: 1;
27+
padding: 0;
28+
overflow: auto;
3129
}
3230

3331
.container {
@@ -46,4 +44,3 @@
4644
}
4745
}
4846
}
49-

app/src/components/Page/index.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import {
2-
type ElementRef,
32
type RefObject,
43
useEffect,
54
} from 'react';
@@ -41,7 +40,7 @@ interface Props {
4140
mainSectionClassName?: string;
4241
wikiLink?: React.ReactNode;
4342
withBackgroundColorInMainSection?: boolean;
44-
elementRef?: RefObject<ElementRef<'div'>>;
43+
elementRef?: RefObject<HTMLDivElement | null>;
4544
blockingContent?: React.ReactNode;
4645
contentOriginalLanguage?: TranslationModuleOriginalLanguageEnum;
4746
beforeHeaderContent?: React.ReactNode;

app/src/components/RichTextArea/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ type InheritedProps<T> = Omit<InputContainerProps, 'input'> & {
3838
) => void;
3939
}
4040
interface Props<T extends string | undefined> extends InheritedProps<T> {
41-
inputElementRef?: React.RefObject<HTMLInputElement>;
41+
inputElementRef?: React.RefObject<HTMLInputElement | null>;
4242
placeholder?: string;
4343
}
4444

@@ -80,7 +80,8 @@ function RichTextArea<T extends string | undefined>(props: Props<T>) {
8080

8181
// eslint-disable-next-line react/destructuring-assignment
8282
if (props.placeholder !== undefined) {
83-
// eslint-disable-next-line react/destructuring-assignment
83+
// NOTE(frozenhelium): editorOptions mutated before passing to editor; safe this render
84+
// eslint-disable-next-line react/destructuring-assignment, react-hooks/immutability
8485
editorOptions.placeholder = props.placeholder;
8586
}
8687

app/src/components/RichTextArea/styles.module.css

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,31 +2,23 @@
22
position: relative;
33
z-index: 0;
44

5-
:global {
6-
.tox-tinymce {
7-
margin: unset;
8-
border: unset;
9-
border-radius: unset;
10-
width: 100%;
5+
& :global(.tox-tinymce) {
6+
margin: unset;
7+
border: unset;
8+
border-radius: unset;
9+
width: 100%;
10+
}
1111

12-
.tox-editor-container {
13-
.tox-editor-header {
14-
.tox-toolbar-overlord {
15-
.tox-toolbar__primary {
16-
background: unset;
17-
background-color: unset;
18-
}
19-
}
20-
}
12+
& :global(.tox-tinymce .tox-editor-container .tox-editor-header .tox-toolbar-overlord .tox-toolbar__primary) {
13+
background: unset;
14+
background-color: unset;
15+
}
2116

22-
.tox-edit-area__iframe {
23-
background-color: unset;
24-
}
25-
}
17+
& :global(.tox-tinymce .tox-editor-container .tox-edit-area__iframe) {
18+
background-color: unset;
19+
}
2620

27-
.tox-statusbar {
28-
background-color: unset;
29-
}
30-
}
21+
& :global(.tox-tinymce .tox-statusbar) {
22+
background-color: unset;
3123
}
3224
}

app/src/components/TabPage/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import WikiLink from '#components/WikiLink';
99
import styles from './styles.module.css';
1010

1111
interface Props {
12-
elementRef?: React.RefObject<HTMLDivElement>;
12+
elementRef?: React.RefObject<HTMLDivElement | null>;
1313
children?: React.ReactNode;
1414
wikiLinkPathName?: string;
1515

0 commit comments

Comments
 (0)