From 25ebb5b76fbd268e3cf8675517d7a9ef62cfe5e3 Mon Sep 17 00:00:00 2001 From: Alex Khomenko Date: Wed, 11 Sep 2024 10:02:11 +0300 Subject: [PATCH] Users/teams: Update to be compatible with react router 6 (#93163) * UserCreatePage: Make compatible with react router 6 * ServiceAccountPage.test: Make compatible with react router 6 * Update TeamPages --- public/app/features/admin/UserCreatePage.tsx | 8 +++---- .../ServiceAccountPage.test.tsx | 23 ++++++++++++++++--- public/app/features/teams/TeamPages.test.tsx | 20 +++++++--------- public/app/features/teams/TeamPages.tsx | 18 +++++++-------- public/app/features/teams/state/actions.ts | 2 +- 5 files changed, 42 insertions(+), 29 deletions(-) diff --git a/public/app/features/admin/UserCreatePage.tsx b/public/app/features/admin/UserCreatePage.tsx index b252c215edd..68daf784c09 100644 --- a/public/app/features/admin/UserCreatePage.tsx +++ b/public/app/features/admin/UserCreatePage.tsx @@ -1,6 +1,6 @@ import { useCallback } from 'react'; import { useForm } from 'react-hook-form'; -import { useHistory } from 'react-router-dom'; +import { useNavigate } from 'react-router-dom-v5-compat'; import { NavModelItem } from '@grafana/data'; import { getBackendSrv } from '@grafana/runtime'; @@ -24,7 +24,7 @@ const pageNav: NavModelItem = { }; const UserCreatePage = () => { - const history = useHistory(); + const navigate = useNavigate(); const { handleSubmit, register, @@ -35,9 +35,9 @@ const UserCreatePage = () => { async (data: UserDTO) => { const { id } = await createUser(data); - history.push(`/admin/users/edit/${id}`); + navigate(`/admin/users/edit/${id}`); }, - [history] + [navigate] ); return ( diff --git a/public/app/features/serviceaccounts/ServiceAccountPage.test.tsx b/public/app/features/serviceaccounts/ServiceAccountPage.test.tsx index e39d5c33daf..2e6478f738a 100644 --- a/public/app/features/serviceaccounts/ServiceAccountPage.test.tsx +++ b/public/app/features/serviceaccounts/ServiceAccountPage.test.tsx @@ -1,6 +1,5 @@ import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -import { History, Location } from 'history'; import { TestProvider } from 'test/helpers/TestProvider'; import { RouteDescriptor } from 'app/core/navigation/types'; @@ -24,6 +23,12 @@ const setup = (propOverrides: Partial) => { const loadServiceAccountTokensMock = jest.fn(); const updateServiceAccountMock = jest.fn(); + const mockLocation = { + search: '', + pathname: '', + state: undefined, + hash: '', + }; const props: Props = { serviceAccount: {} as ServiceAccountDTO, tokens: [], @@ -34,8 +39,20 @@ const setup = (propOverrides: Partial) => { path: '/org/serviceaccounts/1', url: 'http://localhost:3000/org/serviceaccounts/1', }, - history: {} as History, - location: {} as Location, + history: { + length: 0, + action: 'PUSH', + location: mockLocation, + push: jest.fn(), + replace: jest.fn(), + go: jest.fn(), + goBack: jest.fn(), + goForward: jest.fn(), + block: jest.fn(), + listen: jest.fn(), + createHref: jest.fn(), + }, + location: mockLocation, queryParams: {}, route: {} as RouteDescriptor, timezone: '', diff --git a/public/app/features/teams/TeamPages.test.tsx b/public/app/features/teams/TeamPages.test.tsx index f053d113434..9c3de64e177 100644 --- a/public/app/features/teams/TeamPages.test.tsx +++ b/public/app/features/teams/TeamPages.test.tsx @@ -1,9 +1,7 @@ import { screen } from '@testing-library/react'; -import { Route, Router } from 'react-router-dom'; +import { useParams } from 'react-router-dom-v5-compat'; import { render } from 'test/test-utils'; -import { locationService } from '@grafana/runtime'; - import TeamPages from './TeamPages'; import { getMockTeam } from './__mocks__/teamMocks'; @@ -58,18 +56,16 @@ jest.mock('./TeamGroupSync', () => { return () =>
Team group sync
; }); +jest.mock('react-router-dom-v5-compat', () => ({ + ...jest.requireActual('react-router-dom-v5-compat'), + useParams: jest.fn(), +})); + const setup = (propOverrides: { teamId?: number; pageName?: string } = {}) => { const pageName = propOverrides.pageName ?? 'members'; const teamId = propOverrides.teamId ?? 1; - locationService.push({ pathname: `/org/teams/edit/${teamId}/${pageName}` }); - - render( - - - - - - ); + (useParams as jest.Mock).mockReturnValue({ id: `${teamId}`, page: pageName }); + render(); }; describe('TeamPages', () => { diff --git a/public/app/features/teams/TeamPages.tsx b/public/app/features/teams/TeamPages.tsx index e2919ee3b79..8004f19f683 100644 --- a/public/app/features/teams/TeamPages.tsx +++ b/public/app/features/teams/TeamPages.tsx @@ -1,6 +1,6 @@ import { createSelector } from '@reduxjs/toolkit'; -import { memo, useMemo, useRef } from 'react'; -import { useParams } from 'react-router'; +import { memo, useRef } from 'react'; +import { useParams } from 'react-router-dom-v5-compat'; import { useAsync } from 'react-use'; import { featureEnabled } from '@grafana/runtime'; @@ -18,10 +18,10 @@ import { loadTeam } from './state/actions'; import { getTeamLoadingNav } from './state/navModel'; import { getTeam } from './state/selectors'; -interface TeamPageRouteParams { +type TeamPageRouteParams = { id: string; page?: string; -} +}; enum PageTypes { Members = 'members', @@ -32,7 +32,7 @@ enum PageTypes { const PAGES = ['members', 'settings', 'groupsync']; const teamSelector = createSelector( - [(state: StoreState) => state.team, (_: StoreState, teamId: number) => teamId], + [(state: StoreState) => state.team, (_: StoreState, teamId: string) => teamId], (team, teamId) => getTeam(team, teamId) ); @@ -40,7 +40,7 @@ const pageNavSelector = createSelector( [ (state: StoreState) => state.navIndex, (_state: StoreState, pageName: string) => pageName, - (_state: StoreState, _pageName: string, teamId: number) => teamId, + (_state: StoreState, _pageName: string, teamId: string) => teamId, ], (navIndex, pageName, teamId) => { const teamLoadingNav = getTeamLoadingNav(pageName); @@ -50,8 +50,7 @@ const pageNavSelector = createSelector( const TeamPages = memo(() => { const isSyncEnabled = useRef(featureEnabled('teamsync')); - const params = useParams(); - const teamId = useMemo(() => parseInt(params.id, 10), [params]); + const { id: teamId = '', page } = useParams(); const team = useSelector((state) => teamSelector(state, teamId)); let defaultPage = 'members'; @@ -59,7 +58,7 @@ const TeamPages = memo(() => { if (!team || !contextSrv.hasPermissionInMetadata(AccessControlAction.ActionTeamsPermissionsRead, team)) { defaultPage = 'settings'; } - const pageName = params.page ?? defaultPage; + const pageName = page ?? defaultPage; const pageNav = useSelector((state) => pageNavSelector(state, pageName, teamId)); const dispatch = useDispatch(); @@ -83,6 +82,7 @@ const TeamPages = memo(() => { if (canReadTeamPermissions) { return ; } + return null; case PageTypes.Settings: return canReadTeam && ; case PageTypes.GroupSync: diff --git a/public/app/features/teams/state/actions.ts b/public/app/features/teams/state/actions.ts index fc745433f28..81a1986af61 100644 --- a/public/app/features/teams/state/actions.ts +++ b/public/app/features/teams/state/actions.ts @@ -60,7 +60,7 @@ export function loadTeams(initial = false): ThunkResult { const loadTeamsWithDebounce = debounce((dispatch) => dispatch(loadTeams()), 500); -export function loadTeam(id: number): ThunkResult> { +export function loadTeam(id: string | number): ThunkResult> { return async (dispatch) => { const response = await getBackendSrv().get(`/api/teams/${id}`, accessControlQueryParam()); dispatch(teamLoaded(response));