mirror of
https://github.com/grafana/grafana.git
synced 2026-08-26 05:17:26 -05:00
Chore: Convert TeamPages from class component to function component (#87602)
This commit is contained in:
@@ -1,14 +1,11 @@
|
||||
import { render, screen } from '@testing-library/react';
|
||||
import { screen } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
import { match } from 'react-router-dom';
|
||||
import { TestProvider } from 'test/helpers/TestProvider';
|
||||
import { Route, Router } from 'react-router-dom';
|
||||
import { render } from 'test/test-utils';
|
||||
|
||||
import { createTheme } from '@grafana/data';
|
||||
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
|
||||
import { Team } from '../../types';
|
||||
|
||||
import { Props, TeamPages } from './TeamPages';
|
||||
import TeamPages from './TeamPages';
|
||||
import { getMockTeam } from './__mocks__/teamMocks';
|
||||
|
||||
jest.mock('app/core/components/Select/UserPicker', () => {
|
||||
@@ -26,7 +23,7 @@ jest.mock('app/core/services/context_srv', () => ({
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getBackendSrv: () => ({
|
||||
get: jest.fn().mockResolvedValue([{ userId: 1, login: 'Test' }]),
|
||||
get: jest.fn().mockResolvedValue(getMockTeam()),
|
||||
}),
|
||||
config: {
|
||||
...jest.requireActual('@grafana/runtime').config,
|
||||
@@ -62,43 +59,24 @@ jest.mock('./TeamGroupSync', () => {
|
||||
return () => <div>Team group sync</div>;
|
||||
});
|
||||
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props: Props = {
|
||||
...getRouteComponentProps({
|
||||
match: {
|
||||
params: {
|
||||
id: '1',
|
||||
page: null,
|
||||
},
|
||||
} as unknown as match,
|
||||
}),
|
||||
pageNav: { text: 'Cool team ' },
|
||||
teamId: 1,
|
||||
loadTeam: jest.fn(),
|
||||
pageName: 'members',
|
||||
team: {} as Team,
|
||||
theme: createTheme(),
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
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(
|
||||
<TestProvider>
|
||||
<TeamPages {...props} />
|
||||
</TestProvider>
|
||||
<Router history={locationService.getHistory()}>
|
||||
<Route path="/org/teams/edit/:id/:page?">
|
||||
<TeamPages />
|
||||
</Route>
|
||||
</Router>
|
||||
);
|
||||
};
|
||||
|
||||
describe('TeamPages', () => {
|
||||
it('should render settings and preferences page', async () => {
|
||||
setup({
|
||||
team: getMockTeam(),
|
||||
pageName: 'settings',
|
||||
preferences: {
|
||||
homeDashboardUID: 'home-dashboard',
|
||||
theme: 'Default',
|
||||
timezone: 'Default',
|
||||
},
|
||||
});
|
||||
|
||||
expect(await screen.findByText('Team settings')).toBeInTheDocument();
|
||||
@@ -106,7 +84,6 @@ describe('TeamPages', () => {
|
||||
|
||||
it('should render group sync page', async () => {
|
||||
setup({
|
||||
team: getMockTeam(),
|
||||
pageName: 'groupsync',
|
||||
});
|
||||
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import { includes } from 'lodash';
|
||||
import React, { PureComponent } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import { createSelector } from '@reduxjs/toolkit';
|
||||
import React, { useMemo, useRef } from 'react';
|
||||
import { useParams } from 'react-router';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { featureEnabled } from '@grafana/runtime';
|
||||
import { Themeable2, withTheme2 } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { UpgradeBox } from 'app/core/components/Upgrade/UpgradeBox';
|
||||
import config from 'app/core/config';
|
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { AccessControlAction, StoreState } from 'app/types';
|
||||
import { AccessControlAction, StoreState, useDispatch, useSelector } from 'app/types';
|
||||
|
||||
import TeamGroupSync, { TeamSyncUpgradeContent } from './TeamGroupSync';
|
||||
import TeamPermissions from './TeamPermissions';
|
||||
@@ -21,14 +20,7 @@ import { getTeam } from './state/selectors';
|
||||
|
||||
interface TeamPageRouteParams {
|
||||
id: string;
|
||||
page: string | null;
|
||||
}
|
||||
|
||||
export interface OwnProps extends GrafanaRouteComponentProps<TeamPageRouteParams>, Themeable2 {}
|
||||
|
||||
interface State {
|
||||
isSyncEnabled: boolean;
|
||||
isLoading: boolean;
|
||||
page?: string;
|
||||
}
|
||||
|
||||
enum PageTypes {
|
||||
@@ -37,78 +29,44 @@ enum PageTypes {
|
||||
GroupSync = 'groupsync',
|
||||
}
|
||||
|
||||
function mapStateToProps(state: StoreState, props: OwnProps) {
|
||||
const teamId = parseInt(props.match.params.id, 10);
|
||||
const team = getTeam(state.team, teamId);
|
||||
const PAGES = ['members', 'settings', 'groupsync'];
|
||||
|
||||
const teamSelector = createSelector(
|
||||
[(state: StoreState) => state.team, (_: StoreState, teamId: number) => teamId],
|
||||
(team, teamId) => getTeam(team, teamId)
|
||||
);
|
||||
|
||||
const pageNavSelector = createSelector(
|
||||
[
|
||||
(state: StoreState) => state.navIndex,
|
||||
(_state: StoreState, pageName: string) => pageName,
|
||||
(_state: StoreState, _pageName: string, teamId: number) => teamId,
|
||||
],
|
||||
(navIndex, pageName, teamId) => {
|
||||
const teamLoadingNav = getTeamLoadingNav(pageName);
|
||||
return getNavModel(navIndex, `team-${pageName}-${teamId}`, teamLoadingNav).main;
|
||||
}
|
||||
);
|
||||
|
||||
const TeamPages = React.memo(() => {
|
||||
const isSyncEnabled = useRef(featureEnabled('teamsync'));
|
||||
const params = useParams<TeamPageRouteParams>();
|
||||
const teamId = useMemo(() => parseInt(params.id, 10), [params]);
|
||||
const team = useSelector((state) => teamSelector(state, teamId));
|
||||
|
||||
let defaultPage = 'members';
|
||||
// With RBAC the settings page will always be available
|
||||
if (!team || !contextSrv.hasPermissionInMetadata(AccessControlAction.ActionTeamsPermissionsRead, team)) {
|
||||
defaultPage = 'settings';
|
||||
}
|
||||
const pageName = props.match.params.page ?? defaultPage;
|
||||
const teamLoadingNav = getTeamLoadingNav(pageName);
|
||||
const pageNav = getNavModel(state.navIndex, `team-${pageName}-${teamId}`, teamLoadingNav).main;
|
||||
const pageName = params.page ?? defaultPage;
|
||||
const pageNav = useSelector((state) => pageNavSelector(state, pageName, teamId));
|
||||
|
||||
return {
|
||||
pageNav,
|
||||
teamId: teamId,
|
||||
pageName: pageName,
|
||||
team,
|
||||
};
|
||||
}
|
||||
const dispatch = useDispatch();
|
||||
const { loading: isLoading } = useAsync(async () => dispatch(loadTeam(teamId)), [teamId]);
|
||||
|
||||
const mapDispatchToProps = {
|
||||
loadTeam,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
export type Props = OwnProps & ConnectedProps<typeof connector>;
|
||||
|
||||
export class TeamPages extends PureComponent<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
isLoading: false,
|
||||
isSyncEnabled: featureEnabled('teamsync'),
|
||||
};
|
||||
}
|
||||
|
||||
async componentDidMount() {
|
||||
await this.fetchTeam();
|
||||
}
|
||||
|
||||
async fetchTeam() {
|
||||
const { loadTeam, teamId } = this.props;
|
||||
this.setState({ isLoading: true });
|
||||
const team = await loadTeam(teamId);
|
||||
this.setState({ isLoading: false });
|
||||
return team;
|
||||
}
|
||||
|
||||
getCurrentPage() {
|
||||
const pages = ['members', 'settings', 'groupsync'];
|
||||
const currentPage = this.props.pageName;
|
||||
return includes(pages, currentPage) ? currentPage : pages[0];
|
||||
}
|
||||
|
||||
textsAreEqual = (text1: string, text2: string) => {
|
||||
if (!text1 && !text2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!text1 || !text2) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return text1.toLocaleLowerCase() === text2.toLocaleLowerCase();
|
||||
};
|
||||
|
||||
renderPage(): React.ReactNode {
|
||||
const { isSyncEnabled } = this.state;
|
||||
const { team } = this.props;
|
||||
const currentPage = this.getCurrentPage();
|
||||
const renderPage = () => {
|
||||
const currentPage = PAGES.includes(pageName) ? pageName : PAGES[0];
|
||||
|
||||
const canReadTeam = contextSrv.hasPermissionInMetadata(AccessControlAction.ActionTeamsRead, team!);
|
||||
const canReadTeamPermissions = contextSrv.hasPermissionInMetadata(
|
||||
@@ -128,7 +86,7 @@ export class TeamPages extends PureComponent<Props, State> {
|
||||
case PageTypes.Settings:
|
||||
return canReadTeam && <TeamSettings team={team!} />;
|
||||
case PageTypes.GroupSync:
|
||||
if (isSyncEnabled) {
|
||||
if (isSyncEnabled.current) {
|
||||
if (canReadTeamPermissions) {
|
||||
return <TeamGroupSync isReadOnly={!canWriteTeamPermissions} />;
|
||||
}
|
||||
@@ -143,19 +101,15 @@ export class TeamPages extends PureComponent<Props, State> {
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { team, pageNav } = this.props;
|
||||
return (
|
||||
<Page navId="teams" pageNav={pageNav}>
|
||||
<Page.Contents isLoading={isLoading}>{team && Object.keys(team).length !== 0 && renderPage()}</Page.Contents>
|
||||
</Page>
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
<Page navId="teams" pageNav={pageNav}>
|
||||
<Page.Contents isLoading={this.state.isLoading}>
|
||||
{team && Object.keys(team).length !== 0 && this.renderPage()}
|
||||
</Page.Contents>
|
||||
</Page>
|
||||
);
|
||||
}
|
||||
}
|
||||
TeamPages.displayName = 'TeamPages';
|
||||
|
||||
export default connector(withTheme2(TeamPages));
|
||||
export default TeamPages;
|
||||
|
||||
@@ -60,7 +60,7 @@ export function loadTeams(initial = false): ThunkResult<void> {
|
||||
|
||||
const loadTeamsWithDebounce = debounce((dispatch) => dispatch(loadTeams()), 500);
|
||||
|
||||
export function loadTeam(id: number): ThunkResult<void> {
|
||||
export function loadTeam(id: number): ThunkResult<Promise<void>> {
|
||||
return async (dispatch) => {
|
||||
const response = await getBackendSrv().get(`/api/teams/${id}`, accessControlQueryParam());
|
||||
dispatch(teamLoaded(response));
|
||||
|
||||
Reference in New Issue
Block a user