浏览代码

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

Torkel Ödegaard 7 年之前
父节点
当前提交
65b96e1161

+ 2 - 6
public/app/core/directives/dash_class.ts

@@ -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);

+ 7 - 5
public/app/features/dashboard/dashgrid/DashboardGrid.tsx

@@ -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>
       );
     }

+ 33 - 65
public/app/features/dashboard/dashgrid/DashboardPanel.tsx

@@ -1,16 +1,18 @@
 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" />;
   }
 }

+ 67 - 0
public/app/features/dashboard/dashgrid/PanelChrome.tsx

@@ -0,0 +1,67 @@
+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;
+  }
+}

+ 33 - 0
public/app/features/dashboard/dashgrid/PanelEditor.tsx

@@ -0,0 +1,33 @@
+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>
+    );
+  }
+}

+ 64 - 0
public/app/features/dashboard/dashgrid/PanelHeader.tsx

@@ -0,0 +1,64 @@
+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>
+    );
+  }
+}

+ 4 - 1
public/app/features/dashboard/panel_model.ts

@@ -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();
   }

+ 2 - 7
public/app/features/dashboard/specs/AddPanelPanel.jest.tsx

@@ -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', () => {

+ 19 - 64
public/app/features/dashboard/view_state_srv.ts

@@ -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);
   }
 }
 

+ 10 - 9
public/app/features/panel/panel_ctrl.ts

@@ -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() {

+ 2 - 2
public/app/features/panel/panel_directive.ts

@@ -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">

+ 1 - 0
public/app/plugins/panel/graph/graph.ts

@@ -197,6 +197,7 @@ function graphDirective(timeSrv, popoverSrv, contextSrv) {
       // Function for rendering panel
       function render_panel() {
         panelWidth = elem.width();
+
         if (shouldAbortRender()) {
           return;
         }

+ 1 - 1
public/app/plugins/panel/text2/module.tsx

@@ -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>;
   }
 }
 

+ 0 - 1
public/sass/components/_dashboard_grid.scss

@@ -20,7 +20,6 @@
   }
 
   // Disable grid interaction indicators in fullscreen panels
-
   .panel-header:hover {
     background-color: inherit;
   }