Routing: Replace Redirect component with Navigate (#94072)

* Routing: Replace Redirect with Navigate

* Use replace state

* Update routes.tsx

* Fix test
This commit is contained in:
Alex Khomenko 2024-10-02 09:44:18 +03:00 committed by GitHub
parent 28d9cc7310
commit f55f7f2634
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 52 additions and 38 deletions

View File

@ -1,8 +1,8 @@
import { Action, KBarProvider } from 'kbar';
import { Component, ComponentType } from 'react';
import { Provider } from 'react-redux';
import { Redirect, Switch, RouteComponentProps } from 'react-router-dom';
import { CompatRoute } from 'react-router-dom-v5-compat';
import { Switch, RouteComponentProps } from 'react-router-dom';
import { CompatRoute, Navigate } from 'react-router-dom-v5-compat';
import { config, navigationLogger, reportInteraction } from '@grafana/runtime';
import { ErrorBoundaryAlert, GlobalStyles, PortalContainer } from '@grafana/ui';
@ -67,7 +67,7 @@ export class AppWrapper extends Component<AppWrapperProps, AppWrapperState> {
// TODO[Router]: test this logic
if (roles?.length) {
if (!roles.some((r: string) => contextSrv.hasRole(r))) {
return <Redirect to="/" />;
return <Navigate replace to="/" />;
}
}

View File

@ -1,7 +1,8 @@
import { css } from '@emotion/css';
import history from 'history';
import { useEffect, useState } from 'react';
import { Prompt, Redirect } from 'react-router-dom';
import { Prompt } from 'react-router-dom';
import { Navigate } from 'react-router-dom-v5-compat';
import { Button, Modal } from '@grafana/ui';
@ -80,7 +81,7 @@ export const FormPrompt = ({ confirmRedirect, onDiscard, onLocationChange }: Pro
return (
<>
<Prompt when={true} message={handleRedirect} />
{blockedLocation && changesDiscarded && <Redirect to={blockedLocation} />}
{blockedLocation && changesDiscarded && <Navigate replace to={blockedLocation} />}
<UnsavedChangesModal isOpen={modalIsOpen} onDiscard={onDiscardChanges} onBackToForm={onBackToForm} />
</>
);

View File

@ -1,5 +1,6 @@
import { useEffect, useState } from 'react';
import { Redirect, Route, Switch, useRouteMatch } from 'react-router-dom';
import { Route, Switch, useRouteMatch } from 'react-router-dom';
import { Navigate } from 'react-router-dom-v5-compat';
import { NavModelItem } from '@grafana/data';
import { useGetMuteTiming } from 'app/features/alerting/unified/components/mute-timings/useMuteTimings';
@ -23,7 +24,7 @@ const EditTimingRoute = () => {
});
if (!name) {
return <Redirect to="/alerting/routes" />;
return <Navigate replace to="/alerting/routes" />;
}
return (

View File

@ -12,9 +12,9 @@ import { getRulesSourceByName } from './utils/datasource';
jest.mock('./hooks/useCombinedRule');
jest.mock('./utils/datasource');
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
Redirect: jest.fn(({}) => `Redirected`),
jest.mock('react-router-dom-v5-compat', () => ({
...jest.requireActual('react-router-dom-v5-compat'),
Navigate: jest.fn(({}) => `Redirected`),
}));
jest.mock('react-use', () => ({

View File

@ -1,6 +1,6 @@
import { css } from '@emotion/css';
import { useMemo } from 'react';
import { Redirect } from 'react-router-dom';
import { Navigate } from 'react-router-dom-v5-compat';
import { useLocation } from 'react-use';
import { GrafanaTheme2 } from '@grafana/data';
@ -53,7 +53,7 @@ export function RedirectToRuleViewer(): JSX.Element | null {
} = useCloudCombinedRulesMatching(name, sourceName, { namespace, groupName: group });
if (!name || !sourceName) {
return <Redirect to="/notfound" />;
return <Navigate replace to="/notfound" />;
}
if (error) {
@ -95,7 +95,7 @@ export function RedirectToRuleViewer(): JSX.Element | null {
if (rules.length === 1) {
const [rule] = rules;
const to = createViewLink(rulesSource, rule, '/alerting/list').replace(subUrl, '');
return <Redirect to={to} />;
return <Navigate replace to={to} />;
}
if (rules.length === 0) {

View File

@ -1,5 +1,6 @@
import { forwardRef, useState } from 'react';
import { Redirect, useLocation } from 'react-router-dom';
import { useLocation } from 'react-router-dom';
import { Navigate } from 'react-router-dom-v5-compat';
import { Button, ConfirmModal } from '@grafana/ui';
import { RuleIdentifier } from 'app/types/unified-alerting';
@ -33,7 +34,7 @@ export function RedirectToCloneRule({
returnTo: redirectTo ? returnTo : '',
});
return <Redirect to={`/alerting/new?` + queryParams.toString()} push />;
return <Navigate to={`/alerting/new?` + queryParams.toString()} replace={false} />;
}
return (

View File

@ -1,4 +1,5 @@
import { Redirect, Route, Switch, useLocation } from 'react-router-dom';
import { Route, Switch, useLocation } from 'react-router-dom';
import { Navigate } from 'react-router-dom-v5-compat';
import { DataSourcesRoutesContext } from 'app/features/datasources/state';
import { StoreState, useSelector } from 'app/types';
@ -16,7 +17,8 @@ import {
function RedirectToAddNewConnection() {
const { search } = useLocation();
return (
<Redirect
<Navigate
replace
to={{
pathname: ROUTES.AddNewConnection,
search,
@ -40,7 +42,7 @@ export default function Connections() {
>
<Switch>
{/* Redirect to "Add new connection" by default */}
<Route exact sensitive path={ROUTES.Base} component={() => <Redirect to={ROUTES.AddNewConnection} />} />
<Route exact sensitive path={ROUTES.Base} component={() => <Navigate replace to={ROUTES.AddNewConnection} />} />
<Route exact sensitive path={ROUTES.DataSources} component={DataSourcesListPage} />
<Route exact sensitive path={ROUTES.DataSourcesNew} component={NewDataSourcePage} />
<Route exact sensitive path={ROUTES.DataSourcesDetails} component={DataSourceDetailsPage} />
@ -54,11 +56,14 @@ export default function Connections() {
{/* Redirect from earlier routes to updated routes */}
<Route exact path={ROUTES.ConnectDataOutdated} component={RedirectToAddNewConnection} />
<Redirect from={`${ROUTES.Base}/your-connections/:page`} to={`${ROUTES.Base}/:page`} />
<Redirect from={ROUTES.YourConnectionsOutdated} to={ROUTES.DataSources} />
<Route
path={`${ROUTES.Base}/your-connections/:page`}
component={() => <Navigate replace to={`${ROUTES.Base}/:page`} />}
/>
<Route path={ROUTES.YourConnectionsOutdated} component={() => <Navigate replace to={ROUTES.DataSources} />} />
{/* Not found */}
<Route component={() => <Redirect to="/notfound" />} />
<Route component={() => <Navigate replace to="/notfound" />} />
</Switch>
</DataSourcesRoutesContext.Provider>
);

View File

@ -1,6 +1,6 @@
import { css } from '@emotion/css';
import { useState } from 'react';
import { Redirect } from 'react-router-dom';
import { Navigate } from 'react-router-dom-v5-compat';
import { GrafanaTheme2 } from '@grafana/data';
import { SceneComponentProps, sceneGraph, SceneObject, SceneObjectBase, SceneObjectState } from '@grafana/scenes';
@ -57,7 +57,7 @@ export class DataTrailsHome extends SceneObjectBase<DataTrailsHomeState> {
// If there are no recent trails, don't show home page and create a new trail
if (!getTrailStore().recent.length) {
const trail = newMetricsTrail(getDatasourceForNewTrail());
return <Redirect to={getUrlForTrail(trail)} />;
return <Navigate replace to={getUrlForTrail(trail)} />;
}
return (

View File

@ -1,4 +1,4 @@
import { Redirect, RouteComponentProps } from 'react-router-dom';
import { Navigate, useParams } from 'react-router-dom-v5-compat';
import { isTruthy } from '@grafana/data';
import { NavLandingPage } from 'app/core/components/NavLandingPage/NavLandingPage';
@ -107,23 +107,19 @@ export function getAppRoutes(): RouteDescriptor[] {
},
{
path: DATASOURCES_ROUTES.List,
component: () => <Redirect to={CONNECTIONS_ROUTES.DataSources} />,
component: () => <Navigate replace to={CONNECTIONS_ROUTES.DataSources} />,
},
{
path: DATASOURCES_ROUTES.Edit,
component: (props: RouteComponentProps<{ uid: string }>) => (
<Redirect to={CONNECTIONS_ROUTES.DataSourcesEdit.replace(':uid', props.match.params.uid)} />
),
component: DataSourceEditRoute,
},
{
path: DATASOURCES_ROUTES.Dashboards,
component: (props: RouteComponentProps<{ uid: string }>) => (
<Redirect to={CONNECTIONS_ROUTES.DataSourcesDashboards.replace(':uid', props.match.params.uid)} />
),
component: DataSourceDashboardRoute,
},
{
path: DATASOURCES_ROUTES.New,
component: () => <Redirect to={CONNECTIONS_ROUTES.DataSourcesNew} />,
component: () => <Navigate replace to={CONNECTIONS_ROUTES.DataSourcesNew} />,
},
{
path: '/datasources/correlations',
@ -219,7 +215,7 @@ export function getAppRoutes(): RouteDescriptor[] {
{
path: '/org/users',
// Org users page has been combined with admin users
component: () => <Redirect to={'/admin/users'} />,
component: () => <Navigate replace to={'/admin/users'} />,
},
{
path: '/org/users/invite',
@ -292,7 +288,7 @@ export function getAppRoutes(): RouteDescriptor[] {
() =>
import(/* webpackChunkName: "AdminAuthentication" */ '../features/auth-config/AuthProvidersListPage')
)
: () => <Redirect to="/admin" />,
: () => <Navigate replace to="/admin" />,
},
{
path: '/admin/authentication/ldap',
@ -309,7 +305,7 @@ export function getAppRoutes(): RouteDescriptor[] {
? SafeDynamicImport(
() => import(/* webpackChunkName: "AdminAuthentication" */ '../features/auth-config/ProviderConfigPage')
)
: () => <Redirect to="/admin" />,
: () => <Navigate replace to="/admin" />,
},
{
path: '/admin/settings',
@ -357,7 +353,7 @@ export function getAppRoutes(): RouteDescriptor[] {
? SafeDynamicImport(
() => import(/* webpackChunkName: "AdminFeatureTogglesPage" */ 'app/features/admin/AdminFeatureTogglesPage')
)
: () => <Redirect to="/admin" />,
: () => <Navigate replace to="/admin" />,
},
{
path: '/admin/storage/:path*',
@ -398,7 +394,7 @@ export function getAppRoutes(): RouteDescriptor[] {
{
path: '/verify',
component: !config.verifyEmailEnabled
? () => <Redirect to="/signup" />
? () => <Navigate replace to="/signup" />
: SafeDynamicImport(
() => import(/* webpackChunkName "VerifyEmailPage"*/ 'app/core/components/Signup/VerifyEmailPage')
),
@ -408,7 +404,7 @@ export function getAppRoutes(): RouteDescriptor[] {
{
path: '/signup',
component: config.disableUserSignUp
? () => <Redirect to="/login" />
? () => <Navigate replace to="/login" />
: SafeDynamicImport(() => import(/* webpackChunkName "SignupPage"*/ 'app/core/components/Signup/SignupPage')),
pageClass: 'login-page',
chromeless: true,
@ -556,3 +552,13 @@ export function getSupportBundleRoutes(cfg = config): RouteDescriptor[] {
},
];
}
function DataSourceDashboardRoute() {
const { uid = '' } = useParams();
return <Navigate replace to={CONNECTIONS_ROUTES.DataSourcesDashboards.replace(':uid', uid)} />;
}
function DataSourceEditRoute() {
const { uid = '' } = useParams();
return <Navigate replace to={CONNECTIONS_ROUTES.DataSourcesEdit.replace(':uid', uid)} />;
}