Skip to content

Commit 0aa3b1d

Browse files
authored
Migrate react-router to v6 (#4703)
# What this PR does - Migrate react-router from v5 to v6 Closes #4031
1 parent be1dd67 commit 0aa3b1d

32 files changed

+1106
-970
lines changed

grafana-plugin/.config/.eslintrc

-3
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@
1414
{
1515
"plugins": ["deprecation"],
1616
"files": ["src/**/*.{ts,tsx}"],
17-
"rules": {
18-
"deprecation/deprecation": "warn"
19-
},
2017
"parserOptions": {
2118
"project": "./tsconfig.json"
2219
}

grafana-plugin/.config/webpack/webpack.config.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ const config = async (env): Promise<Configuration> => {
199199

200200
if (isWSL()) {
201201
baseConfig.watchOptions = {
202-
poll: 3000,
202+
// poll: 3000,
203203
ignored: /node_modules/,
204204
};
205205
}

grafana-plugin/.eslintrc.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = {
1212
{
1313
files: ['src/**/*.{ts,tsx}'],
1414
rules: {
15-
'deprecation/deprecation': 'off',
15+
'deprecation/deprecation': 'warn',
1616
},
1717
parserOptions: {
1818
project: './tsconfig.json',

grafana-plugin/package.json

+1-2
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@
7373
"@types/react-copy-to-clipboard": "^5.0.4",
7474
"@types/react-dom": "^18.0.6",
7575
"@types/react-responsive": "^8.0.5",
76-
"@types/react-router-dom": "^5.3.3",
7776
"@types/react-test-renderer": "^18.0.5",
7877
"@types/react-transition-group": "^4.4.5",
7978
"@types/testing-library__jest-dom": "5.14.8",
@@ -170,7 +169,7 @@
170169
"react-hook-form": "^7.50.1",
171170
"react-modal": "^3.15.1",
172171
"react-responsive": "^8.1.0",
173-
"react-router-dom": "5.3.3",
172+
"react-router-dom-v5-compat": "^6.25.1",
174173
"react-sortable-hoc": "^1.11.0",
175174
"react-string-replace": "^0.4.4",
176175
"react-transition-group": "^4.4.5",

grafana-plugin/src/components/PluginLink/PluginLink.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import React, { FC, useCallback, useMemo } from 'react';
33
import { css, cx } from '@emotion/css';
44
import { GrafanaTheme2 } from '@grafana/data';
55
import { useStyles2 } from '@grafana/ui';
6-
import { Link } from 'react-router-dom';
6+
import { Link } from 'react-router-dom-v5-compat';
77
import { bem } from 'styles/utils.styles';
88

99
import { getPathFromQueryParams } from 'utils/url';

grafana-plugin/src/containers/IntegrationForm/IntegrationForm.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
} from '@grafana/ui';
2020
import { observer } from 'mobx-react';
2121
import { Controller, useForm, useFormContext, FormProvider } from 'react-hook-form';
22-
import { useHistory } from 'react-router-dom';
22+
import { useNavigate } from 'react-router-dom-v5-compat';
2323

2424
import { HowTheIntegrationWorks } from 'components/HowTheIntegrationWorks/HowTheIntegrationWorks';
2525
import { PluginLink } from 'components/PluginLink/PluginLink';
@@ -94,7 +94,7 @@ export const IntegrationForm = observer(
9494
onBackClick,
9595
}: IntegrationFormProps) => {
9696
const store = useStore();
97-
const history = useHistory();
97+
const navigate = useNavigate();
9898
const styles = useStyles2(getIntegrationFormStyles);
9999
const isNew = id === 'new';
100100
const {
@@ -453,7 +453,8 @@ export const IntegrationForm = observer(
453453
async function createNewIntegration(): Promise<void | ApiSchemas['AlertReceiveChannelCreate']> {
454454
const response = await alertReceiveChannelStore.create({ data, skipErrorHandling: true });
455455
const pushHistory = (id: ApiSchemas['AlertReceiveChannel']['id']) =>
456-
history.push(`${PLUGIN_ROOT}/integrations/${id}`);
456+
navigate(`${PLUGIN_ROOT}/integrations/${id}`);
457+
457458
if (!response) {
458459
return;
459460
}

grafana-plugin/src/containers/MobileAppConnection/MobileAppConnection.test.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22

33
import { render, screen, waitFor } from '@testing-library/react';
44
import userEvent from '@testing-library/user-event';
5-
import { MemoryRouter } from 'react-router-dom';
5+
import { MemoryRouter } from 'react-router-dom-v5-compat';
66

77
import { UserHelper } from 'models/user/user.helpers';
88
import { ApiSchemas } from 'network/oncall-api/api.types';

grafana-plugin/src/containers/OutgoingWebhookForm/OutgoingWebhookForm.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
import cn from 'classnames/bind';
1515
import { observer } from 'mobx-react';
1616
import { FormProvider, useForm, useFormContext } from 'react-hook-form';
17-
import { useHistory } from 'react-router-dom';
17+
import { useNavigate } from 'react-router-dom-v5-compat';
1818

1919
import { Text } from 'components/Text/Text';
2020
import { OutgoingWebhookStatus } from 'containers/OutgoingWebhookStatus/OutgoingWebhookStatus';
@@ -304,7 +304,7 @@ interface EditWebhookTabsProps {
304304
const EditWebhookTabs = (props: EditWebhookTabsProps) => {
305305
const { id, data, action, onHide, onUpdate, onDelete, onSubmit, onTemplateEditClick, preset } = props;
306306

307-
const history = useHistory();
307+
const navigate = useNavigate();
308308

309309
const [activeTab, setActiveTab] = useState(
310310
action === WebhookFormActionType.EDIT_SETTINGS ? WebhookTabs.Settings.key : WebhookTabs.LastRun.key
@@ -323,7 +323,7 @@ const EditWebhookTabs = (props: EditWebhookTabsProps) => {
323323
key={WebhookTabs.Settings.key}
324324
onChangeTab={() => {
325325
setActiveTab(WebhookTabs.Settings.key);
326-
history.push(`${PLUGIN_ROOT}/outgoing_webhooks/edit/${id}`);
326+
navigate(`${PLUGIN_ROOT}/outgoing_webhooks/edit/${id}`);
327327
}}
328328
active={activeTab === WebhookTabs.Settings.key}
329329
label={WebhookTabs.Settings.value}
@@ -333,7 +333,7 @@ const EditWebhookTabs = (props: EditWebhookTabsProps) => {
333333
key={WebhookTabs.LastRun.key}
334334
onChangeTab={() => {
335335
setActiveTab(WebhookTabs.LastRun.key);
336-
history.push(`${PLUGIN_ROOT}/outgoing_webhooks/last_run/${id}`);
336+
navigate(`${PLUGIN_ROOT}/outgoing_webhooks/last_run/${id}`);
337337
}}
338338
active={activeTab === WebhookTabs.LastRun.key}
339339
label={WebhookTabs.LastRun.value}

grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.test.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22

33
import { render, screen } from '@testing-library/react';
44
import userEvent from '@testing-library/user-event';
5-
import { useLocation as useLocationOriginal } from 'react-router-dom';
5+
import { useLocation as useLocationOriginal } from 'react-router-dom-v5-compat';
66
import { OnCallPluginConfigPageProps } from 'types';
77

88
import { PluginState } from 'state/plugin/plugin';
@@ -17,7 +17,7 @@ jest.mock('../../../package.json', () => ({
1717
version: 'v1.2.3',
1818
}));
1919

20-
jest.mock('react-router-dom', () => ({
20+
jest.mock('react-router-dom-v5-compat', () => ({
2121
useLocation: jest.fn(() => ({
2222
search: '',
2323
})),

grafana-plugin/src/containers/PluginConfigPage/PluginConfigPage.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React, { FC, useCallback, useEffect, useState } from 'react';
22

33
import { Button, HorizontalGroup, Label, Legend, LinkButton, LoadingPlaceholder, VerticalGroup } from '@grafana/ui';
4-
import { useLocation } from 'react-router-dom';
4+
import { useLocation } from 'react-router-dom-v5-compat';
55
import { OnCallPluginConfigPageProps } from 'types';
66

77
import { PluginState, PluginStatusResponseBase } from 'state/plugin/plugin';

grafana-plugin/src/containers/Rotations/SchedulePersonal.tsx

+7-5
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { GrafanaTheme2 } from '@grafana/data';
55
import { Badge, BadgeColor, Button, HorizontalGroup, Icon, useStyles2, withTheme2 } from '@grafana/ui';
66
import dayjs from 'dayjs';
77
import { observer } from 'mobx-react';
8-
import { RouteComponentProps, withRouter } from 'react-router-dom';
8+
import { useNavigate } from 'react-router-dom-v5-compat';
99
import { CSSTransition, TransitionGroup } from 'react-transition-group';
1010

1111
import { Avatar } from 'components/Avatar/Avatar';
@@ -32,14 +32,16 @@ import { getRotationsStyles } from './Rotations.styles';
3232

3333
import animationStyles from './Rotations.module.css';
3434

35-
interface SchedulePersonalProps extends RouteComponentProps {
35+
interface SchedulePersonalProps {
3636
userPk: ApiSchemas['User']['pk'];
3737
onSlotClick?: (event: Event) => void;
3838
theme: GrafanaTheme2;
3939
}
4040

41-
const _SchedulePersonal: FC<SchedulePersonalProps> = observer(({ userPk, onSlotClick, history }) => {
41+
const _SchedulePersonal: FC<SchedulePersonalProps> = observer(({ userPk, onSlotClick }) => {
4242
const store = useStore();
43+
const navigate = useNavigate();
44+
4345
const { timezoneStore, scheduleStore, userStore } = store;
4446
const updatePersonalEventsLoading = useIsLoading(ActionKey.UPDATE_PERSONAL_EVENTS);
4547

@@ -77,7 +79,7 @@ const _SchedulePersonal: FC<SchedulePersonalProps> = observer(({ userPk, onSlotC
7779
};
7880

7981
const openSchedule = (event: Event) => {
80-
history.push(`${PLUGIN_ROOT}/schedules/${event.schedule?.id}`);
82+
navigate(`${PLUGIN_ROOT}/schedules/${event.schedule?.id}`);
8183
};
8284

8385
const currentTimeX = getCurrentTimeX(
@@ -172,4 +174,4 @@ const _SchedulePersonal: FC<SchedulePersonalProps> = observer(({ userPk, onSlotC
172174
);
173175
});
174176

175-
export const SchedulePersonal = withRouter(withTheme2(_SchedulePersonal));
177+
export const SchedulePersonal = withTheme2(_SchedulePersonal);

grafana-plugin/src/pages/NoMatch.tsx

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,22 @@
11
import React, { useEffect, useMemo } from 'react';
22

33
import qs from 'query-string';
4-
import { useHistory } from 'react-router-dom';
4+
import { useNavigate } from 'react-router-dom-v5-compat';
55

66
import { DEFAULT_PAGE, PLUGIN_ROOT } from 'utils/consts';
77
import { getPathFromQueryParams } from 'utils/url';
88

99
export const NoMatch = () => {
10-
const history = useHistory();
10+
const navigate = useNavigate();
1111

1212
const query = useMemo(() => qs.parse(window.location.search), [window.location.search]);
1313

1414
useEffect(() => {
1515
if (query.page) {
1616
const path = getPathFromQueryParams(query);
17-
history.push(path);
17+
navigate(path);
1818
} else {
19-
history.push(`${PLUGIN_ROOT}/${DEFAULT_PAGE}`);
19+
navigate(`${PLUGIN_ROOT}/${DEFAULT_PAGE}`);
2020
}
2121
}, [query]);
2222

grafana-plugin/src/pages/escalation-chains/EscalationChains.tsx

+33-16
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import React from 'react';
33
import { GrafanaTheme2 } from '@grafana/data';
44
import { Button, HorizontalGroup, Icon, IconButton, Tooltip, VerticalGroup, withTheme2 } from '@grafana/ui';
55
import { observer } from 'mobx-react';
6-
import { RouteComponentProps, withRouter } from 'react-router-dom';
76
import { getUtilStyles } from 'styles/utils.styles';
87

98
import { Collapse } from 'components/Collapse/Collapse';
@@ -30,10 +29,15 @@ import { PageProps, WithStoreProps } from 'state/types';
3029
import { withMobXProviderContext } from 'state/withStore';
3130
import { UserActions } from 'utils/authorization/authorization';
3231
import { PAGE, PLUGIN_ROOT } from 'utils/consts';
32+
import { PropsWithRouter, withRouter } from 'utils/hoc';
3333

3434
import { getEscalationChainStyles } from './EscalationChains.styles';
3535

36-
interface EscalationChainsPageProps extends WithStoreProps, PageProps, RouteComponentProps<{ id: string }> {
36+
interface RouteProps {
37+
id: string;
38+
}
39+
40+
interface EscalationChainsPageProps extends WithStoreProps, PageProps, PropsWithRouter<RouteProps> {
3741
theme: GrafanaTheme2;
3842
}
3943

@@ -60,7 +64,7 @@ class _EscalationChainsPage extends React.Component<EscalationChainsPageProps, E
6064

6165
const {
6266
store,
63-
match: {
67+
router: {
6468
params: { id },
6569
},
6670
} = this.props;
@@ -101,9 +105,11 @@ class _EscalationChainsPage extends React.Component<EscalationChainsPageProps, E
101105
};
102106

103107
handleEsclalationSelect = (id: EscalationChain['id']) => {
104-
const { history } = this.props;
108+
const {
109+
router: { navigate },
110+
} = this.props;
105111

106-
history.push(`${PLUGIN_ROOT}/escalations/${id}${window.location.search}`);
112+
navigate(`${PLUGIN_ROOT}/escalations/${id}${window.location.search}`);
107113
};
108114

109115
setSelectedEscalationChain = async (escalationChainId: EscalationChain['id']) => {
@@ -119,15 +125,17 @@ class _EscalationChainsPage extends React.Component<EscalationChainsPageProps, E
119125
};
120126

121127
componentDidUpdate(prevProps: EscalationChainsPageProps) {
122-
if (this.props.match.params.id !== prevProps.match.params.id) {
128+
const { router } = this.props;
129+
130+
if (router.params.id !== prevProps.router.params.id) {
123131
this.parseQueryParams();
124132
}
125133
}
126134

127135
render() {
128136
const {
129137
store,
130-
match: {
138+
router: {
131139
params: { id },
132140
},
133141
theme,
@@ -256,7 +264,7 @@ class _EscalationChainsPage extends React.Component<EscalationChainsPageProps, E
256264

257265
handleFiltersChange = (filters: FiltersValues, isOnMount = false) => {
258266
const {
259-
match: {
267+
router: {
260268
params: { id },
261269
},
262270
} = this.props;
@@ -272,15 +280,18 @@ class _EscalationChainsPage extends React.Component<EscalationChainsPageProps, E
272280
};
273281

274282
autoSelectEscalationChain = () => {
275-
const { store, history } = this.props;
283+
const {
284+
store,
285+
router: { navigate },
286+
} = this.props;
276287
const { selectedEscalationChain } = this.state;
277288
const { escalationChainStore } = store;
278289

279290
const searchResult = escalationChainStore.getSearchResult();
280291

281292
if (!searchResult.find((escalationChain: EscalationChain) => escalationChain.id === selectedEscalationChain)) {
282293
const id = searchResult[0]?.id;
283-
history.push(`${PLUGIN_ROOT}/escalations/${id || ''}${window.location.search}`);
294+
navigate(`${PLUGIN_ROOT}/escalations/${id || ''}${window.location.search}`);
284295
}
285296
};
286297

@@ -400,11 +411,13 @@ class _EscalationChainsPage extends React.Component<EscalationChainsPageProps, E
400411

401412
handleEscalationChainCreate = async (id: EscalationChain['id']) => {
402413
const { selectedEscalationChain } = this.state;
403-
const { history } = this.props;
414+
const {
415+
router: { navigate },
416+
} = this.props;
404417

405418
await this.applyFilters();
406419

407-
history.push(`${PLUGIN_ROOT}/escalations/${id}${window.location.search}`);
420+
navigate(`${PLUGIN_ROOT}/escalations/${id}${window.location.search}`);
408421

409422
// because this page wouldn't detect query.id change
410423
if (selectedEscalationChain === id) {
@@ -444,7 +457,10 @@ class _EscalationChainsPage extends React.Component<EscalationChainsPageProps, E
444457
};
445458

446459
handleDeleteEscalationChain = async () => {
447-
const { store, history } = this.props;
460+
const {
461+
store,
462+
router: { navigate },
463+
} = this.props;
448464
const { escalationChainStore } = store;
449465
const { selectedEscalationChain, extraEscalationChains } = this.state;
450466

@@ -464,10 +480,9 @@ class _EscalationChainsPage extends React.Component<EscalationChainsPageProps, E
464480
}
465481

466482
const escalationChains = escalationChainStore.getSearchResult();
467-
468483
const newSelected = escalationChains[index - 1] || escalationChains[0];
469484

470-
history.push(`${PLUGIN_ROOT}/escalations/${newSelected?.id || ''}${window.location.search}`);
485+
navigate(`${PLUGIN_ROOT}/escalations/${newSelected?.id || ''}${window.location.search}`);
471486
};
472487

473488
handleEscalationChainNameChange = (value: string) => {
@@ -480,4 +495,6 @@ class _EscalationChainsPage extends React.Component<EscalationChainsPageProps, E
480495
};
481496
}
482497

483-
export const EscalationChainsPage = withRouter(withMobXProviderContext(withTheme2(_EscalationChainsPage)));
498+
export const EscalationChainsPage = withRouter<RouteProps, Omit<EscalationChainsPageProps, 'store' | 'meta' | 'theme'>>(
499+
withMobXProviderContext(withTheme2(_EscalationChainsPage))
500+
);

0 commit comments

Comments
 (0)