dashfolder: refactor breadcrumbs in PageHeader

Combines title and breadcrumbs in PageHeader instead of having to
set title to empty and add it as a breadcrumb.
This commit is contained in:
Daniel Lee 2018-01-11 17:53:06 +01:00
parent 545d7b9477
commit 21b5ded75b
9 changed files with 82 additions and 19 deletions

View File

@ -49,7 +49,7 @@ export class FolderSettings extends React.Component<IContainerProps, any> {
const { nav, folder, view } = this.props; const { nav, folder, view } = this.props;
folder folder
.saveDashboard(this.dashboard, { overwrite: false }) .saveFolder(this.dashboard, { overwrite: false })
.then(newUrl => { .then(newUrl => {
view.updatePathAndQuery(newUrl, '', ''); view.updatePathAndQuery(newUrl, '', '');
@ -96,7 +96,7 @@ export class FolderSettings extends React.Component<IContainerProps, any> {
yesText: 'Save & Overwrite', yesText: 'Save & Overwrite',
icon: 'fa-warning', icon: 'fa-warning',
onConfirm: () => { onConfirm: () => {
this.props.folder.saveDashboard(this.dashboard, { overwrite: true }); this.props.folder.saveFolder(this.dashboard, { overwrite: true });
}, },
}); });
} }

View File

@ -0,0 +1,53 @@
import React from 'react';
import PageHeader from './PageHeader';
import { shallow } from 'enzyme';
describe('PageHeader', () => {
let wrapper;
describe('when the nav tree has a node with a title', () => {
beforeAll(() => {
const nav = {
main: {
icon: 'fa fa-folder-open',
id: 'node',
subTitle: 'node subtitle',
url: '',
text: 'node',
},
node: {},
};
wrapper = shallow(<PageHeader model={nav as any} />);
});
it('should render the title', () => {
const title = wrapper.find('.page-header__title');
expect(title.text()).toBe('node');
});
});
describe('when the nav tree has a node with breadcrumbs and a title', () => {
beforeAll(() => {
const nav = {
main: {
icon: 'fa fa-folder-open',
id: 'child',
subTitle: 'child subtitle',
url: '',
text: 'child',
breadcrumbs: [{ title: 'Parent', url: 'parentUrl' }],
},
node: {},
};
wrapper = shallow(<PageHeader model={nav as any} />);
});
it('should render the title with breadcrumbs first and then title last', () => {
const title = wrapper.find('.page-header__title');
expect(title.text()).toBe('Parent / child');
const parentLink = wrapper.find('.page-header__title > a.text-link');
expect(parentLink.prop('href')).toBe('parentUrl');
});
});
});

View File

@ -85,7 +85,15 @@ export default class PageHeader extends React.Component<IProps, any> {
super(props); super(props);
} }
renderBreadcrumb(breadcrumbs) { renderTitle(title: string, breadcrumbs: any[]) {
if (!title && (!breadcrumbs || breadcrumbs.length === 0)) {
return null;
}
if (!breadcrumbs || breadcrumbs.length === 0) {
return <h1 className="page-header__title">{title}</h1>;
}
const breadcrumbsResult = []; const breadcrumbsResult = [];
for (let i = 0; i < breadcrumbs.length; i++) { for (let i = 0; i < breadcrumbs.length; i++) {
const bc = breadcrumbs[i]; const bc = breadcrumbs[i];
@ -99,7 +107,9 @@ export default class PageHeader extends React.Component<IProps, any> {
breadcrumbsResult.push(<span key={i}> / {bc.title}</span>); breadcrumbsResult.push(<span key={i}> / {bc.title}</span>);
} }
} }
return breadcrumbsResult; breadcrumbsResult.push(<span key={breadcrumbs.length + 1}> / {title}</span>);
return <h1 className="page-header__title">{breadcrumbsResult}</h1>;
} }
renderHeaderTitle(main) { renderHeaderTitle(main) {
@ -111,11 +121,7 @@ export default class PageHeader extends React.Component<IProps, any> {
</span> </span>
<div className="page-header__info-block"> <div className="page-header__info-block">
{main.text && <h1 className="page-header__title">{main.text}</h1>} {this.renderTitle(main.text, main.breadcrumbs)}
{main.breadcrumbs &&
main.breadcrumbs.length > 0 && (
<h1 className="page-header__title">{this.renderBreadcrumb(main.breadcrumbs)}</h1>
)}
{main.subTitle && <div className="page-header__sub-title">{main.subTitle}</div>} {main.subTitle && <div className="page-header__sub-title">{main.subTitle}</div>}
{main.subType && ( {main.subType && (
<div className="page-header__stamps"> <div className="page-header__stamps">

View File

@ -10,8 +10,9 @@ export class InvitedCtrl {
$scope.navModel = { $scope.navModel = {
main: { main: {
icon: 'gicon gicon-branding', icon: 'gicon gicon-branding',
text: 'Invite',
subTitle: 'Register your Grafana account', subTitle: 'Register your Grafana account',
breadcrumbs: [{ title: 'Login', url: '/login' }, { title: 'Invite' }], breadcrumbs: [{ title: 'Login', url: '/login' }],
}, },
}; };

View File

@ -16,8 +16,9 @@ export class ResetPasswordCtrl {
$scope.navModel = { $scope.navModel = {
main: { main: {
icon: 'gicon gicon-branding', icon: 'gicon gicon-branding',
text: 'Reset Password',
subTitle: 'Reset your Grafana password', subTitle: 'Reset your Grafana password',
breadcrumbs: [{ title: 'Login', url: 'login' }, { title: 'Reset Password' }], breadcrumbs: [{ title: 'Login', url: 'login' }],
}, },
}; };

View File

@ -11,7 +11,7 @@ export class FolderPageLoader {
subTitle: 'Manage folder dashboards & permissions', subTitle: 'Manage folder dashboards & permissions',
url: '', url: '',
text: '', text: '',
breadcrumbs: [{ title: 'Dashboards', url: 'dashboards' }, { title: ' ' }], breadcrumbs: [{ title: 'Dashboards', url: 'dashboards' }],
children: [ children: [
{ {
active: activeChildId === 'manage-folder-dashboards', active: activeChildId === 'manage-folder-dashboards',
@ -40,8 +40,7 @@ export class FolderPageLoader {
return this.backendSrv.getDashboard('db', this.$routeParams.slug).then(result => { return this.backendSrv.getDashboard('db', this.$routeParams.slug).then(result => {
const folderTitle = result.dashboard.title; const folderTitle = result.dashboard.title;
ctrl.navModel.main.text = ''; ctrl.navModel.main.text = folderTitle;
ctrl.navModel.main.breadcrumbs = [{ title: 'Dashboards', url: 'dashboards' }, { title: folderTitle }];
const folderUrl = this.createFolderUrl(folderId, result.meta.slug); const folderUrl = this.createFolderUrl(folderId, result.meta.slug);

View File

@ -32,8 +32,8 @@ export class PluginEditCtrl {
img: model.info.logos.large, img: model.info.logos.large,
subTitle: model.info.author.name, subTitle: model.info.author.name,
url: '', url: '',
text: '', text: model.name,
breadcrumbs: [{ title: 'Plugins', url: 'plugins' }, { title: model.name }], breadcrumbs: [{ title: 'Plugins', url: 'plugins' }],
children: [ children: [
{ {
icon: 'fa fa-fw fa-file-text-o', icon: 'fa fa-fw fa-file-text-o',

View File

@ -40,8 +40,8 @@ export class AppPageCtrl {
img: app.info.logos.large, img: app.info.logos.large,
subTitle: app.name, subTitle: app.name,
url: '', url: '',
text: '', text: this.page.name,
breadcrumbs: [{ title: app.name, url: pluginNav.main.url }, { title: this.page.name }], breadcrumbs: [{ title: app.name, url: pluginNav.main.url }],
}, },
}; };
} }

View File

@ -25,11 +25,13 @@ export const FolderStore = types
}); });
return res; return res;
}), }),
setTitle: function(originalTitle: string, title: string) { setTitle: function(originalTitle: string, title: string) {
self.folder.title = title; self.folder.title = title;
self.folder.hasChanged = originalTitle.toLowerCase() !== title.trim().toLowerCase() && title.trim().length > 0; self.folder.hasChanged = originalTitle.toLowerCase() !== title.trim().toLowerCase() && title.trim().length > 0;
}, },
saveDashboard: flow(function* saveDashboard(dashboard: any, options: any) {
saveFolder: flow(function* saveFolder(dashboard: any, options: any) {
const backendSrv = getEnv(self).backendSrv; const backendSrv = getEnv(self).backendSrv;
dashboard.title = self.folder.title.trim(); dashboard.title = self.folder.title.trim();
@ -37,6 +39,7 @@ export const FolderStore = types
self.folder.slug = res.slug; self.folder.slug = res.slug;
return `dashboards/folder/${self.folder.id}/${res.slug}/settings`; return `dashboards/folder/${self.folder.id}/${res.slug}/settings`;
}), }),
deleteFolder: flow(function* deleteFolder() { deleteFolder: flow(function* deleteFolder() {
const backendSrv = getEnv(self).backendSrv; const backendSrv = getEnv(self).backendSrv;