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:
Torkel Ödegaard 2022-07-19 20:41:27 +02:00 committed by GitHub
parent 79d92aa03e
commit b29045b57b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 84 additions and 63 deletions

View File

@ -12,9 +12,10 @@ import { PageLayoutType } from 'app/core/components/Page/types';
import { createErrorNotification } from 'app/core/copy/appNotification';
import { getKioskMode } from 'app/core/navigation/kiosk';
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 { 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 { cancelVariables, templateVarsChangedInUrl } from '../../variables/state/actions';
@ -149,28 +150,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
return;
}
// Update page nav
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}`,
};
}
this.updatePageNav(dashboard);
if (
prevProps.match.params.uid !== match.params.uid ||
@ -339,6 +319,45 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
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() {
const { dashboard, initError, queryParams, isPublic } = this.props;
const { editPanel, viewPanel, updateScrollTop } = this.state;
@ -368,8 +387,7 @@ export class UnthemedDashboardPage extends PureComponent<Props, State> {
return (
<Page
navId="dashboards"
pageNav={this.pageNav}
{...this.getPageProps()}
layout={PageLayoutType.Dashboard}
toolbar={toolbar}
className={containerClassNames}

View File

@ -1,32 +1,24 @@
import { css } from '@emotion/css';
import React, { FC } from 'react';
import React from 'react';
import { useAsync } from 'react-use';
import { DataFrame, GrafanaTheme2 } from '@grafana/data';
import { Card, Icon, Spinner, useStyles2 } from '@grafana/ui';
import { DataFrame, NavModel, NavModelItem } from '@grafana/data';
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 { getGrafanaStorage } from './storage';
export interface Props extends GrafanaRouteComponentProps<{ slug: string }> {}
export const StorageFolderPage: FC<Props> = (props) => {
const slug = props.match.params.slug;
const styles = useStyles2(getStyles);
export function StorageFolderPage(props: Props) {
const slug = props.match.params.slug ?? '';
const listing = useAsync((): Promise<DataFrame | undefined> => {
return getGrafanaStorage().list(slug);
}, [slug]);
let base = document.location.pathname;
if (!base.endsWith('/')) {
base += '/';
}
let parent = '';
const idx = base.lastIndexOf('/', base.length - 2);
if (idx > 0) {
parent = base.substring(0, idx);
}
const childRoot = slug.length > 0 ? `g/${slug}/` : 'g/';
const pageNav = getPageNavFromSlug(slug);
const renderListing = () => {
if (listing.value) {
@ -35,8 +27,10 @@ export const StorageFolderPage: FC<Props> = (props) => {
let name = item;
const isFolder = name.indexOf('.') < 0;
const isDash = !isFolder && name.endsWith('.json');
const url = `${childRoot}${name}`;
return (
<Card key={name} href={isFolder || isDash ? base + name : undefined}>
<Card key={name} href={isFolder || isDash ? url : undefined}>
<Card.Heading>{name}</Card.Heading>
<Card.Figure>
<Icon name={isFolder ? 'folder' : isDash ? 'gf-grid' : 'file-alt'} size="sm" />
@ -51,29 +45,37 @@ export const StorageFolderPage: FC<Props> = (props) => {
return <div>?</div>;
};
const navModel = getRootContentNavModel();
return (
<div className={styles.wrapper}>
{slug?.length > 0 && (
<>
<h1>{slug}</h1>
<Card href={parent}>
<Card.Heading>{parent}</Card.Heading>
<Card.Figure>
<Icon name="arrow-left" size="sm" />
</Card.Figure>
</Card>
<br />
</>
<Page navModel={navModel} pageNav={pageNav}>
{!config.featureToggles.topnav && (
<div>
<Alert title="Enable the topnav feature toggle">This page is designed assuming topnav is enabled</Alert>
</div>
)}
{renderListing()}
</div>
</Page>
);
};
}
const getStyles = (theme: GrafanaTheme2) => ({
wrapper: css`
margin: 50px;
`,
});
export function getPageNavFromSlug(slug: string) {
const parts = slug.split('/');
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;

View File

@ -123,6 +123,7 @@ class SimpleStorage implements GrafanaStorage {
if (!path.endsWith('.json')) {
path += '.json';
}
const result = await backendSrv.get(`/api/storage/read/${path}`);
result.uid = path;
delete result.id; // Saved with the dev dashboards!