mirror of
https://github.com/grafana/grafana.git
synced 2024-11-28 03:34:15 -06:00
PageLayouts: Updates dashboard section routes with navId (#52175)
* 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 * Moving user profile routes to navId * PageLayouts: Updates dashboards routes with navId * added missing navId * AppChrome outside route * Renaming folder * Minor fix * Updated * Fixing StoragePage * Updated * Updating translation ids * Updated snapshot * update nav translation ids (yes this is confusing) Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com> Co-authored-by: joshhunt <josh@trtr.co>
This commit is contained in:
parent
320262c3db
commit
77f7e8dafc
@ -83,7 +83,7 @@ exports[`no enzyme tests`] = {
|
||||
"public/app/features/dimensions/editors/ThresholdsEditor/ThresholdsEditor.test.tsx:4164297658": [
|
||||
[0, 17, 13, "RegExp match", "2409514259"]
|
||||
],
|
||||
"public/app/features/folders/FolderSettingsPage.test.tsx:1109052730": [
|
||||
"public/app/features/folders/FolderSettingsPage.test.tsx:1208063654": [
|
||||
[0, 19, 13, "RegExp match", "2409514259"]
|
||||
],
|
||||
"public/app/plugins/datasource/cloudwatch/components/ConfigEditor.test.tsx:4057721851": [
|
||||
|
@ -443,23 +443,23 @@ func (hs *HTTPServer) buildDashboardNavLinks(c *models.ReqContext, hasEditPerm b
|
||||
|
||||
dashboardChildNavs := []*dtos.NavLink{}
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
|
||||
Text: "Browse", Id: "manage-dashboards", Url: hs.Cfg.AppSubURL + "/dashboards", Icon: "sitemap",
|
||||
Text: "Browse", Id: "dashboards/browse", Url: hs.Cfg.AppSubURL + "/dashboards", Icon: "sitemap",
|
||||
})
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
|
||||
Text: "Playlists", Id: "playlists", Url: hs.Cfg.AppSubURL + "/playlists", Icon: "presentation-play",
|
||||
Text: "Playlists", Id: "dashboards/playlists", Url: hs.Cfg.AppSubURL + "/playlists", Icon: "presentation-play",
|
||||
})
|
||||
|
||||
if c.IsSignedIn {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
|
||||
Text: "Snapshots",
|
||||
Id: "snapshots",
|
||||
Id: "dashboards/snapshots",
|
||||
Url: hs.Cfg.AppSubURL + "/dashboard/snapshots",
|
||||
Icon: "camera",
|
||||
})
|
||||
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
|
||||
Text: "Library panels",
|
||||
Id: "library-panels",
|
||||
Id: "dashboards/library-panels",
|
||||
Url: hs.Cfg.AppSubURL + "/library-panels",
|
||||
Icon: "library-panel",
|
||||
})
|
||||
@ -481,20 +481,20 @@ func (hs *HTTPServer) buildDashboardNavLinks(c *models.ReqContext, hasEditPerm b
|
||||
|
||||
if hasAccess(hasEditPermInAnyFolder, ac.EvalPermission(dashboards.ActionDashboardsCreate)) {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
|
||||
Text: "New dashboard", Icon: "plus", Url: hs.Cfg.AppSubURL + "/dashboard/new", HideFromTabs: true, Id: "new-dashboard", ShowIconInNavbar: true,
|
||||
Text: "New dashboard", Icon: "plus", Url: hs.Cfg.AppSubURL + "/dashboard/new", HideFromTabs: true, Id: "dashboards/new", ShowIconInNavbar: true,
|
||||
})
|
||||
}
|
||||
|
||||
if hasAccess(ac.ReqOrgAdminOrEditor, ac.EvalPermission(dashboards.ActionFoldersCreate)) {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
|
||||
Text: "New folder", SubTitle: "Create a new folder to organize your dashboards", Id: "new-folder",
|
||||
Text: "New folder", SubTitle: "Create a new folder to organize your dashboards", Id: "dashboards/folder/new",
|
||||
Icon: "plus", Url: hs.Cfg.AppSubURL + "/dashboards/folder/new", HideFromTabs: true, ShowIconInNavbar: true,
|
||||
})
|
||||
}
|
||||
|
||||
if hasAccess(hasEditPermInAnyFolder, ac.EvalPermission(dashboards.ActionDashboardsCreate)) {
|
||||
dashboardChildNavs = append(dashboardChildNavs, &dtos.NavLink{
|
||||
Text: "Import", SubTitle: "Import dashboard from file or Grafana.com", Id: "import", Icon: "plus",
|
||||
Text: "Import", SubTitle: "Import dashboard from file or Grafana.com", Id: "dashboards/import", Icon: "plus",
|
||||
Url: hs.Cfg.AppSubURL + "/dashboard/import", HideFromTabs: true, ShowIconInNavbar: true,
|
||||
})
|
||||
}
|
||||
|
@ -4,7 +4,10 @@ import { defineMessage } from '@lingui/macro';
|
||||
// Maps the ID of the nav item to a translated phrase to later pass to <Trans />
|
||||
// Because the navigation content is dynamic (defined in the backend), we can not use
|
||||
// the normal inline message definition method.
|
||||
// Keys MUST match the ID of the navigation item, defined in the backend.
|
||||
|
||||
// The keys of the TRANSLATED_MENU_ITEMS object (NOT the id inside the defineMessage function)
|
||||
// must match the ID of the navigation item, as defined in the backend nav model
|
||||
|
||||
// see pkg/api/index.go
|
||||
const TRANSLATED_MENU_ITEMS: Record<string, MessageDescriptor> = {
|
||||
home: defineMessage({ id: 'nav.home', message: 'Home' }),
|
||||
@ -18,12 +21,12 @@ const TRANSLATED_MENU_ITEMS: Record<string, MessageDescriptor> = {
|
||||
starred: defineMessage({ id: 'nav.starred', message: 'Starred' }),
|
||||
'starred-empty': defineMessage({ id: 'nav.starred-empty', message: 'Your starred dashboards will appear here' }),
|
||||
dashboards: defineMessage({ id: 'nav.dashboards', message: 'Dashboards' }),
|
||||
'manage-dashboards': defineMessage({ id: 'nav.manage-dashboards', message: 'Browse' }),
|
||||
playlists: defineMessage({ id: 'nav.playlists', message: 'Playlists' }),
|
||||
snapshots: defineMessage({ id: 'nav.snapshots', message: 'Snapshots' }),
|
||||
'library-panels': defineMessage({ id: 'nav.library-panels', message: 'Library panels' }),
|
||||
'new-dashboard': defineMessage({ id: 'nav.new-dashboard', message: 'New dashboard' }),
|
||||
'new-folder': defineMessage({ id: 'nav.new-folder', message: 'New folder' }),
|
||||
'dashboards/browse': defineMessage({ id: 'nav.manage-dashboards', message: 'Browse' }),
|
||||
'dashboards/playlists': defineMessage({ id: 'nav.playlists', message: 'Playlists' }),
|
||||
'dashboards/snapshots': defineMessage({ id: 'nav.snapshots', message: 'Snapshots' }),
|
||||
'dashboards/library-panels': defineMessage({ id: 'nav.library-panels', message: 'Library panels' }),
|
||||
'dashboards/new': defineMessage({ id: 'nav.new-dashboard', message: 'New dashboard' }),
|
||||
'dashboards/folder/new': defineMessage({ id: 'nav.new-folder', message: 'New folder' }),
|
||||
|
||||
explore: defineMessage({ id: 'nav.explore', message: 'Explore' }),
|
||||
|
||||
|
@ -17,7 +17,7 @@ function mapStateToProps(state: StoreState, props: RouteProps) {
|
||||
const uid = props.match.params.uid;
|
||||
return {
|
||||
uid: uid,
|
||||
navModel: getNavModel(state.navIndex, `folder-permissions-${uid}`, getLoadingNav(1)),
|
||||
pageNav: getNavModel(state.navIndex, `folder-permissions-${uid}`, getLoadingNav(1)),
|
||||
};
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@ const mapDispatchToProps = {
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
export type Props = ConnectedProps<typeof connector>;
|
||||
|
||||
export const AccessControlFolderPermissions = ({ uid, getFolderByUid, navModel }: Props) => {
|
||||
export const AccessControlFolderPermissions = ({ uid, getFolderByUid, pageNav }: Props) => {
|
||||
useEffect(() => {
|
||||
getFolderByUid(uid);
|
||||
}, [getFolderByUid, uid]);
|
||||
@ -36,7 +36,7 @@ export const AccessControlFolderPermissions = ({ uid, getFolderByUid, navModel }
|
||||
const canSetPermissions = contextSrv.hasPermission(AccessControlAction.FoldersPermissionsWrite);
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/browse" pageNav={pageNav.main}>
|
||||
<Page.Contents>
|
||||
<Permissions resource="folders" resourceId={uid} canSetPermissions={canSetPermissions} />
|
||||
</Page.Contents>
|
||||
|
@ -20,12 +20,12 @@ const FolderAlerting = ({ match }: OwnProps) => {
|
||||
const folder = useSelector((state: StoreState) => state.folder);
|
||||
|
||||
const uid = match.params.uid;
|
||||
const navModel = getNavModel(navIndex, `folder-alerting-${uid}`, getLoadingNav(1));
|
||||
const pageNav = getNavModel(navIndex, `folder-alerting-${uid}`, getLoadingNav(1));
|
||||
|
||||
const { loading } = useAsync(async () => dispatch(getFolderByUid(uid)), [getFolderByUid, uid]);
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/browse" pageNav={pageNav.main}>
|
||||
<Page.Contents isLoading={loading}>
|
||||
<AlertsFolderView folder={folder} />
|
||||
</Page.Contents>
|
||||
|
@ -19,7 +19,7 @@ export interface OwnProps extends GrafanaRouteComponentProps<{ uid: string }> {}
|
||||
const mapStateToProps = (state: StoreState, props: OwnProps) => {
|
||||
const uid = props.match.params.uid;
|
||||
return {
|
||||
navModel: getNavModel(state.navIndex, `folder-library-panels-${uid}`, getLoadingNav(1)),
|
||||
pageNav: getNavModel(state.navIndex, `folder-library-panels-${uid}`, getLoadingNav(1)),
|
||||
folderUid: uid,
|
||||
folder: state.folder,
|
||||
};
|
||||
@ -33,12 +33,12 @@ const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
export type Props = OwnProps & ConnectedProps<typeof connector>;
|
||||
|
||||
export function FolderLibraryPanelsPage({ navModel, getFolderByUid, folderUid, folder }: Props): JSX.Element {
|
||||
export function FolderLibraryPanelsPage({ pageNav, getFolderByUid, folderUid, folder }: Props): JSX.Element {
|
||||
const { loading } = useAsync(async () => await getFolderByUid(folderUid), [getFolderByUid, folderUid]);
|
||||
const [selected, setSelected] = useState<LibraryElementDTO | undefined>(undefined);
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/browse" pageNav={pageNav.main}>
|
||||
<Page.Contents isLoading={loading}>
|
||||
<LibraryPanelsSearch
|
||||
onClick={setSelected}
|
||||
|
@ -26,7 +26,7 @@ export interface OwnProps extends GrafanaRouteComponentProps<{ uid: string }> {}
|
||||
const mapStateToProps = (state: StoreState, props: OwnProps) => {
|
||||
const uid = props.match.params.uid;
|
||||
return {
|
||||
navModel: getNavModel(state.navIndex, `folder-permissions-${uid}`, getLoadingNav(1)),
|
||||
pageNav: getNavModel(state.navIndex, `folder-permissions-${uid}`, getLoadingNav(1)),
|
||||
folderUid: uid,
|
||||
folder: state.folder,
|
||||
};
|
||||
@ -83,12 +83,12 @@ export class FolderPermissions extends PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navModel, folder } = this.props;
|
||||
const { pageNav, folder } = this.props;
|
||||
const { isAdding } = this.state;
|
||||
|
||||
if (folder.id === 0) {
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/browse" pageNav={pageNav.main}>
|
||||
<Page.Contents isLoading={true}>
|
||||
<span />
|
||||
</Page.Contents>
|
||||
@ -99,7 +99,7 @@ export class FolderPermissions extends PureComponent<Props, State> {
|
||||
const folderInfo = { title: folder.title, url: folder.url, id: folder.id };
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="browse" pageNav={pageNav.main}>
|
||||
<Page.Contents>
|
||||
<div className="page-action-bar">
|
||||
<h3 className="page-sub-heading">Folder Permissions</h3>
|
||||
|
@ -11,7 +11,7 @@ import { setFolderTitle } from './state/reducers';
|
||||
const setup = (propOverrides?: object) => {
|
||||
const props: Props = {
|
||||
...getRouteComponentProps(),
|
||||
navModel: {} as NavModel,
|
||||
pageNav: {} as NavModel,
|
||||
folderUid: '1234',
|
||||
folder: {
|
||||
id: 0,
|
||||
|
@ -20,7 +20,7 @@ export interface OwnProps extends GrafanaRouteComponentProps<{ uid: string }> {}
|
||||
const mapStateToProps = (state: StoreState, props: OwnProps) => {
|
||||
const uid = props.match.params.uid;
|
||||
return {
|
||||
navModel: getNavModel(state.navIndex, `folder-settings-${uid}`, getLoadingNav(2)),
|
||||
pageNav: getNavModel(state.navIndex, `folder-settings-${uid}`, getLoadingNav(2)),
|
||||
folderUid: uid,
|
||||
folder: state.folder,
|
||||
};
|
||||
@ -84,10 +84,10 @@ export class FolderSettingsPage extends PureComponent<Props, State> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navModel, folder } = this.props;
|
||||
const { pageNav, folder } = this.props;
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/browse" pageNav={pageNav.main}>
|
||||
<Page.Contents isLoading={this.state.isLoading}>
|
||||
<h3 className="page-sub-heading">Folder settings</h3>
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
exports[`Render should enable save button 1`] = `
|
||||
<OldPage
|
||||
navModel={Object {}}
|
||||
navId="dashboards/browse"
|
||||
>
|
||||
<PageContents
|
||||
isLoading={false}
|
||||
@ -59,7 +59,7 @@ exports[`Render should enable save button 1`] = `
|
||||
|
||||
exports[`Render should render component 1`] = `
|
||||
<OldPage
|
||||
navModel={Object {}}
|
||||
navId="dashboards/browse"
|
||||
>
|
||||
<PageContents
|
||||
isLoading={false}
|
||||
|
@ -3,21 +3,15 @@ import { connect, ConnectedProps } from 'react-redux';
|
||||
|
||||
import { Button, Input, Form, Field } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
import { validationSrv } from '../../manage-dashboards/services/ValidationSrv';
|
||||
import { createNewFolder } from '../state/actions';
|
||||
|
||||
const mapStateToProps = (state: StoreState) => ({
|
||||
navModel: getNavModel(state.navIndex, 'manage-dashboards'),
|
||||
});
|
||||
|
||||
const mapDispatchToProps = {
|
||||
createNewFolder,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
const connector = connect(null, mapDispatchToProps);
|
||||
|
||||
interface OwnProps {}
|
||||
|
||||
@ -47,7 +41,7 @@ export class NewDashboardsFolder extends PureComponent<Props> {
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Page navModel={this.props.navModel}>
|
||||
<Page navId="dashboards/folder/new">
|
||||
<Page.Contents>
|
||||
<h3>New dashboard folder</h3>
|
||||
<Form defaultValues={initialFormModel} onSubmit={this.onSubmit}>
|
||||
|
@ -1,31 +1,16 @@
|
||||
import React, { FC, useState } from 'react';
|
||||
import { connect, ConnectedProps } from 'react-redux';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
|
||||
import { GrafanaRouteComponentProps } from '../../core/navigation/types';
|
||||
import { getNavModel } from '../../core/selectors/navModel';
|
||||
import { StoreState } from '../../types';
|
||||
|
||||
import { LibraryPanelsSearch } from './components/LibraryPanelsSearch/LibraryPanelsSearch';
|
||||
import { OpenLibraryPanelModal } from './components/OpenLibraryPanelModal/OpenLibraryPanelModal';
|
||||
import { LibraryElementDTO } from './types';
|
||||
|
||||
const mapStateToProps = (state: StoreState) => ({
|
||||
navModel: getNavModel(state.navIndex, 'library-panels'),
|
||||
});
|
||||
|
||||
const connector = connect(mapStateToProps, undefined);
|
||||
|
||||
interface OwnProps extends GrafanaRouteComponentProps {}
|
||||
|
||||
type Props = OwnProps & ConnectedProps<typeof connector>;
|
||||
|
||||
export const LibraryPanelsPage: FC<Props> = ({ navModel }) => {
|
||||
export const LibraryPanelsPage = () => {
|
||||
const [selected, setSelected] = useState<LibraryElementDTO | undefined>(undefined);
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/library-panels">
|
||||
<Page.Contents>
|
||||
<LibraryPanelsSearch onClick={setSelected} showSecondaryActions showSort showPanelFilter showFolderFilter />
|
||||
{selected ? <OpenLibraryPanelModal onDismiss={() => setSelected(undefined)} libraryPanel={selected} /> : null}
|
||||
@ -34,4 +19,4 @@ export const LibraryPanelsPage: FC<Props> = ({ navModel }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(LibraryPanelsPage);
|
||||
export default LibraryPanelsPage;
|
||||
|
@ -22,7 +22,6 @@ import {
|
||||
import appEvents from 'app/core/app_events';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
import { cleanUpAction } from '../../core/actions/cleanUp';
|
||||
@ -40,7 +39,6 @@ type OwnProps = Themeable2 & GrafanaRouteComponentProps<{}, DashboardImportPageR
|
||||
const IMPORT_STARTED_EVENT_NAME = 'dashboard_import_loaded';
|
||||
|
||||
const mapStateToProps = (state: StoreState) => ({
|
||||
navModel: getNavModel(state.navIndex, 'import', undefined, true),
|
||||
loadingState: state.importDashboard.state,
|
||||
});
|
||||
|
||||
@ -189,10 +187,10 @@ class UnthemedDashboardImport extends PureComponent<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { loadingState, navModel } = this.props;
|
||||
const { loadingState } = this.props;
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/import">
|
||||
<Page.Contents>
|
||||
{loadingState === LoadingState.Loading && (
|
||||
<VerticalGroup justify="center">
|
||||
|
@ -1,23 +1,12 @@
|
||||
import React, { FC } from 'react';
|
||||
import { MapStateToProps, connect } from 'react-redux';
|
||||
import React from 'react';
|
||||
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
import { GrafanaRouteComponentProps } from '../../core/navigation/types';
|
||||
|
||||
import { SnapshotListTable } from './components/SnapshotListTable';
|
||||
|
||||
interface ConnectedProps {
|
||||
navModel: NavModel;
|
||||
}
|
||||
interface Props extends ConnectedProps, GrafanaRouteComponentProps {}
|
||||
|
||||
export const SnapshotListPage: FC<Props> = ({ navModel, location }) => {
|
||||
export const SnapshotListPage = ({}) => {
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/snapshots">
|
||||
<Page.Contents>
|
||||
<SnapshotListTable />
|
||||
</Page.Contents>
|
||||
@ -25,8 +14,4 @@ export const SnapshotListPage: FC<Props> = ({ navModel, location }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps: MapStateToProps<ConnectedProps, {}, StoreState> = (state: StoreState) => ({
|
||||
navModel: getNavModel(state.navIndex, 'snapshots'),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(SnapshotListPage);
|
||||
export default SnapshotListPage;
|
||||
|
@ -27,10 +27,6 @@ async function getTestContext({ name, interval, items, uid }: Partial<Playlist>
|
||||
const match: any = { params: { uid: 'foo' } };
|
||||
const location: any = {};
|
||||
const history: any = {};
|
||||
const navModel: any = {
|
||||
node: {},
|
||||
main: {},
|
||||
};
|
||||
const getMock = jest.spyOn(backendSrv, 'get');
|
||||
const putMock = jest.spyOn(backendSrv, 'put');
|
||||
getMock.mockResolvedValue({
|
||||
@ -40,14 +36,7 @@ async function getTestContext({ name, interval, items, uid }: Partial<Playlist>
|
||||
uid: 'foo',
|
||||
});
|
||||
const { rerender } = render(
|
||||
<PlaylistEditPage
|
||||
queryParams={queryParams}
|
||||
route={route}
|
||||
match={match}
|
||||
location={location}
|
||||
history={history}
|
||||
navModel={navModel}
|
||||
/>
|
||||
<PlaylistEditPage queryParams={queryParams} route={route} match={match} location={location} history={history} />
|
||||
);
|
||||
await waitFor(() => expect(getMock).toHaveBeenCalledTimes(1));
|
||||
|
||||
|
@ -1,12 +1,8 @@
|
||||
import React, { FC } from 'react';
|
||||
import { connect, MapStateToProps } from 'react-redux';
|
||||
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
import { GrafanaRouteComponentProps } from '../../core/navigation/types';
|
||||
|
||||
@ -16,17 +12,13 @@ import { getPlaylistStyles } from './styles';
|
||||
import { Playlist } from './types';
|
||||
import { usePlaylist } from './usePlaylist';
|
||||
|
||||
interface ConnectedProps {
|
||||
navModel: NavModel;
|
||||
}
|
||||
|
||||
export interface RouteParams {
|
||||
uid: string;
|
||||
}
|
||||
|
||||
interface Props extends ConnectedProps, GrafanaRouteComponentProps<RouteParams> {}
|
||||
interface Props extends GrafanaRouteComponentProps<RouteParams> {}
|
||||
|
||||
export const PlaylistEditPage: FC<Props> = ({ navModel, match }) => {
|
||||
export const PlaylistEditPage: FC<Props> = ({ match }) => {
|
||||
const styles = useStyles2(getPlaylistStyles);
|
||||
const { playlist, loading } = usePlaylist(match.params.uid);
|
||||
const onSubmit = async (playlist: Playlist) => {
|
||||
@ -35,7 +27,7 @@ export const PlaylistEditPage: FC<Props> = ({ navModel, match }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/playlists">
|
||||
<Page.Contents isLoading={loading}>
|
||||
<h3 className={styles.subHeading}>Edit playlist</h3>
|
||||
|
||||
@ -50,8 +42,4 @@ export const PlaylistEditPage: FC<Props> = ({ navModel, match }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps: MapStateToProps<ConnectedProps, {}, StoreState> = (state: StoreState) => ({
|
||||
navModel: getNavModel(state.navIndex, 'playlists'),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(PlaylistEditPage);
|
||||
export default PlaylistEditPage;
|
||||
|
@ -23,7 +23,7 @@ export const PlaylistForm: FC<PlaylistFormProps> = ({ onSubmit, playlist }) => {
|
||||
const { name, interval, items: propItems } = playlist;
|
||||
const { items, addById, addByTag, deleteItem, moveDown, moveUp } = usePlaylistItems(propItems);
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Form onSubmit={(list: Playlist) => onSubmit({ ...list, items })} validateOn={'onBlur'}>
|
||||
{({ register, errors }) => {
|
||||
const isDisabled = items.length === 0 || Object.keys(errors).length > 0;
|
||||
@ -81,6 +81,6 @@ export const PlaylistForm: FC<PlaylistFormProps> = ({ onSubmit, playlist }) => {
|
||||
);
|
||||
}}
|
||||
</Form>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -30,26 +30,9 @@ jest.mock('../../core/components/TagFilter/TagFilter', () => ({
|
||||
function getTestContext({ name, interval, items }: Partial<Playlist> = {}) {
|
||||
jest.clearAllMocks();
|
||||
const playlist = { name, items, interval } as unknown as Playlist;
|
||||
const queryParams = {};
|
||||
const route: any = {};
|
||||
const match: any = {};
|
||||
const location: any = {};
|
||||
const history: any = {};
|
||||
const navModel: any = {
|
||||
node: {},
|
||||
main: {},
|
||||
};
|
||||
const backendSrvMock = jest.spyOn(backendSrv, 'post');
|
||||
const { rerender } = render(
|
||||
<PlaylistNewPage
|
||||
queryParams={queryParams}
|
||||
route={route}
|
||||
match={match}
|
||||
location={location}
|
||||
history={history}
|
||||
navModel={navModel}
|
||||
/>
|
||||
);
|
||||
|
||||
const { rerender } = render(<PlaylistNewPage />);
|
||||
|
||||
return { playlist, rerender, backendSrvMock };
|
||||
}
|
||||
|
@ -1,14 +1,8 @@
|
||||
import React, { FC } from 'react';
|
||||
import { connect, MapStateToProps } from 'react-redux';
|
||||
import React from 'react';
|
||||
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { useStyles2 } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
import { GrafanaRouteComponentProps } from '../../core/navigation/types';
|
||||
|
||||
import { PlaylistForm } from './PlaylistForm';
|
||||
import { createPlaylist } from './api';
|
||||
@ -16,13 +10,7 @@ import { getPlaylistStyles } from './styles';
|
||||
import { Playlist } from './types';
|
||||
import { usePlaylist } from './usePlaylist';
|
||||
|
||||
interface ConnectedProps {
|
||||
navModel: NavModel;
|
||||
}
|
||||
|
||||
interface Props extends ConnectedProps, GrafanaRouteComponentProps {}
|
||||
|
||||
export const PlaylistNewPage: FC<Props> = ({ navModel }) => {
|
||||
export const PlaylistNewPage = () => {
|
||||
const styles = useStyles2(getPlaylistStyles);
|
||||
const { playlist, loading } = usePlaylist();
|
||||
const onSubmit = async (playlist: Playlist) => {
|
||||
@ -31,7 +19,7 @@ export const PlaylistNewPage: FC<Props> = ({ navModel }) => {
|
||||
};
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/playlists">
|
||||
<Page.Contents isLoading={loading}>
|
||||
<h3 className={styles.subHeading}>New Playlist</h3>
|
||||
|
||||
@ -46,8 +34,4 @@ export const PlaylistNewPage: FC<Props> = ({ navModel }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps: MapStateToProps<ConnectedProps, {}, StoreState> = (state: StoreState) => ({
|
||||
navModel: getNavModel(state.navIndex, 'playlists'),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(PlaylistNewPage);
|
||||
export default PlaylistNewPage;
|
||||
|
@ -3,9 +3,7 @@ import React from 'react';
|
||||
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
|
||||
import { locationService } from '../../../../packages/grafana-runtime/src';
|
||||
|
||||
import { PlaylistPage, PlaylistPageProps } from './PlaylistPage';
|
||||
import { PlaylistPage } from './PlaylistPage';
|
||||
|
||||
const fnMock = jest.fn();
|
||||
|
||||
@ -22,29 +20,8 @@ jest.mock('app/core/services/context_srv', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
function getTestContext(propOverrides?: object) {
|
||||
const props: PlaylistPageProps = {
|
||||
navModel: {
|
||||
main: {
|
||||
text: 'Playlist',
|
||||
},
|
||||
node: {
|
||||
text: 'playlist',
|
||||
},
|
||||
},
|
||||
route: {
|
||||
path: '/playlists',
|
||||
component: jest.fn(),
|
||||
},
|
||||
queryParams: { state: 'ok' },
|
||||
match: { params: { name: 'playlist', sourceName: 'test playlist' }, isExact: false, url: 'asdf', path: '' },
|
||||
history: locationService.getHistory(),
|
||||
location: { pathname: '', hash: '', search: '', state: '' },
|
||||
};
|
||||
|
||||
Object.assign(props, propOverrides);
|
||||
|
||||
return render(<PlaylistPage {...props} />);
|
||||
function getTestContext() {
|
||||
return render(<PlaylistPage />);
|
||||
}
|
||||
|
||||
describe('PlaylistPage', () => {
|
||||
|
@ -1,17 +1,12 @@
|
||||
import React, { FC, useState } from 'react';
|
||||
import { connect, MapStateToProps } from 'react-redux';
|
||||
import React, { useState } from 'react';
|
||||
import { useDebounce } from 'react-use';
|
||||
|
||||
import { NavModel } from '@grafana/data';
|
||||
import { ConfirmModal } from '@grafana/ui';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import PageActionBar from 'app/core/components/PageActionBar/PageActionBar';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { contextSrv } from 'app/core/services/context_srv';
|
||||
import { StoreState } from 'app/types';
|
||||
|
||||
import EmptyListCTA from '../../core/components/EmptyListCTA/EmptyListCTA';
|
||||
import { GrafanaRouteComponentProps } from '../../core/navigation/types';
|
||||
|
||||
import { EmptyQueryListBanner } from './EmptyQueryListBanner';
|
||||
import { PlaylistPageList } from './PlaylistPageList';
|
||||
@ -19,12 +14,7 @@ import { StartModal } from './StartModal';
|
||||
import { deletePlaylist, getAllPlaylist } from './api';
|
||||
import { PlaylistDTO } from './types';
|
||||
|
||||
interface ConnectedProps {
|
||||
navModel: NavModel;
|
||||
}
|
||||
export interface PlaylistPageProps extends ConnectedProps, GrafanaRouteComponentProps {}
|
||||
|
||||
export const PlaylistPage: FC<PlaylistPageProps> = ({ navModel }) => {
|
||||
export const PlaylistPage = () => {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [debouncedSearchQuery, setDebouncedSearchQuery] = useState(searchQuery);
|
||||
const [hasFetched, setHasFetched] = useState(false);
|
||||
@ -76,7 +66,7 @@ export const PlaylistPage: FC<PlaylistPageProps> = ({ navModel }) => {
|
||||
const showSearch = playlists.length > 0 || searchQuery.length > 0 || debouncedSearchQuery.length > 0;
|
||||
|
||||
return (
|
||||
<Page navModel={navModel}>
|
||||
<Page navId="dashboards/playlists">
|
||||
<Page.Contents isLoading={!hasFetched}>
|
||||
{showSearch && (
|
||||
<PageActionBar
|
||||
@ -112,8 +102,4 @@ export const PlaylistPage: FC<PlaylistPageProps> = ({ navModel }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const mapStateToProps: MapStateToProps<ConnectedProps, {}, StoreState> = (state: StoreState) => ({
|
||||
navModel: getNavModel(state.navIndex, 'playlists'),
|
||||
});
|
||||
|
||||
export default connect(mapStateToProps)(PlaylistPage);
|
||||
export default PlaylistPage;
|
||||
|
@ -1,13 +1,11 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React, { FC, memo } from 'react';
|
||||
import { connect, MapStateToProps } from 'react-redux';
|
||||
import { useAsync } from 'react-use';
|
||||
|
||||
import { NavModel, locationUtil } from '@grafana/data';
|
||||
import { locationUtil, NavModelItem } from '@grafana/data';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import { Page } from 'app/core/components/Page/Page';
|
||||
import { getNavModel } from 'app/core/selectors/navModel';
|
||||
import { FolderDTO, StoreState } from 'app/types';
|
||||
import { FolderDTO } from 'app/types';
|
||||
|
||||
import { GrafanaRouteComponentProps } from '../../../core/navigation/types';
|
||||
import { loadFolderPage } from '../loaders';
|
||||
@ -19,17 +17,14 @@ export interface DashboardListPageRouteParams {
|
||||
slug?: string;
|
||||
}
|
||||
|
||||
interface DashboardListPageConnectedProps {
|
||||
navModel: NavModel;
|
||||
}
|
||||
interface Props extends GrafanaRouteComponentProps<DashboardListPageRouteParams>, DashboardListPageConnectedProps {}
|
||||
interface Props extends GrafanaRouteComponentProps<DashboardListPageRouteParams> {}
|
||||
|
||||
export const DashboardListPage: FC<Props> = memo(({ navModel, match, location }) => {
|
||||
const { loading, value } = useAsync<() => Promise<{ folder?: FolderDTO; pageNavModel: NavModel }>>(() => {
|
||||
export const DashboardListPage: FC<Props> = memo(({ match, location }) => {
|
||||
const { loading, value } = useAsync<() => Promise<{ folder?: FolderDTO; pageNav?: NavModelItem }>>(() => {
|
||||
const uid = match.params.uid;
|
||||
const url = location.pathname;
|
||||
if (!uid || !url.startsWith('/dashboards')) {
|
||||
return Promise.resolve({ pageNavModel: navModel });
|
||||
return Promise.resolve({});
|
||||
}
|
||||
|
||||
return loadFolderPage(uid!).then(({ folder, folderNav }) => {
|
||||
@ -39,12 +34,12 @@ export const DashboardListPage: FC<Props> = memo(({ navModel, match, location })
|
||||
locationService.push(path);
|
||||
}
|
||||
|
||||
return { folder, pageNavModel: { ...navModel, main: folderNav } };
|
||||
return { folder, pageNav: folderNav };
|
||||
});
|
||||
}, [match.params.uid]);
|
||||
|
||||
return (
|
||||
<Page navModel={value?.pageNavModel ?? navModel}>
|
||||
<Page navId="dashboards/browse" pageNav={value?.pageNav}>
|
||||
<Page.Contents
|
||||
isLoading={loading}
|
||||
className={css`
|
||||
@ -61,10 +56,4 @@ export const DashboardListPage: FC<Props> = memo(({ navModel, match, location })
|
||||
|
||||
DashboardListPage.displayName = 'DashboardListPage';
|
||||
|
||||
const mapStateToProps: MapStateToProps<DashboardListPageConnectedProps, {}, StoreState> = (state) => {
|
||||
return {
|
||||
navModel: getNavModel(state.navIndex, 'manage-dashboards'),
|
||||
};
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(DashboardListPage);
|
||||
export default DashboardListPage;
|
||||
|
@ -51,8 +51,6 @@ export function getAppRoutes(): RouteDescriptor[] {
|
||||
path: '/dashboard/new',
|
||||
pageClass: 'page-dashboard',
|
||||
routeName: DashboardRoutes.New,
|
||||
// TODO[Router]
|
||||
//roles: () => (contextSrv.hasEditPermissionInFolders ? [contextSrv.user.orgRole] : ['Admin']),
|
||||
component: SafeDynamicImport(
|
||||
() => import(/* webpackChunkName: "DashboardPage" */ '../features/dashboard/containers/DashboardPage')
|
||||
),
|
||||
@ -61,6 +59,7 @@ export function getAppRoutes(): RouteDescriptor[] {
|
||||
path: '/d-solo/:uid/:slug',
|
||||
pageClass: 'dashboard-solo',
|
||||
routeName: DashboardRoutes.Normal,
|
||||
chromeless: true,
|
||||
component: SafeDynamicImport(
|
||||
() => import(/* webpackChunkName: "SoloPanelPage" */ '../features/dashboard/containers/SoloPanelPage')
|
||||
),
|
||||
@ -70,6 +69,7 @@ export function getAppRoutes(): RouteDescriptor[] {
|
||||
path: '/dashboard-solo/:type/:slug',
|
||||
pageClass: 'dashboard-solo',
|
||||
routeName: DashboardRoutes.Normal,
|
||||
chromeless: true,
|
||||
component: SafeDynamicImport(
|
||||
() => import(/* webpackChunkName: "SoloPanelPage" */ '../features/dashboard/containers/SoloPanelPage')
|
||||
),
|
||||
@ -78,6 +78,7 @@ export function getAppRoutes(): RouteDescriptor[] {
|
||||
path: '/d-solo/:uid',
|
||||
pageClass: 'dashboard-solo',
|
||||
routeName: DashboardRoutes.Normal,
|
||||
chromeless: true,
|
||||
component: SafeDynamicImport(
|
||||
() => import(/* webpackChunkName: "SoloPanelPage" */ '../features/dashboard/containers/SoloPanelPage')
|
||||
),
|
||||
|
Loading…
Reference in New Issue
Block a user