TopNav: New page layouts (#51510)

* First stab at new page layouts behind feature toggle

* Simplifying PageHeader

* Progress on a new model that can more easily support new and old page layouts

* Progress

* rename folder

* Progress

* Minor change

* fixes

* Fixing tests

* Make breadcrumbs work

* Add tests for old Page component

* Adding tests for new Page component and behavior

* fixing page header test

* Fixed test

* AppChrome outside route

* Renaming folder

* Minor fix

* Updated

* Fixing StoragePage

* Fix for banners

Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
This commit is contained in:
Torkel Ödegaard
2022-07-06 17:00:56 +02:00
committed by GitHub
co-authored by Ashley Harrison
parent 663f3fcd2a
commit 1e85a6f4fd
106 changed files with 927 additions and 347 deletions
+6 -17
View File
@@ -2,9 +2,9 @@ import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
import { NavModel } from '@grafana/data';
import { BackendSrv, setBackendSrv } from '@grafana/runtime';
import { CreateTeam, Props } from './CreateTeam';
import { CreateTeam } from './CreateTeam';
beforeEach(() => {
jest.clearAllMocks();
@@ -14,23 +14,12 @@ const mockPost = jest.fn(() => {
return Promise.resolve({});
});
jest.mock('@grafana/runtime', () => ({
getBackendSrv: () => {
return {
post: mockPost,
};
},
config: {
buildInfo: {},
licenseInfo: {},
},
}));
setBackendSrv({
post: mockPost,
} as any as BackendSrv);
const setup = () => {
const props: Props = {
navModel: { node: {}, main: {} } as NavModel,
};
return render(<CreateTeam {...props} />);
return render(<CreateTeam />);
};
describe('Create team', () => {
+4 -20
View File
@@ -1,24 +1,16 @@
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import { NavModel } from '@grafana/data';
import { getBackendSrv, locationService } from '@grafana/runtime';
import { Button, Form, Field, Input, FieldSet } from '@grafana/ui';
import Page from 'app/core/components/Page/Page';
import { Page } from 'app/core/components/Page/Page';
import { contextSrv } from 'app/core/core';
import { getNavModel } from 'app/core/selectors/navModel';
import { StoreState } from 'app/types';
export interface Props {
navModel: NavModel;
}
interface TeamDTO {
name: string;
email: string;
}
export class CreateTeam extends PureComponent<Props> {
export class CreateTeam extends PureComponent {
create = async (formModel: TeamDTO) => {
const result = await getBackendSrv().post('/api/teams', formModel);
if (result.teamId) {
@@ -27,10 +19,8 @@ export class CreateTeam extends PureComponent<Props> {
}
};
render() {
const { navModel } = this.props;
return (
<Page navModel={navModel}>
<Page navId="teams">
<Page.Contents>
<Form onSubmit={this.create}>
{({ register, errors }) => (
@@ -58,10 +48,4 @@ export class CreateTeam extends PureComponent<Props> {
}
}
function mapStateToProps(state: StoreState) {
return {
navModel: getNavModel(state.navIndex, 'teams'),
};
}
export default connect(mapStateToProps)(CreateTeam);
export default CreateTeam;
@@ -3,7 +3,6 @@ import userEvent from '@testing-library/user-event';
import React from 'react';
import { mockToolkitActionCreator } from 'test/core/redux/mocks';
import { NavModel } from '@grafana/data';
import { contextSrv, User } from 'app/core/services/context_srv';
import { OrgRole, Team } from '../../types';
@@ -20,14 +19,6 @@ jest.mock('app/core/config', () => {
const setup = (propOverrides?: object) => {
const props: Props = {
navModel: {
main: {
text: 'Configuration',
},
node: {
text: 'Team List',
},
} as NavModel,
teams: [] as Team[],
loadTeams: jest.fn(),
deleteTeam: jest.fn(),
+3 -7
View File
@@ -1,13 +1,11 @@
import React, { PureComponent } from 'react';
import { NavModel } from '@grafana/data';
import { DeleteButton, LinkButton, FilterInput, VerticalGroup, HorizontalGroup, Pagination } from '@grafana/ui';
import EmptyListCTA from 'app/core/components/EmptyListCTA/EmptyListCTA';
import Page from 'app/core/components/Page/Page';
import { Page } from 'app/core/components/Page/Page';
import { TeamRolePicker } from 'app/core/components/RolePicker/TeamRolePicker';
import { fetchRoleOptions } from 'app/core/components/RolePicker/api';
import { config } from 'app/core/config';
import { getNavModel } from 'app/core/selectors/navModel';
import { contextSrv, User } from 'app/core/services/context_srv';
import { AccessControlAction, Role, StoreState, Team } from 'app/types';
@@ -20,7 +18,6 @@ import { getSearchQuery, getTeams, getTeamsCount, getTeamsSearchPage, isPermissi
const pageLimit = 30;
export interface Props {
navModel: NavModel;
teams: Team[];
searchQuery: string;
searchPage: number;
@@ -224,10 +221,10 @@ export class TeamList extends PureComponent<Props, State> {
}
render() {
const { hasFetched, navModel } = this.props;
const { hasFetched } = this.props;
return (
<Page navModel={navModel}>
<Page navId="teams">
<Page.Contents isLoading={!hasFetched}>{this.renderList()}</Page.Contents>
</Page>
);
@@ -236,7 +233,6 @@ export class TeamList extends PureComponent<Props, State> {
function mapStateToProps(state: StoreState) {
return {
navModel: getNavModel(state.navIndex, 'teams'),
teams: getTeams(state.teams),
searchQuery: getSearchQuery(state.teams),
searchPage: getTeamsSearchPage(state.teams),
+2 -2
View File
@@ -2,7 +2,7 @@ import { render, screen } from '@testing-library/react';
import React from 'react';
import { Provider } from 'react-redux';
import { NavModel, createTheme } from '@grafana/data';
import { createTheme } from '@grafana/data';
import { getRouteComponentProps } from 'app/core/navigation/__mocks__/routeProps';
import { User } from 'app/core/services/context_srv';
import { configureStore } from 'app/store/configureStore';
@@ -68,7 +68,7 @@ const setup = (propOverrides?: object) => {
},
} as any,
}),
navModel: { node: {}, main: {} } as NavModel,
pageNav: { text: 'Cool team ' },
teamId: 1,
loadTeam: jest.fn(),
loadTeamMembers: jest.fn(),
+11 -11
View File
@@ -2,10 +2,10 @@ import { includes } from 'lodash';
import React, { PureComponent } from 'react';
import { connect, ConnectedProps } from 'react-redux';
import { NavModel } from '@grafana/data';
import { NavModelItem } from '@grafana/data';
import { featureEnabled } from '@grafana/runtime';
import { Themeable2, withTheme2 } from '@grafana/ui';
import Page from 'app/core/components/Page/Page';
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';
@@ -51,11 +51,11 @@ function mapStateToProps(state: StoreState, props: OwnProps) {
}
const pageName = props.match.params.page ?? defaultPage;
const teamLoadingNav = getTeamLoadingNav(pageName as string);
const navModel = getNavModel(state.navIndex, `team-${pageName}-${teamId}`, teamLoadingNav);
const pageNav = getNavModel(state.navIndex, `team-${pageName}-${teamId}`, teamLoadingNav).main;
const members = getTeamMembers(state.team);
return {
navModel,
pageNav,
teamId: teamId,
pageName: pageName,
team,
@@ -118,20 +118,20 @@ export class TeamPages extends PureComponent<Props, State> {
return text1.toLocaleLowerCase() === text2.toLocaleLowerCase();
};
hideTabsFromNonTeamAdmin = (navModel: NavModel, isSignedInUserTeamAdmin: boolean) => {
hideTabsFromNonTeamAdmin = (pageNav: NavModelItem, isSignedInUserTeamAdmin: boolean) => {
if (contextSrv.accessControlEnabled()) {
return navModel;
return pageNav;
}
if (!isSignedInUserTeamAdmin && navModel.main && navModel.main.children) {
navModel.main.children
if (!isSignedInUserTeamAdmin && pageNav && pageNav.children) {
pageNav.children
.filter((navItem) => !this.textsAreEqual(navItem.text, PageTypes.Members))
.map((navItem) => {
navItem.hideFromTabs = true;
});
}
return navModel;
return pageNav;
};
renderPage(isSignedInUserTeamAdmin: boolean): React.ReactNode {
@@ -183,11 +183,11 @@ export class TeamPages extends PureComponent<Props, State> {
}
render() {
const { team, navModel, members, editorsCanAdmin, signedInUser } = this.props;
const { team, pageNav, members, editorsCanAdmin, signedInUser } = this.props;
const isTeamAdmin = isSignedInUserTeamAdmin({ members, editorsCanAdmin, signedInUser });
return (
<Page navModel={this.hideTabsFromNonTeamAdmin(navModel, isTeamAdmin)}>
<Page navId="teams" pageNav={this.hideTabsFromNonTeamAdmin(pageNav, isTeamAdmin)}>
<Page.Contents isLoading={this.state.isLoading}>
{team && Object.keys(team).length !== 0 && this.renderPage(isTeamAdmin)}
</Page.Contents>
@@ -18,7 +18,7 @@ const TeamPermissions = (props: TeamPermissionsProps) => {
return (
<Permissions
title="Members"
title=""
addPermissionTitle="Add member"
buttonLabel="Add member"
resource="teams"