mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
FolderPage: Improve folder page to work with new nav breadcrumbs and modify Dashboard page show path based breadcrumbs" (#52428)
* FolderPage: Progress on folder page navigation * Dashboard to adapt breadcrumbs when opening dashboard from file storage * add warning when topnav is not enabled Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
parent
79d92aa03e
commit
b29045b57b
@ -12,9 +12,10 @@ import { PageLayoutType } from 'app/core/components/Page/types';
|
|||||||
import { createErrorNotification } from 'app/core/copy/appNotification';
|
import { createErrorNotification } from 'app/core/copy/appNotification';
|
||||||
import { getKioskMode } from 'app/core/navigation/kiosk';
|
import { getKioskMode } from 'app/core/navigation/kiosk';
|
||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||||
import { PanelModel } from 'app/features/dashboard/state';
|
import { DashboardModel, PanelModel } from 'app/features/dashboard/state';
|
||||||
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
|
||||||
import { KioskMode, StoreState } from 'app/types';
|
import { getPageNavFromSlug, getRootContentNavModel } from 'app/features/storage/StorageFolderPage';
|
||||||
|
import { DashboardRoutes, KioskMode, StoreState } from 'app/types';
|
||||||
import { PanelEditEnteredEvent, PanelEditExitedEvent } from 'app/types/events';
|
import { PanelEditEnteredEvent, PanelEditExitedEvent } from 'app/types/events';
|
||||||
|
|
||||||
import { cancelVariables, templateVarsChangedInUrl } from '../../variables/state/actions';
|
import { cancelVariables, templateVarsChangedInUrl } from '../../variables/state/actions';
|
||||||
@ -149,28 +150,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update page nav
|
this.updatePageNav(dashboard);
|
||||||
if (!this.pageNav || dashboard.title !== this.pageNav.text) {
|
|
||||||
this.pageNav = {
|
|
||||||
text: dashboard.title,
|
|
||||||
url: locationUtil.getUrlForPartial(this.props.history.location, {
|
|
||||||
editview: null,
|
|
||||||
editPanel: null,
|
|
||||||
viewPanel: null,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if folder changed
|
|
||||||
if (
|
|
||||||
dashboard.meta.folderTitle &&
|
|
||||||
(!this.pageNav.parentItem || this.pageNav.parentItem.text !== dashboard.meta.folderTitle)
|
|
||||||
) {
|
|
||||||
this.pageNav.parentItem = {
|
|
||||||
text: dashboard.meta.folderTitle,
|
|
||||||
url: `/dashboards/f/${dashboard.meta.folderUid}`,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
prevProps.match.params.uid !== match.params.uid ||
|
prevProps.match.params.uid !== match.params.uid ||
|
||||||
@ -339,6 +319,45 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
|
|||||||
return inspectPanel;
|
return inspectPanel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updatePageNav(dashboard: DashboardModel) {
|
||||||
|
if (!this.pageNav || dashboard.title !== this.pageNav.text) {
|
||||||
|
this.pageNav = {
|
||||||
|
text: dashboard.title,
|
||||||
|
url: locationUtil.getUrlForPartial(this.props.history.location, {
|
||||||
|
editview: null,
|
||||||
|
editPanel: null,
|
||||||
|
viewPanel: null,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if folder changed
|
||||||
|
if (
|
||||||
|
dashboard.meta.folderTitle &&
|
||||||
|
(!this.pageNav.parentItem || this.pageNav.parentItem.text !== dashboard.meta.folderTitle)
|
||||||
|
) {
|
||||||
|
this.pageNav.parentItem = {
|
||||||
|
text: dashboard.meta.folderTitle,
|
||||||
|
url: `/dashboards/f/${dashboard.meta.folderUid}`,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.props.route.routeName === DashboardRoutes.Path) {
|
||||||
|
const pageNav = getPageNavFromSlug(this.props.match.params.slug!);
|
||||||
|
if (pageNav?.parentItem) {
|
||||||
|
this.pageNav.parentItem = pageNav.parentItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPageProps() {
|
||||||
|
if (this.props.route.routeName === DashboardRoutes.Path) {
|
||||||
|
return { navModel: getRootContentNavModel(), pageNav: this.pageNav };
|
||||||
|
} else {
|
||||||
|
return { navId: 'dashboards', pageNav: this.pageNav };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { dashboard, initError, queryParams, isPublic } = this.props;
|
const { dashboard, initError, queryParams, isPublic } = this.props;
|
||||||
const { editPanel, viewPanel, updateScrollTop } = this.state;
|
const { editPanel, viewPanel, updateScrollTop } = this.state;
|
||||||
@ -368,8 +387,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Page
|
<Page
|
||||||
navId="dashboards"
|
{...this.getPageProps()}
|
||||||
pageNav={this.pageNav}
|
|
||||||
layout={PageLayoutType.Dashboard}
|
layout={PageLayoutType.Dashboard}
|
||||||
toolbar={toolbar}
|
toolbar={toolbar}
|
||||||
className={containerClassNames}
|
className={containerClassNames}
|
||||||
|
@ -1,32 +1,24 @@
|
|||||||
import { css } from '@emotion/css';
|
import React from 'react';
|
||||||
import React, { FC } from 'react';
|
|
||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
|
|
||||||
import { DataFrame, GrafanaTheme2 } from '@grafana/data';
|
import { DataFrame, NavModel, NavModelItem } from '@grafana/data';
|
||||||
import { Card, Icon, Spinner, useStyles2 } from '@grafana/ui';
|
import { config } from '@grafana/runtime';
|
||||||
|
import { Alert, Card, Icon, Spinner } from '@grafana/ui';
|
||||||
|
import { Page } from 'app/core/components/Page/Page';
|
||||||
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
import { GrafanaRouteComponentProps } from 'app/core/navigation/types';
|
||||||
|
|
||||||
import { getGrafanaStorage } from './storage';
|
import { getGrafanaStorage } from './storage';
|
||||||
|
|
||||||
export interface Props extends GrafanaRouteComponentProps<{ slug: string }> {}
|
export interface Props extends GrafanaRouteComponentProps<{ slug: string }> {}
|
||||||
|
|
||||||
export const StorageFolderPage: FC<Props> = (props) => {
|
export function StorageFolderPage(props: Props) {
|
||||||
const slug = props.match.params.slug;
|
const slug = props.match.params.slug ?? '';
|
||||||
|
|
||||||
const styles = useStyles2(getStyles);
|
|
||||||
const listing = useAsync((): Promise<DataFrame | undefined> => {
|
const listing = useAsync((): Promise<DataFrame | undefined> => {
|
||||||
return getGrafanaStorage().list(slug);
|
return getGrafanaStorage().list(slug);
|
||||||
}, [slug]);
|
}, [slug]);
|
||||||
|
|
||||||
let base = document.location.pathname;
|
const childRoot = slug.length > 0 ? `g/${slug}/` : 'g/';
|
||||||
if (!base.endsWith('/')) {
|
const pageNav = getPageNavFromSlug(slug);
|
||||||
base += '/';
|
|
||||||
}
|
|
||||||
let parent = '';
|
|
||||||
const idx = base.lastIndexOf('/', base.length - 2);
|
|
||||||
if (idx > 0) {
|
|
||||||
parent = base.substring(0, idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
const renderListing = () => {
|
const renderListing = () => {
|
||||||
if (listing.value) {
|
if (listing.value) {
|
||||||
@ -35,8 +27,10 @@ export const StorageFolderPage: FC<Props> = (props) => {
|
|||||||
let name = item;
|
let name = item;
|
||||||
const isFolder = name.indexOf('.') < 0;
|
const isFolder = name.indexOf('.') < 0;
|
||||||
const isDash = !isFolder && name.endsWith('.json');
|
const isDash = !isFolder && name.endsWith('.json');
|
||||||
|
const url = `${childRoot}${name}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card key={name} href={isFolder || isDash ? base + name : undefined}>
|
<Card key={name} href={isFolder || isDash ? url : undefined}>
|
||||||
<Card.Heading>{name}</Card.Heading>
|
<Card.Heading>{name}</Card.Heading>
|
||||||
<Card.Figure>
|
<Card.Figure>
|
||||||
<Icon name={isFolder ? 'folder' : isDash ? 'gf-grid' : 'file-alt'} size="sm" />
|
<Icon name={isFolder ? 'folder' : isDash ? 'gf-grid' : 'file-alt'} size="sm" />
|
||||||
@ -51,29 +45,37 @@ export const StorageFolderPage: FC<Props> = (props) => {
|
|||||||
return <div>?</div>;
|
return <div>?</div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const navModel = getRootContentNavModel();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<Page navModel={navModel} pageNav={pageNav}>
|
||||||
{slug?.length > 0 && (
|
{!config.featureToggles.topnav && (
|
||||||
<>
|
<div>
|
||||||
<h1>{slug}</h1>
|
<Alert title="Enable the topnav feature toggle">This page is designed assuming topnav is enabled</Alert>
|
||||||
<Card href={parent}>
|
</div>
|
||||||
<Card.Heading>{parent}</Card.Heading>
|
|
||||||
<Card.Figure>
|
|
||||||
<Icon name="arrow-left" size="sm" />
|
|
||||||
</Card.Figure>
|
|
||||||
</Card>
|
|
||||||
<br />
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
{renderListing()}
|
{renderListing()}
|
||||||
</div>
|
</Page>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
const getStyles = (theme: GrafanaTheme2) => ({
|
export function getPageNavFromSlug(slug: string) {
|
||||||
wrapper: css`
|
const parts = slug.split('/');
|
||||||
margin: 50px;
|
let pageNavs: NavModelItem[] = [];
|
||||||
`,
|
let url = 'g';
|
||||||
});
|
let lastPageNav: NavModelItem | undefined;
|
||||||
|
|
||||||
|
for (let i = 0; i < parts.length; i++) {
|
||||||
|
url += `/${parts[i]}`;
|
||||||
|
pageNavs.push({ text: parts[i], url, parentItem: lastPageNav });
|
||||||
|
lastPageNav = pageNavs[pageNavs.length - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return lastPageNav;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRootContentNavModel(): NavModel {
|
||||||
|
return { main: { text: 'C:' }, node: { text: 'Content', url: '/g' } };
|
||||||
|
}
|
||||||
|
|
||||||
export default StorageFolderPage;
|
export default StorageFolderPage;
|
||||||
|
@ -123,6 +123,7 @@ class SimpleStorage implements GrafanaStorage {
|
|||||||
if (!path.endsWith('.json')) {
|
if (!path.endsWith('.json')) {
|
||||||
path += '.json';
|
path += '.json';
|
||||||
}
|
}
|
||||||
|
|
||||||
const result = await backendSrv.get(`/api/storage/read/${path}`);
|
const result = await backendSrv.get(`/api/storage/read/${path}`);
|
||||||
result.uid = path;
|
result.uid = path;
|
||||||
delete result.id; // Saved with the dev dashboards!
|
delete result.id; // Saved with the dev dashboards!
|
||||||
|
Loading…
Reference in New Issue
Block a user