mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Dashboard settings starting to work
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import { Emitter } from './utils/emitter';
|
import { Emitter } from './utils/emitter';
|
||||||
|
|
||||||
const appEvents = new Emitter();
|
export const appEvents = new Emitter();
|
||||||
|
|
||||||
export default appEvents;
|
export default appEvents;
|
||||||
|
|||||||
144
public/app/features/dashboard/components/DashNav/DashNav.tsx
Normal file
144
public/app/features/dashboard/components/DashNav/DashNav.tsx
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
// Libaries
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
import { connect } from 'react-redux';
|
||||||
|
|
||||||
|
// Utils & Services
|
||||||
|
import { appEvents } from 'app/core/app_events';
|
||||||
|
|
||||||
|
// State
|
||||||
|
import { updateLocation } from 'app/core/actions';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { DashboardModel } from '../../state/DashboardModel';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
dashboard: DashboardModel | null;
|
||||||
|
updateLocation: typeof updateLocation;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DashNav extends PureComponent<Props> {
|
||||||
|
onOpenSearch = () => {
|
||||||
|
appEvents.emit('show-dash-search');
|
||||||
|
};
|
||||||
|
|
||||||
|
onAddPanel = () => {};
|
||||||
|
onOpenSettings = () => {
|
||||||
|
this.props.updateLocation({
|
||||||
|
query: {
|
||||||
|
editview: 'settings',
|
||||||
|
},
|
||||||
|
partial: true,
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
renderLoadingState() {
|
||||||
|
return (
|
||||||
|
<div className="navbar">
|
||||||
|
<div>
|
||||||
|
<a className="navbar-page-btn" onClick={this.onOpenSearch}>
|
||||||
|
<i className="gicon gicon-dashboard" />
|
||||||
|
Loading...
|
||||||
|
<i className="fa fa-caret-down" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { dashboard } = this.props;
|
||||||
|
|
||||||
|
if (!dashboard) {
|
||||||
|
return this.renderLoadingState();
|
||||||
|
}
|
||||||
|
|
||||||
|
const haveFolder = dashboard.meta.folderId > 0;
|
||||||
|
const { canEdit, canSave, folderTitle, showSettings } = dashboard.meta;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="navbar">
|
||||||
|
<div>
|
||||||
|
<a className="navbar-page-btn" onClick={this.onOpenSearch}>
|
||||||
|
<i className="gicon gicon-dashboard" />
|
||||||
|
{haveFolder && <span className="navbar-page-btn--folder">{folderTitle} / </span>}
|
||||||
|
{dashboard.title}
|
||||||
|
<i className="fa fa-caret-down" />
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="navbar__spacer" />
|
||||||
|
{/*
|
||||||
|
<div class="navbar-buttons navbar-buttons--playlist" ng-if="ctrl.playlistSrv.isPlaying">
|
||||||
|
<a class="navbar-button navbar-button--tight" ng-click="ctrl.playlistSrv.prev()"><i class="fa fa-step-backward"></i></a>
|
||||||
|
<a class="navbar-button navbar-button--tight" ng-click="ctrl.playlistSrv.stop()"><i class="fa fa-stop"></i></a>
|
||||||
|
<a class="navbar-button navbar-button--tight" ng-click="ctrl.playlistSrv.next()"><i class="fa fa-step-forward"></i></a>
|
||||||
|
</div>
|
||||||
|
*/}
|
||||||
|
|
||||||
|
<div className="navbar-buttons navbar-buttons--actions">
|
||||||
|
{canEdit && (
|
||||||
|
<button className="btn navbar-button navbar-button--add-panel" title="Add panel" onClick={this.onAddPanel}>
|
||||||
|
<i className="gicon gicon-add-panel" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{showSettings && (
|
||||||
|
<button
|
||||||
|
className="btn navbar-button navbar-button--settings"
|
||||||
|
onClick={this.onOpenSettings}
|
||||||
|
title="Dashboard Settings"
|
||||||
|
>
|
||||||
|
<i className="fa fa-cog" />
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{
|
||||||
|
// <button class="btn navbar-button navbar-button--star" ng-show="::ctrl.dashboard.meta.canStar" ng-click="ctrl.starDashboard()" bs-tooltip="'Mark as favorite'" data-placement="bottom">
|
||||||
|
// <i class="fa" ng-class="{'fa-star-o': !ctrl.dashboard.meta.isStarred, 'fa-star': ctrl.dashboard.meta.isStarred}"></i>
|
||||||
|
// </button>
|
||||||
|
//
|
||||||
|
// <button class="btn navbar-button navbar-button--share" ng-show="::ctrl.dashboard.meta.canShare" ng-click="ctrl.shareDashboard(0)" bs-tooltip="'Share dashboard'" data-placement="bottom">
|
||||||
|
// <i class="fa fa-share-square-o"></i></a>
|
||||||
|
// </button>
|
||||||
|
//
|
||||||
|
// <button class="btn navbar-button navbar-button--save" ng-show="ctrl.dashboard.meta.canSave" ng-click="ctrl.saveDashboard()" bs-tooltip="'Save dashboard <br> CTRL+S'" data-placement="bottom">
|
||||||
|
// <i class="fa fa-save"></i>
|
||||||
|
// </button>
|
||||||
|
//
|
||||||
|
// <a class="btn navbar-button navbar-button--snapshot-origin" ng-if="::ctrl.dashboard.snapshot.originalUrl" href="{{ctrl.dashboard.snapshot.originalUrl}}" bs-tooltip="'Open original dashboard'" data-placement="bottom">
|
||||||
|
// <i class="fa fa-link"></i>
|
||||||
|
// </a>
|
||||||
|
//
|
||||||
|
// <button class="btn navbar-button navbar-button--settings" ng-click="ctrl.toggleSettings()" bs-tooltip="'Dashboard Settings'" data-placement="bottom" ng-show="ctrl.dashboard.meta.showSettings">
|
||||||
|
// <i class="fa fa-cog"></i>
|
||||||
|
// </button>
|
||||||
|
// </div>
|
||||||
|
//
|
||||||
|
// <div class="navbar-buttons navbar-buttons--tv">
|
||||||
|
// <button class="btn navbar-button navbar-button--tv" ng-click="ctrl.toggleViewMode()" bs-tooltip="'Cycle view mode'" data-placement="bottom">
|
||||||
|
// <i class="fa fa-desktop"></i>
|
||||||
|
// </button>
|
||||||
|
// </div>
|
||||||
|
//
|
||||||
|
// <gf-time-picker class="gf-timepicker-nav" dashboard="ctrl.dashboard" ng-if="!ctrl.dashboard.timepicker.hidden"></gf-time-picker>
|
||||||
|
//
|
||||||
|
// <div class="navbar-buttons navbar-buttons--close">
|
||||||
|
// <button class="btn navbar-button navbar-button--primary" ng-click="ctrl.close()" bs-tooltip="'Back to dashboard'" data-placement="bottom">
|
||||||
|
// <i class="fa fa-reply"></i>
|
||||||
|
// </button>
|
||||||
|
// </div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapStateToProps = () => ({
|
||||||
|
});
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
updateLocation
|
||||||
|
};
|
||||||
|
|
||||||
|
export default connect(mapStateToProps, mapDispatchToProps)(DashNav);
|
||||||
@@ -1 +1,3 @@
|
|||||||
export { DashNavCtrl } from './DashNavCtrl';
|
export { DashNavCtrl } from './DashNavCtrl';
|
||||||
|
import DashNav from './DashNav';
|
||||||
|
export { DashNav };
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
// Libaries
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
|
|
||||||
|
// Utils & Services
|
||||||
|
import { AngularComponent, getAngularLoader } from 'app/core/services/AngularLoader';
|
||||||
|
|
||||||
|
// Types
|
||||||
|
import { DashboardModel } from '../../state/DashboardModel';
|
||||||
|
|
||||||
|
export interface Props {
|
||||||
|
dashboard: DashboardModel | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DashboardSettings extends PureComponent<Props> {
|
||||||
|
element: HTMLElement;
|
||||||
|
angularCmp: AngularComponent;
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const loader = getAngularLoader();
|
||||||
|
|
||||||
|
const template = '<dashboard-settings dashboard="dashboard" class="dashboard-settings" />';
|
||||||
|
const scopeProps = { dashboard: this.props.dashboard };
|
||||||
|
|
||||||
|
this.angularCmp = loader.load(this.element, scopeProps, template);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.angularCmp) {
|
||||||
|
this.angularCmp.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return <div className="panel-height-helper" ref={element => this.element = element} />;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
export { SettingsCtrl } from './SettingsCtrl';
|
export { SettingsCtrl } from './SettingsCtrl';
|
||||||
|
export { DashboardSettings } from './DashboardSettings';
|
||||||
|
|||||||
@@ -1,14 +1,19 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import React, { Component } from 'react';
|
import $ from 'jquery';
|
||||||
|
import React, { PureComponent } from 'react';
|
||||||
import { hot } from 'react-hot-loader';
|
import { hot } from 'react-hot-loader';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { LoadingPlaceholder } from '@grafana/ui';
|
import { LoadingPlaceholder } from '@grafana/ui';
|
||||||
import { DashboardGrid } from '../dashgrid/DashboardGrid';
|
import { DashboardGrid } from '../dashgrid/DashboardGrid';
|
||||||
|
import { DashNav } from '../components/DashNav';
|
||||||
|
import { DashboardSettings } from '../components/DashboardSettings';
|
||||||
|
|
||||||
// Redux
|
// Redux
|
||||||
import { initDashboard } from '../state/initDashboard';
|
import { initDashboard } from '../state/initDashboard';
|
||||||
|
import { setDashboardModel } from '../state/actions';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { StoreState } from 'app/types';
|
import { StoreState } from 'app/types';
|
||||||
@@ -20,22 +25,23 @@ interface Props {
|
|||||||
urlUid?: string;
|
urlUid?: string;
|
||||||
urlSlug?: string;
|
urlSlug?: string;
|
||||||
urlType?: string;
|
urlType?: string;
|
||||||
|
editview: string;
|
||||||
$scope: any;
|
$scope: any;
|
||||||
$injector: any;
|
$injector: any;
|
||||||
initDashboard: typeof initDashboard;
|
initDashboard: typeof initDashboard;
|
||||||
|
setDashboardModel: typeof setDashboardModel;
|
||||||
loadingState: DashboardLoadingState;
|
loadingState: DashboardLoadingState;
|
||||||
dashboard: DashboardModel;
|
dashboard: DashboardModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
dashboard: DashboardModel | null;
|
isSettingsOpening: boolean;
|
||||||
notFound: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export class DashboardPage extends Component<Props, State> {
|
export class DashboardPage extends PureComponent<Props, State> {
|
||||||
state: State = {
|
state: State = {
|
||||||
dashboard: null,
|
isSettingsOpening: false,
|
||||||
notFound: false,
|
isSettingsOpen: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
async componentDidMount() {
|
async componentDidMount() {
|
||||||
@@ -45,18 +51,82 @@ export class DashboardPage extends Component<Props, State> {
|
|||||||
urlSlug: this.props.urlSlug,
|
urlSlug: this.props.urlSlug,
|
||||||
urlUid: this.props.urlUid,
|
urlUid: this.props.urlUid,
|
||||||
urlType: this.props.urlType,
|
urlType: this.props.urlType,
|
||||||
})
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps: Props) {
|
||||||
|
const { dashboard, editview } = this.props;
|
||||||
|
|
||||||
|
// when dashboard has loaded subscribe to somme events
|
||||||
|
if (prevProps.dashboard === null && dashboard) {
|
||||||
|
dashboard.events.on('view-mode-changed', this.onViewModeChanged);
|
||||||
|
|
||||||
|
// set initial fullscreen class state
|
||||||
|
this.setPanelFullscreenClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!prevProps.editview && editview) {
|
||||||
|
this.setState({ isSettingsOpening: true });
|
||||||
|
setTimeout(() => {
|
||||||
|
this.setState({ isSettingsOpening: false});
|
||||||
|
}, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onViewModeChanged = () => {
|
||||||
|
this.setPanelFullscreenClass();
|
||||||
|
};
|
||||||
|
|
||||||
|
setPanelFullscreenClass() {
|
||||||
|
$('body').toggleClass('panel-in-fullscreen', this.props.dashboard.meta.fullscreen === true);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
if (this.props.dashboard) {
|
||||||
|
this.props.dashboard.destroy();
|
||||||
|
this.props.setDashboardModel(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderLoadingState() {
|
||||||
|
return <LoadingPlaceholder text="Loading" />;
|
||||||
|
}
|
||||||
|
|
||||||
|
renderDashboard() {
|
||||||
|
const { dashboard, editview } = this.props;
|
||||||
|
|
||||||
|
const classes = classNames({
|
||||||
|
'dashboard-container': true,
|
||||||
|
'dashboard-container--has-submenu': dashboard.meta.submenuEnabled
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="scroll-canvas scroll-canvas--dashboard">
|
||||||
|
{dashboard && editview && <DashboardSettings dashboard={dashboard} />}
|
||||||
|
|
||||||
|
<div className={classes}>
|
||||||
|
<DashboardGrid dashboard={dashboard} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { loadingState, dashboard } = this.props;
|
const { dashboard, editview } = this.props;
|
||||||
|
const { isSettingsOpening } = this.state;
|
||||||
|
|
||||||
if (!dashboard) {
|
const classes = classNames({
|
||||||
return <LoadingPlaceholder text={loadingState.toString()} />;
|
'dashboard-page--settings-opening': isSettingsOpening,
|
||||||
}
|
'dashboard-page--settings-open': !isSettingsOpening && editview,
|
||||||
|
});
|
||||||
|
|
||||||
console.log(dashboard);
|
return (
|
||||||
return <DashboardGrid dashboard={dashboard} />
|
<div className={classes}>
|
||||||
|
<DashNav dashboard={dashboard} />
|
||||||
|
{!dashboard && this.renderLoadingState()}
|
||||||
|
{dashboard && this.renderDashboard()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,12 +135,14 @@ const mapStateToProps = (state: StoreState) => ({
|
|||||||
urlSlug: state.location.routeParams.slug,
|
urlSlug: state.location.routeParams.slug,
|
||||||
urlType: state.location.routeParams.type,
|
urlType: state.location.routeParams.type,
|
||||||
panelId: state.location.query.panelId,
|
panelId: state.location.query.panelId,
|
||||||
|
editview: state.location.query.editview,
|
||||||
loadingState: state.dashboard.loadingState,
|
loadingState: state.dashboard.loadingState,
|
||||||
dashboard: state.dashboard.model as DashboardModel,
|
dashboard: state.dashboard.model as DashboardModel,
|
||||||
});
|
});
|
||||||
|
|
||||||
const mapDispatchToProps = {
|
const mapDispatchToProps = {
|
||||||
initDashboard
|
initDashboard,
|
||||||
|
setDashboardModel
|
||||||
};
|
};
|
||||||
|
|
||||||
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage));
|
export default hot(module)(connect(mapStateToProps, mapDispatchToProps)(DashboardPage));
|
||||||
|
|||||||
@@ -67,6 +67,7 @@ export function initDashboard({ injector, scope, urlUid, urlSlug, urlType }: Ini
|
|||||||
dashboard.updateSubmenuVisibility();
|
dashboard.updateSubmenuVisibility();
|
||||||
dashboard.autoFitPanels(window.innerHeight);
|
dashboard.autoFitPanels(window.innerHeight);
|
||||||
|
|
||||||
|
// init unsaved changes tracking
|
||||||
injector.get('unsavedChangesSrv').init(dashboard, scope);
|
injector.get('unsavedChangesSrv').init(dashboard, scope);
|
||||||
|
|
||||||
scope.dashboard = dashboard;
|
scope.dashboard = dashboard;
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ export const dashboardReducer = reducerFactory(initialState)
|
|||||||
model: action.payload
|
model: action.payload
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.create()
|
.create();
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
dashboard: dashboardReducer,
|
dashboard: dashboardReducer,
|
||||||
|
|||||||
@@ -16,6 +16,9 @@
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity 300ms ease-in-out;
|
transition: opacity 300ms ease-in-out;
|
||||||
}
|
}
|
||||||
|
.dashboard-container {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.dashboard-settings__content {
|
.dashboard-settings__content {
|
||||||
|
|||||||
Reference in New Issue
Block a user