Commit 65b96e11 by Torkel Ödegaard

Merge branch 'react-panels' of github.com:grafana/grafana into react-panels

parents c74a1c18 13bc9f6f
......@@ -5,14 +5,10 @@ coreModule.directive('dashClass', function($timeout) {
return {
link: function($scope, elem) {
$scope.ctrl.dashboard.events.on('view-mode-changed', function(panel) {
$timeout(() => {
elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true);
});
elem.toggleClass('panel-in-fullscreen', panel.fullscreen === true);
});
$scope.onAppEvent('panel-fullscreen-exit', function() {
elem.toggleClass('panel-in-fullscreen', false);
});
elem.toggleClass('panel-in-fullscreen', $scope.ctrl.dashboard.meta.fullscreen === true);
$scope.$watch('ctrl.playlistSrv.isPlaying', function(newValue) {
elem.toggleClass('playlist-active', newValue === true);
......
......@@ -85,7 +85,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
this.dashboard.on('panel-added', this.triggerForceUpdate.bind(this));
this.dashboard.on('panel-removed', this.triggerForceUpdate.bind(this));
this.dashboard.on('repeats-processed', this.triggerForceUpdate.bind(this));
this.dashboard.on('view-mode-changed', this.triggerForceUpdate.bind(this));
this.dashboard.on('view-mode-changed', this.onViewModeChanged.bind(this));
this.dashboard.on('row-collapsed', this.triggerForceUpdate.bind(this));
this.dashboard.on('row-expanded', this.triggerForceUpdate.bind(this));
}
......@@ -142,6 +142,10 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
}
}
onViewModeChanged(payload) {
this.setState({ animated: payload.fullscreen });
}
updateGridPos(item, layout) {
this.panelMap[item.i].updateGridPos(item);
......@@ -165,9 +169,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
componentDidMount() {
setTimeout(() => {
this.setState(() => {
return { animated: true };
});
this.setState({ animated: true });
});
}
......@@ -178,7 +180,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen });
panelElements.push(
<div key={panel.id.toString()} className={panelClasses}>
<DashboardPanel panel={panel} dashboard={this.dashboard} />
<DashboardPanel panel={panel} dashboard={this.dashboard} panelContainer={this.panelContainer} />
</div>
);
}
......
import React from 'react';
import config from 'app/core/config';
import classNames from 'classnames';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import { AttachedPanel } from './PanelLoader';
import { DashboardRow } from './DashboardRow';
import { PanelContainer } from './PanelContainer';
import { AddPanelPanel } from './AddPanelPanel';
import { importPluginModule } from 'app/features/plugins/plugin_loader';
import { PanelChrome } from './PanelChrome';
export interface DashboardPanelProps {
panel: PanelModel;
dashboard: DashboardModel;
panelContainer: PanelContainer;
}
export class DashboardPanel extends React.Component<DashboardPanelProps, any> {
......@@ -50,77 +52,43 @@ export class DashboardPanel extends React.Component<DashboardPanelProps, any> {
return <AddPanelPanel panel={this.props.panel} dashboard={this.props.dashboard} />;
}
render() {
if (this.isSpecial()) {
return this.specialPanels[this.props.panel.type]();
componentDidUpdate() {
// skip loading angular component if we have no element
// or we have already loaded it
if (!this.element || this.attachedPanel) {
return;
}
let PanelComponent = null;
const loader = this.props.panelContainer.getPanelLoader();
this.attachedPanel = loader.load(this.element, this.props.panel, this.props.dashboard);
}
if (this.pluginExports && this.pluginExports.PanelComponent) {
PanelComponent = this.pluginExports.PanelComponent;
componentWillUnmount() {
if (this.attachedPanel) {
this.attachedPanel.destroy();
}
return (
<div className="panel-container">
<PanelHeader panel={this.props.panel} dashboard={this.props.dashboard} />
<div className="panel-content">{PanelComponent && <PanelComponent />}</div>
</div>
);
}
}
interface PanelHeaderProps {
panel: PanelModel;
dashboard: DashboardModel;
}
render() {
if (this.isSpecial()) {
return this.specialPanels[this.props.panel.type]();
}
export class PanelHeader extends React.Component<PanelHeaderProps, any> {
onEditPanel = () => {
this.props.dashboard.setViewMode(this.props.panel, true, true);
};
if (!this.pluginExports) {
return null;
}
render() {
let isFullscreen = false;
let isLoading = false;
let panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen });
return (
<div className={panelHeaderClass}>
<span className="panel-info-corner">
<i className="fa" />
<span className="panel-info-corner-inner" />
</span>
{isLoading && (
<span className="panel-loading">
<i className="fa fa-spinner fa-spin" />
</span>
)}
<div className="panel-title-container">
<span className="panel-title">
<span className="icon-gf panel-alert-icon" />
<span className="panel-title-text">{this.props.panel.title}</span>
<span className="panel-menu-container dropdown">
<span className="fa fa-caret-down panel-menu-toggle" data-toggle="dropdown" />
<ul className="dropdown-menu dropdown-menu--menu panel-menu" role="menu">
<li>
<a onClick={this.onEditPanel}>
<i className="fa fa-fw fa-edit" /> Edit
</a>
</li>
<li>
<a href="asd">asd</a>
</li>
</ul>
</span>
<span className="panel-time-info">
<i className="fa fa-clock-o" /> 4m
</span>
</span>
</div>
</div>
);
if (this.pluginExports.PanelComponent) {
return (
<PanelChrome
component={this.pluginExports.PanelComponent}
panel={this.props.panel}
dashboard={this.props.dashboard}
/>
);
}
// legacy angular rendering
return <div ref={element => (this.element = element)} className="panel-height-helper" />;
}
}
import React from 'react';
import $ from 'jquery';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants';
import { PanelHeader } from './PanelHeader';
import { PanelEditor } from './PanelEditor';
const TITLE_HEIGHT = 27;
const PANEL_BORDER = 2;
export interface PanelChromeProps {
panel: PanelModel;
dashboard: DashboardModel;
component: any;
}
export class PanelChrome extends React.Component<PanelChromeProps, any> {
constructor(props) {
super(props);
this.props.panel.events.on('panel-size-changed', this.triggerForceUpdate.bind(this));
}
triggerForceUpdate() {
this.forceUpdate();
}
render() {
let panelContentStyle = {
height: this.getPanelHeight(),
};
let PanelComponent = this.props.component;
console.log('PanelChrome render');
return (
<div className="panel-height-helper">
<div className="panel-container">
<PanelHeader panel={this.props.panel} dashboard={this.props.dashboard} />
<div className="panel-content" style={panelContentStyle}>
{<PanelComponent />}
</div>
</div>
<div>
{this.props.panel.isEditing && <PanelEditor panel={this.props.panel} dashboard={this.props.dashboard} />}
</div>
</div>
);
}
getPanelHeight() {
const panel = this.props.panel;
let height = 0;
if (panel.fullscreen) {
var docHeight = $(window).height();
var editHeight = Math.floor(docHeight * 0.4);
var fullscreenHeight = Math.floor(docHeight * 0.8);
height = panel.isEditing ? editHeight : fullscreenHeight;
} else {
height = panel.gridPos.h * GRID_CELL_HEIGHT + (panel.gridPos.h - 1) * GRID_CELL_VMARGIN;
}
return height - PANEL_BORDER + TITLE_HEIGHT;
}
}
import React from 'react';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
interface PanelEditorProps {
panel: PanelModel;
dashboard: DashboardModel;
}
export class PanelEditor extends React.Component<PanelEditorProps, any> {
render() {
return (
<div className="tabbed-view tabbed-view--panel-edit">
<div className="tabbed-view-header">
<ul className="gf-tabs">
<li className="gf-tabs-item">
<a className="gf-tabs-link active">Queries</a>
</li>
<li className="gf-tabs-item">
<a className="gf-tabs-link">Visualization</a>
</li>
</ul>
<button className="tabbed-view-close-btn" ng-click="ctrl.exitFullscreen();">
<i className="fa fa-remove" />
</button>
</div>
<div className="tabbed-view-body">testing</div>
</div>
);
}
}
import React from 'react';
import classNames from 'classnames';
import { PanelModel } from '../panel_model';
import { DashboardModel } from '../dashboard_model';
import { store } from 'app/stores/store';
interface PanelHeaderProps {
panel: PanelModel;
dashboard: DashboardModel;
}
export class PanelHeader extends React.Component<PanelHeaderProps, any> {
onEditPanel = () => {
store.view.updateQuery({
panelId: this.props.panel.id,
edit: true,
fullscreen: true,
});
};
render() {
let isFullscreen = false;
let isLoading = false;
let panelHeaderClass = classNames({ 'panel-header': true, 'grid-drag-handle': !isFullscreen });
return (
<div className={panelHeaderClass}>
<span className="panel-info-corner">
<i className="fa" />
<span className="panel-info-corner-inner" />
</span>
{isLoading && (
<span className="panel-loading">
<i className="fa fa-spinner fa-spin" />
</span>
)}
<div className="panel-title-container">
<span className="panel-title">
<span className="icon-gf panel-alert-icon" />
<span className="panel-title-text">{this.props.panel.title}</span>
<span className="panel-menu-container dropdown">
<span className="fa fa-caret-down panel-menu-toggle" data-toggle="dropdown" />
<ul className="dropdown-menu dropdown-menu--menu panel-menu" role="menu">
<li>
<a onClick={this.onEditPanel}>
<i className="fa fa-fw fa-edit" /> Edit
</a>
</li>
<li>
<a href="asd">asd</a>
</li>
</ul>
</span>
<span className="panel-time-info">
<i className="fa fa-clock-o" /> 4m
</span>
</span>
</div>
</div>
);
}
}
......@@ -82,7 +82,6 @@ export class PanelModel {
this.gridPos.h = newPos.h;
if (sizeChanged) {
console.log('PanelModel sizeChanged event and render events fired');
this.events.emit('panel-size-changed');
}
}
......@@ -91,6 +90,10 @@ export class PanelModel {
this.events.emit('panel-size-changed');
}
initEditMode() {
this.events.emit('panel-init-edit-mode');
}
destroy() {
this.events.removeAllListeners();
}
......
......@@ -14,7 +14,7 @@ jest.mock('app/core/store', () => ({
}));
describe('AddPanelPanel', () => {
let wrapper, dashboardMock, getPanelContainer, panel;
let wrapper, dashboardMock, panel;
beforeEach(() => {
config.panels = [
......@@ -77,13 +77,8 @@ describe('AddPanelPanel', () => {
dashboardMock = { toggleRow: jest.fn() };
getPanelContainer = jest.fn().mockReturnValue({
getDashboard: jest.fn().mockReturnValue(dashboardMock),
getPanelLoader: jest.fn(),
});
panel = new PanelModel({ collapsed: false });
wrapper = shallow(<AddPanelPanel panel={panel} getPanelContainer={getPanelContainer} />);
wrapper = shallow(<AddPanelPanel panel={panel} dashboard={dashboardMock} />);
});
it('should fetch all panels sorted with core plugins first', () => {
......
......@@ -33,10 +33,6 @@ export class DashboardViewState {
self.update(payload);
});
$scope.onAppEvent('panel-initialized', function(evt, payload) {
self.registerPanel(payload.scope);
});
// this marks changes to location during this digest cycle as not to add history item
// don't want url changes like adding orgId to add browser history
$location.replace();
......@@ -124,102 +120,61 @@ export class DashboardViewState {
}
syncState() {
if (this.panelScopes.length === 0) {
return;
}
if (this.dashboard.meta.fullscreen) {
var panelScope = this.getPanelScope(this.state.panelId);
if (!panelScope) {
var panel = this.dashboard.getPanelById(this.state.panelId);
if (!panel) {
return;
}
if (this.fullscreenPanel) {
// if already fullscreen
if (this.fullscreenPanel === panelScope && this.editStateChanged === false) {
if (this.fullscreenPanel === panel && this.editStateChanged === false) {
return;
} else {
this.leaveFullscreen(false);
}
}
if (!panelScope.ctrl.editModeInitiated) {
panelScope.ctrl.initEditMode();
}
if (!panelScope.ctrl.fullscreen) {
this.enterFullscreen(panelScope);
if (!panel.fullscreen) {
this.enterFullscreen(panel);
}
} else if (this.fullscreenPanel) {
this.leaveFullscreen(true);
}
}
getPanelScope(id) {
return _.find(this.panelScopes, function(panelScope) {
return panelScope.ctrl.panel.id === id;
});
}
leaveFullscreen(render) {
var self = this;
var ctrl = self.fullscreenPanel.ctrl;
ctrl.editMode = false;
ctrl.fullscreen = false;
var panel = this.fullscreenPanel;
this.dashboard.setViewMode(ctrl.panel, false, false);
this.$scope.appEvent('panel-fullscreen-exit', { panelId: ctrl.panel.id });
this.dashboard.setViewMode(panel, false, false);
this.$scope.appEvent('dash-scroll', { restore: true });
if (!render) {
return false;
}
this.$timeout(function() {
if (self.oldTimeRange !== ctrl.range) {
self.$rootScope.$broadcast('refresh');
this.$timeout(() => {
if (this.oldTimeRange !== this.dashboard.time) {
this.$rootScope.$broadcast('refresh');
} else {
self.$rootScope.$broadcast('render');
this.$rootScope.$broadcast('render');
}
delete self.fullscreenPanel;
delete this.fullscreenPanel;
});
return true;
}
enterFullscreen(panelScope) {
var ctrl = panelScope.ctrl;
ctrl.editMode = this.state.edit && this.dashboard.meta.canEdit;
ctrl.fullscreen = true;
enterFullscreen(panel) {
const isEditing = this.state.edit && this.dashboard.meta.canEdit;
this.oldTimeRange = ctrl.range;
this.fullscreenPanel = panelScope;
this.oldTimeRange = this.dashboard.time;
this.fullscreenPanel = panel;
// Firefox doesn't return scrollTop position properly if 'dash-scroll' is emitted after setViewMode()
this.$scope.appEvent('dash-scroll', { animate: false, pos: 0 });
this.dashboard.setViewMode(ctrl.panel, true, ctrl.editMode);
this.$scope.appEvent('panel-fullscreen-enter', { panelId: ctrl.panel.id });
}
registerPanel(panelScope) {
var self = this;
self.panelScopes.push(panelScope);
if (!self.dashboard.meta.soloMode) {
if (self.state.panelId === panelScope.ctrl.panel.id) {
if (self.state.edit) {
panelScope.ctrl.editPanel();
} else {
panelScope.ctrl.viewPanel();
}
}
}
var unbind = panelScope.$on('$destroy', function() {
self.panelScopes = _.without(self.panelScopes, panelScope);
unbind();
});
this.dashboard.setViewMode(panel, true, isEditing);
}
}
......
......@@ -24,10 +24,8 @@ export class PanelCtrl {
$injector: any;
$location: any;
$timeout: any;
fullscreen: boolean;
inspector: any;
editModeInitiated: boolean;
editMode: any;
height: any;
containerHeight: any;
events: Emitter;
......@@ -130,6 +128,7 @@ export class PanelCtrl {
return { templateUrl: directiveFn };
};
}
if (index) {
this.editorTabs.splice(index, 0, editorTab);
} else {
......@@ -190,7 +189,7 @@ export class PanelCtrl {
getExtendedMenu() {
let menu = [];
if (!this.fullscreen && this.dashboard.meta.canEdit) {
if (!this.panel.fullscreen && this.dashboard.meta.canEdit) {
menu.push({
text: 'Duplicate',
click: 'ctrl.duplicate()',
......@@ -220,15 +219,15 @@ export class PanelCtrl {
}
otherPanelInFullscreenMode() {
return this.dashboard.meta.fullscreen && !this.fullscreen;
return this.dashboard.meta.fullscreen && !this.panel.fullscreen;
}
calculatePanelHeight() {
if (this.fullscreen) {
if (this.panel.fullscreen) {
var docHeight = $(window).height();
var editHeight = Math.floor(docHeight * 0.4);
var fullscreenHeight = Math.floor(docHeight * 0.8);
this.containerHeight = this.editMode ? editHeight : fullscreenHeight;
this.containerHeight = this.panel.isEditing ? editHeight : fullscreenHeight;
} else {
this.containerHeight = this.panel.gridPos.h * GRID_CELL_HEIGHT + (this.panel.gridPos.h - 1) * GRID_CELL_VMARGIN;
}
......@@ -237,6 +236,11 @@ export class PanelCtrl {
this.containerHeight = $(window).height();
}
// hacky solution
if (this.panel.isEditing && !this.editModeInitiated) {
this.initEditMode();
}
this.height = this.containerHeight - (PANEL_BORDER + TITLE_HEIGHT);
}
......@@ -247,9 +251,6 @@ export class PanelCtrl {
duplicate() {
this.dashboard.duplicatePanel(this.panel);
this.$timeout(() => {
this.$scope.$root.$broadcast('render');
});
}
removePanel() {
......
......@@ -7,7 +7,7 @@ var module = angular.module('grafana.directives');
var panelTemplate = `
<div class="panel-container">
<div class="panel-header" ng-class="{'grid-drag-handle': !ctrl.fullscreen}">
<div class="panel-header" ng-class="{'grid-drag-handle': !ctrl.panel.fullscreen}">
<span class="panel-info-corner">
<i class="fa"></i>
<span class="panel-info-corner-inner"></span>
......@@ -25,7 +25,7 @@ var panelTemplate = `
</div>
</div>
<div class="panel-full-edit" ng-if="ctrl.editMode">
<div class="panel-full-edit" ng-if="ctrl.panel.isEditing">
<div class="tabbed-view tabbed-view--panel-edit">
<div class="tabbed-view-header">
<h3 class="tabbed-view-panel-title">
......
......@@ -197,6 +197,7 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) {
// Function for rendering panel
function render_panel() {
panelWidth = elem.width();
if (shouldAbortRender()) {
return;
}
......
......@@ -6,7 +6,7 @@ export class ReactTestPanel extends React.Component<any, any> {
}
render() {
return <h2>Panel content</h2>;
return <h2>I am a react panel, haha!</h2>;
}
}
......
......@@ -20,7 +20,6 @@
}
// Disable grid interaction indicators in fullscreen panels
.panel-header:hover {
background-color: inherit;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment