grafana/public/app/features/dashboard/components/DashboardSettings/DashboardSettings.tsx
Jack Westbrook ad3b0b2272
PageToolbar: add bottom padding (#34602)
* fix(pagetoolbar): pad elements top and bottom to keep vertical align and wrapper padding

* fix(sidemenu): prevent mobile sidemenu squashing dashboard, vertically align mobile sidemenu icon

* fix(dashboardsettings): prevent subheader overlap

* fix(pagetoolbar): center align all toolbar elements vertically

* refactor(pagetoolbar): combine vertical paddings to bring back original padding values

* refactor: update padding values for components that use PageToolbar

* fix(playlists): update Empty cta link route to prevent 404

* test(dashboard): update snapshot
2021-05-26 10:49:39 +02:00

201 lines
6.1 KiB
TypeScript

import React, { PureComponent } from 'react';
import { css, cx } from '@emotion/css';
import { selectors } from '@grafana/e2e-selectors';
import { Button, CustomScrollbar, Icon, IconName, PageToolbar, stylesFactory } from '@grafana/ui';
import config from 'app/core/config';
import { contextSrv } from 'app/core/services/context_srv';
import { dashboardWatcher } from 'app/features/live/dashboard/dashboardWatcher';
import { DashboardModel } from '../../state/DashboardModel';
import { SaveDashboardButton, SaveDashboardAsButton } from '../SaveDashboard/SaveDashboardButton';
import { VariableEditorContainer } from '../../../variables/editor/VariableEditorContainer';
import { DashboardPermissions } from '../DashboardPermissions/DashboardPermissions';
import { GeneralSettings } from './GeneralSettings';
import { AnnotationsSettings } from './AnnotationsSettings';
import { LinksSettings } from './LinksSettings';
import { VersionsSettings } from './VersionsSettings';
import { JsonEditorSettings } from './JsonEditorSettings';
import { GrafanaTheme2 } from '@grafana/data';
import { locationService } from '@grafana/runtime';
export interface Props {
dashboard: DashboardModel;
editview: string;
}
export interface SettingsPage {
id: string;
title: string;
icon: IconName;
render: () => React.ReactNode;
}
export class DashboardSettings extends PureComponent<Props> {
onClose = () => {
locationService.partial({ editview: null });
};
onChangePage = (editview: string) => {
locationService.partial({ editview });
};
getPages(): SettingsPage[] {
const { dashboard } = this.props;
const pages: SettingsPage[] = [];
if (dashboard.meta.canEdit) {
pages.push(this.getGeneralPage());
pages.push({
title: 'Annotations',
id: 'annotations',
icon: 'comment-alt',
render: () => <AnnotationsSettings dashboard={dashboard} />,
});
pages.push({
title: 'Variables',
id: 'templating',
icon: 'calculator-alt',
render: () => <VariableEditorContainer />,
});
pages.push({
title: 'Links',
id: 'links',
icon: 'link',
render: () => <LinksSettings dashboard={dashboard} />,
});
}
if (dashboard.meta.canMakeEditable) {
pages.push({
title: 'General',
icon: 'sliders-v-alt',
id: 'settings',
render: () => this.renderMakeEditable(),
});
}
if (dashboard.id && dashboard.meta.canSave) {
pages.push({
title: 'Versions',
id: 'versions',
icon: 'history',
render: () => <VersionsSettings dashboard={dashboard} />,
});
}
if (dashboard.id && dashboard.meta.canAdmin) {
pages.push({
title: 'Permissions',
id: 'permissions',
icon: 'lock',
render: () => <DashboardPermissions dashboard={dashboard} />,
});
}
pages.push({
title: 'JSON Model',
id: 'dashboard_json',
icon: 'arrow',
render: () => <JsonEditorSettings dashboard={dashboard} />,
});
return pages;
}
onMakeEditable = () => {
const { dashboard } = this.props;
dashboard.editable = true;
dashboard.meta.canMakeEditable = false;
dashboard.meta.canEdit = true;
dashboard.meta.canSave = true;
this.forceUpdate();
};
onPostSave = () => {
this.props.dashboard.meta.hasUnsavedFolderChange = false;
dashboardWatcher.reloadPage();
};
renderMakeEditable(): React.ReactNode {
return (
<div>
<div className="dashboard-settings__header">Dashboard not editable</div>
<Button onClick={this.onMakeEditable}>Make editable</Button>
</div>
);
}
getGeneralPage(): SettingsPage {
return {
title: 'General',
id: 'settings',
icon: 'sliders-v-alt',
render: () => <GeneralSettings dashboard={this.props.dashboard} />,
};
}
render() {
const { dashboard, editview } = this.props;
const folderTitle = dashboard.meta.folderTitle;
const pages = this.getPages();
const currentPage = pages.find((page) => page.id === editview) ?? pages[0];
const canSaveAs = contextSrv.hasEditPermissionInFolders;
const canSave = dashboard.meta.canSave;
const styles = getStyles(config.theme2);
return (
<div className="dashboard-settings">
<PageToolbar title={`${dashboard.title} / Settings`} parent={folderTitle} onGoBack={this.onClose} />
<CustomScrollbar>
<div className={styles.scrollInner}>
<div className={styles.settingsWrapper}>
<aside className="dashboard-settings__aside">
{pages.map((page) => (
<a
className={cx('dashboard-settings__nav-item', { active: page.id === editview })}
aria-label={selectors.pages.Dashboard.Settings.General.sectionItems(page.title)}
onClick={() => this.onChangePage(page.id)}
key={page.id}
>
<Icon name={page.icon} style={{ marginRight: '4px' }} />
{page.title}
</a>
))}
<div className="dashboard-settings__aside-actions">
{canSave && <SaveDashboardButton dashboard={dashboard} onSaveSuccess={this.onPostSave} />}
{canSaveAs && (
<SaveDashboardAsButton dashboard={dashboard} onSaveSuccess={this.onPostSave} variant="secondary" />
)}
</div>
</aside>
<div className={styles.settingsContent}>{currentPage.render()}</div>
</div>
</div>
</CustomScrollbar>
</div>
);
}
}
const getStyles = stylesFactory((theme: GrafanaTheme2) => ({
scrollInner: css`
min-width: 100%;
display: flex;
`,
settingsWrapper: css`
margin: ${theme.spacing(0, 2, 2)};
display: flex;
flex-grow: 1;
`,
settingsContent: css`
flex-grow: 1;
height: 100%;
padding: 32px;
border: 1px solid ${theme.colors.border.weak};
background: ${theme.colors.background.primary};
border-radius: ${theme.shape.borderRadius()};
`,
}));