Quellcode durchsuchen

react panels: working on changing type

Torkel Ödegaard vor 7 Jahren
Ursprung
Commit
761283231c

+ 1 - 11
public/app/core/config.ts

@@ -1,4 +1,5 @@
 import _ from 'lodash';
+import { PanelPlugin } from 'app/types/plugins';
 
 export interface BuildInfo {
   version: string;
@@ -7,17 +8,6 @@ export interface BuildInfo {
   env: string;
 }
 
-export interface PanelPlugin {
-  id: string;
-  name: string;
-  meta: any;
-  hideFromList: boolean;
-  module: string;
-  baseUrl: string;
-  info: any;
-  sort: number;
-}
-
 export class Settings {
   datasources: any;
   panels: PanelPlugin[];

+ 1 - 0
public/app/features/dashboard/dashgrid/DashboardGrid.tsx

@@ -171,6 +171,7 @@ export class DashboardGrid extends React.Component<DashboardGridProps, any> {
 
   renderPanels() {
     const panelElements = [];
+    console.log('render panels');
 
     for (let panel of this.props.dashboard.panels) {
       const panelClasses = classNames({ panel: true, 'panel--fullscreen': panel.fullscreen });

+ 61 - 20
public/app/features/dashboard/dashgrid/DashboardPanel.tsx

@@ -5,8 +5,10 @@ import { DashboardModel } from '../dashboard_model';
 import { getAngularLoader, AngularComponent } from 'app/core/services/angular_loader';
 import { DashboardRow } from './DashboardRow';
 import { AddPanelPanel } from './AddPanelPanel';
-import { importPluginModule, PluginExports } from 'app/features/plugins/plugin_loader';
+import { importPluginModule } from 'app/features/plugins/plugin_loader';
+import { PluginExports } from 'app/types/plugins';
 import { PanelChrome } from './PanelChrome';
+import { PanelEditor } from './PanelEditor';
 
 export interface Props {
   panel: PanelModel;
@@ -29,15 +31,11 @@ export class DashboardPanel extends React.Component<Props, State> {
 
     this.specialPanels['row'] = this.renderRow.bind(this);
     this.specialPanels['add-panel'] = this.renderAddPanel.bind(this);
+    this.props.panel.events.on('panel-size-changed', this.triggerForceUpdate.bind(this));
+  }
 
-    if (!this.isSpecial()) {
-      this.pluginInfo = config.panels[this.props.panel.type];
-
-      // load panel plugin
-      importPluginModule(this.pluginInfo.module).then(pluginExports => {
-        this.setState({ pluginExports: pluginExports });
-      });
-    }
+  triggerForceUpdate() {
+    this.forceUpdate();
   }
 
   isSpecial() {
@@ -52,8 +50,33 @@ export class DashboardPanel extends React.Component<Props, State> {
     return <AddPanelPanel panel={this.props.panel} dashboard={this.props.dashboard} />;
   }
 
+  loadPlugin() {
+    if (this.isSpecial()) {
+      return;
+    }
+
+    // handle plugin loading & changing of plugin type
+    if (!this.pluginInfo || this.pluginInfo.id !== this.props.panel.type) {
+      this.pluginInfo = config.panels[this.props.panel.type];
+
+      if (this.pluginInfo.exports) {
+        this.setState({ pluginExports: this.pluginInfo.exports });
+      } else {
+        importPluginModule(this.pluginInfo.module).then(pluginExports => {
+          this.setState({ pluginExports: pluginExports });
+        });
+      }
+    }
+  }
+
+  componentDidMount() {
+    this.loadPlugin();
+  }
+
   componentDidUpdate() {
-    // skip loading angular component if we have no element or we have already loaded it
+    this.loadPlugin();
+
+    // handle angular plugin loading
     if (!this.element || this.angularPanel) {
       return;
     }
@@ -70,25 +93,43 @@ export class DashboardPanel extends React.Component<Props, State> {
     }
   }
 
-  render() {
+  renderReactPanel() {
     const { pluginExports } = this.state;
+    const containerClass = this.props.panel.isEditing ? 'panel-editor-container' : 'panel-height-helper';
+    const panelWrapperClass = this.props.panel.isEditing ? 'panel-editor-container__panel' : 'panel-height-helper';
+
+    // this might look strange with these classes that change when edit, but
+    // I want to try to keep markup (parents) for panel the same in edit mode to avoide unmount / new mount of panel
+    // plugin component
+    return (
+      <div className={containerClass}>
+        <div className={panelWrapperClass}>
+          <PanelChrome
+            component={pluginExports.PanelComponent}
+            panel={this.props.panel}
+            dashboard={this.props.dashboard}
+          />
+        </div>
+        {this.props.panel.isEditing && (
+          <div className="panel-editor-container__editor">
+            <PanelEditor panel={this.props.panel} dashboard={this.props.dashboard} />
+          </div>
+        )}
+      </div>
+    );
+  }
 
+  render() {
     if (this.isSpecial()) {
       return this.specialPanels[this.props.panel.type]();
     }
 
-    if (!pluginExports) {
+    if (!this.state.pluginExports) {
       return null;
     }
 
-    if (pluginExports.PanelComponent) {
-      return (
-        <PanelChrome
-          component={pluginExports.PanelComponent}
-          panel={this.props.panel}
-          dashboard={this.props.dashboard}
-        />
-      );
+    if (this.state.pluginExports.PanelComponent) {
+      return this.renderReactPanel();
     }
 
     // legacy angular rendering

+ 4 - 43
public/app/features/dashboard/dashgrid/PanelChrome.tsx

@@ -2,23 +2,16 @@ import React, { ComponentClass } 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';
 import { DataPanel, PanelProps, DataPanelWrapper } from './DataPanel';
 
-const TITLE_HEIGHT = 27;
-const PANEL_BORDER = 2;
-
 export interface Props {
   panel: PanelModel;
   dashboard: DashboardModel;
   component: ComponentClass<PanelProps>;
 }
 
-interface State {
-  height: number;
-}
+interface State {}
 
 export class PanelChrome extends React.Component<Props, State> {
   panelComponent: DataPanel;
@@ -26,20 +19,9 @@ export class PanelChrome extends React.Component<Props, State> {
   constructor(props) {
     super(props);
 
-    this.state = {
-      height: this.getPanelHeight(),
-    };
-
     this.panelComponent = DataPanelWrapper(this.props.component);
-    this.props.panel.events.on('panel-size-changed', this.onPanelSizeChanged);
   }
 
-  onPanelSizeChanged = () => {
-    this.setState({
-      height: this.getPanelHeight(),
-    });
-  };
-
   componentDidMount() {
     console.log('panel chrome mounted');
   }
@@ -48,31 +30,10 @@ export class PanelChrome extends React.Component<Props, State> {
     let PanelComponent = this.panelComponent;
 
     return (
-      <div className="panel-editor-container">
-        <div className="panel-container">
-          <PanelHeader panel={this.props.panel} dashboard={this.props.dashboard} />
-          <div className="panel-content" style={{ height: this.state.height }}>
-            {<PanelComponent type={'test'} queries={[]} isVisible={true} />}
-          </div>
-        </div>
-        {this.props.panel.isEditing && <PanelEditor panel={this.props.panel} dashboard={this.props.dashboard} />}
+      <div className="panel-container">
+        <PanelHeader panel={this.props.panel} dashboard={this.props.dashboard} />
+        <div className="panel-content">{<PanelComponent type={'test'} queries={[]} isVisible={true} />}</div>
       </div>
     );
   }
-
-  getPanelHeight() {
-    const panel = this.props.panel;
-    let height = 0;
-
-    if (panel.fullscreen) {
-      var docHeight = $(window).height();
-      var editHeight = Math.floor(docHeight * 0.3);
-      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);
-  }
 }

+ 3 - 3
public/app/features/dashboard/dashgrid/PanelEditor.tsx

@@ -5,7 +5,7 @@ import { DashboardModel } from '../dashboard_model';
 import { store } from 'app/stores/store';
 import { observer } from 'mobx-react';
 import { QueriesTab } from './QueriesTab';
-import { PanelPlugin } from 'app/core/config';
+import { PanelPlugin } from 'app/types/plugins';
 import { VizTypePicker } from './VizTypePicker';
 
 interface PanelEditorProps {
@@ -50,8 +50,8 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> {
   }
 
   onVizTypeChanged = (plugin: PanelPlugin) => {
-    this.props.panel.type = plugin.id;
-    this.forceUpdate();
+    console.log('changing type to ', plugin.id);
+    this.props.panel.changeType(plugin.id);
   };
 
   onChangeTab = (tab: PanelEditorTab) => {

+ 13 - 1
public/app/features/dashboard/dashgrid/PanelHeader.tsx

@@ -21,6 +21,16 @@ export class PanelHeader extends React.Component<PanelHeaderProps, any> {
     );
   };
 
+  onViewPanel = () => {
+    store.view.updateQuery(
+      {
+        panelId: this.props.panel.id,
+        fullscreen: true,
+      },
+      false
+    );
+  };
+
   render() {
     let isFullscreen = false;
     let isLoading = false;
@@ -52,7 +62,9 @@ export class PanelHeader extends React.Component<PanelHeaderProps, any> {
                   </a>
                 </li>
                 <li>
-                  <a href="asd">asd</a>
+                  <a onClick={this.onViewPanel}>
+                    <i className="fa fa-fw fa-eye" /> View
+                  </a>
                 </li>
               </ul>
             </span>

+ 2 - 1
public/app/features/dashboard/dashgrid/VizTypePicker.tsx

@@ -1,6 +1,7 @@
 import React, { PureComponent } from 'react';
 import classNames from 'classnames';
-import config, { PanelPlugin } from 'app/core/config';
+import config from 'app/core/config';
+import { PanelPlugin } from 'app/types/plugins';
 import _ from 'lodash';
 
 interface Props {

+ 5 - 0
public/app/features/dashboard/panel_model.ts

@@ -97,6 +97,11 @@ export class PanelModel {
     this.events.emit('panel-init-edit-mode');
   }
 
+  changeType(newType: string) {
+    this.type = newType;
+    this.events.emit('panel-size-changed');
+  }
+
   destroy() {
     this.events.removeAllListeners();
   }

+ 2 - 0
public/app/features/plugins/built_in_plugins.ts

@@ -12,6 +12,7 @@ import * as mssqlPlugin from 'app/plugins/datasource/mssql/module';
 
 import * as textPanel from 'app/plugins/panel/text/module';
 import * as text2Panel from 'app/plugins/panel/text2/module';
+import * as graph2Panel from 'app/plugins/panel/graph2/module';
 import * as graphPanel from 'app/plugins/panel/graph/module';
 import * as dashListPanel from 'app/plugins/panel/dashlist/module';
 import * as pluginsListPanel from 'app/plugins/panel/pluginlist/module';
@@ -41,6 +42,7 @@ const builtInPlugins = {
 
   'app/plugins/panel/text/module': textPanel,
   'app/plugins/panel/text2/module': text2Panel,
+  'app/plugins/panel/graph2/module': graph2Panel,
   'app/plugins/panel/graph/module': graphPanel,
   'app/plugins/panel/dashlist/module': dashListPanel,
   'app/plugins/panel/pluginlist/module': pluginsListPanel,

+ 1 - 10
public/app/features/plugins/plugin_loader.ts

@@ -18,6 +18,7 @@ import config from 'app/core/config';
 import TimeSeries from 'app/core/time_series2';
 import TableModel from 'app/core/table_model';
 import { coreModule, appEvents, contextSrv } from 'app/core/core';
+import { PluginExports } from 'app/types/plugins';
 import * as datemath from 'app/core/utils/datemath';
 import * as fileExport from 'app/core/utils/file_export';
 import * as flatten from 'app/core/utils/flatten';
@@ -143,16 +144,6 @@ for (let flotDep of flotDeps) {
   exposeToPlugin(flotDep, { fakeDep: 1 });
 }
 
-export interface PluginExports {
-  PanelCtrl?;
-  any;
-  PanelComponent?: any;
-  Datasource?: any;
-  QueryCtrl?: any;
-  ConfigCtrl?: any;
-  AnnotationsQueryCtrl?: any;
-}
-
 export function importPluginModule(path: string): Promise<PluginExports> {
   let builtIn = builtInPlugins[path];
   if (builtIn) {

+ 5 - 0
public/app/plugins/panel/graph2/README.md

@@ -0,0 +1,5 @@
+# Text Panel -  Native Plugin
+
+The Text Panel is **included** with Grafana.
+
+The Text Panel is a very simple panel that displays text. The source text is written in the Markdown syntax meaning you can format the text. Read [GitHub's Mastering Markdown](https://guides.github.com/features/mastering-markdown/) to learn more.

+ 186 - 0
public/app/plugins/panel/graph2/img/icn-graph-panel.svg

@@ -0,0 +1,186 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 19.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="100px" height="100px" viewBox="0 0 100 100" style="enable-background:new 0 0 100 100;" xml:space="preserve">
+<polyline style="fill:none;stroke:#898989;stroke-width:2;stroke-miterlimit:10;" points="4.734,34.349 36.05,19.26 64.876,36.751 
+	96.308,6.946 "/>
+<circle style="fill:#898989;" cx="4.885" cy="33.929" r="4.885"/>
+<circle style="fill:#898989;" cx="35.95" cy="19.545" r="4.885"/>
+<circle style="fill:#898989;" cx="65.047" cy="36.046" r="4.885"/>
+<circle style="fill:#898989;" cx="94.955" cy="7.135" r="4.885"/>
+<g>
+	<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="5" y1="103.7019" x2="5" y2="32.0424">
+		<stop  offset="0" style="stop-color:#FFF33B"/>
+		<stop  offset="0" style="stop-color:#FFD53F"/>
+		<stop  offset="0" style="stop-color:#FBBC40"/>
+		<stop  offset="0" style="stop-color:#F7A840"/>
+		<stop  offset="0" style="stop-color:#F59B40"/>
+		<stop  offset="0" style="stop-color:#F3933F"/>
+		<stop  offset="0" style="stop-color:#F3903F"/>
+		<stop  offset="0.8423" style="stop-color:#ED683C"/>
+		<stop  offset="1" style="stop-color:#E93E3A"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_1_);" d="M9.001,48.173H0.999C0.447,48.173,0,48.62,0,49.172V100h10V49.172
+		C10,48.62,9.553,48.173,9.001,48.173z"/>
+	<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="5" y1="98.9423" x2="5" y2="53.1961">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#F99B1C"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_2_);" d="M0,69.173v30.563h10V69.173"/>
+	<linearGradient id="SVGID_3_" gradientUnits="userSpaceOnUse" x1="5" y1="99.4343" x2="5" y2="74.4359">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#FFDE17"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_3_);" d="M0,83.166v16.701h10V83.166"/>
+</g>
+<g>
+	<linearGradient id="SVGID_4_" gradientUnits="userSpaceOnUse" x1="20" y1="103.7019" x2="20" y2="32.0424">
+		<stop  offset="0" style="stop-color:#FFF33B"/>
+		<stop  offset="0" style="stop-color:#FFD53F"/>
+		<stop  offset="0" style="stop-color:#FBBC40"/>
+		<stop  offset="0" style="stop-color:#F7A840"/>
+		<stop  offset="0" style="stop-color:#F59B40"/>
+		<stop  offset="0" style="stop-color:#F3933F"/>
+		<stop  offset="0" style="stop-color:#F3903F"/>
+		<stop  offset="0.8423" style="stop-color:#ED683C"/>
+		<stop  offset="1" style="stop-color:#E93E3A"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_4_);" d="M24.001,40.769h-8.002c-0.552,0-0.999,0.447-0.999,0.999V100h10V41.768
+		C25,41.216,24.553,40.769,24.001,40.769z"/>
+	<linearGradient id="SVGID_5_" gradientUnits="userSpaceOnUse" x1="20" y1="98.9423" x2="20" y2="53.1961">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#F99B1C"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_5_);" d="M15,64.716v35.02h10v-35.02"/>
+	<linearGradient id="SVGID_6_" gradientUnits="userSpaceOnUse" x1="20" y1="99.4343" x2="20" y2="74.4359">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#FFDE17"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_6_);" d="M15,80.731v19.137h10V80.731"/>
+</g>
+<g>
+	<linearGradient id="SVGID_7_" gradientUnits="userSpaceOnUse" x1="35" y1="103.7019" x2="35" y2="32.0424">
+		<stop  offset="0" style="stop-color:#FFF33B"/>
+		<stop  offset="0" style="stop-color:#FFD53F"/>
+		<stop  offset="0" style="stop-color:#FBBC40"/>
+		<stop  offset="0" style="stop-color:#F7A840"/>
+		<stop  offset="0" style="stop-color:#F59B40"/>
+		<stop  offset="0" style="stop-color:#F3933F"/>
+		<stop  offset="0" style="stop-color:#F3903F"/>
+		<stop  offset="0.8423" style="stop-color:#ED683C"/>
+		<stop  offset="1" style="stop-color:#E93E3A"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_7_);" d="M39.001,34.423h-8.002c-0.552,0-0.999,0.447-0.999,0.999V100h10V35.422
+		C40,34.87,39.553,34.423,39.001,34.423z"/>
+	<linearGradient id="SVGID_8_" gradientUnits="userSpaceOnUse" x1="35" y1="98.9423" x2="35" y2="53.1961">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#F99B1C"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_8_);" d="M30,60.895v38.84h10v-38.84"/>
+	<linearGradient id="SVGID_9_" gradientUnits="userSpaceOnUse" x1="35" y1="99.4343" x2="35" y2="74.4359">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#FFDE17"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_9_);" d="M30,78.643v21.225h10V78.643"/>
+</g>
+<g>
+	<linearGradient id="SVGID_10_" gradientUnits="userSpaceOnUse" x1="50" y1="103.7019" x2="50" y2="32.0424">
+		<stop  offset="0" style="stop-color:#FFF33B"/>
+		<stop  offset="0" style="stop-color:#FFD53F"/>
+		<stop  offset="0" style="stop-color:#FBBC40"/>
+		<stop  offset="0" style="stop-color:#F7A840"/>
+		<stop  offset="0" style="stop-color:#F59B40"/>
+		<stop  offset="0" style="stop-color:#F3933F"/>
+		<stop  offset="0" style="stop-color:#F3903F"/>
+		<stop  offset="0.8423" style="stop-color:#ED683C"/>
+		<stop  offset="1" style="stop-color:#E93E3A"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_10_);" d="M54.001,41.827h-8.002c-0.552,0-0.999,0.447-0.999,0.999V100h10V42.826
+		C55,42.274,54.553,41.827,54.001,41.827z"/>
+	<linearGradient id="SVGID_11_" gradientUnits="userSpaceOnUse" x1="50" y1="98.9423" x2="50" y2="53.1961">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#F99B1C"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_11_);" d="M45,65.352v34.383h10V65.352"/>
+	<linearGradient id="SVGID_12_" gradientUnits="userSpaceOnUse" x1="50" y1="99.4343" x2="50" y2="74.4359">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#FFDE17"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_12_);" d="M45,81.079v18.789h10V81.079"/>
+</g>
+<g>
+	<linearGradient id="SVGID_13_" gradientUnits="userSpaceOnUse" x1="65" y1="103.8575" x2="65" y2="29.1875">
+		<stop  offset="0" style="stop-color:#FFF33B"/>
+		<stop  offset="0" style="stop-color:#FFD53F"/>
+		<stop  offset="0" style="stop-color:#FBBC40"/>
+		<stop  offset="0" style="stop-color:#F7A840"/>
+		<stop  offset="0" style="stop-color:#F59B40"/>
+		<stop  offset="0" style="stop-color:#F3933F"/>
+		<stop  offset="0" style="stop-color:#F3903F"/>
+		<stop  offset="0.8423" style="stop-color:#ED683C"/>
+		<stop  offset="1" style="stop-color:#E93E3A"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_13_);" d="M69.001,50.404h-8.002c-0.552,0-0.999,0.447-0.999,0.999V100h10V51.403
+		C70,50.851,69.553,50.404,69.001,50.404z"/>
+	<linearGradient id="SVGID_14_" gradientUnits="userSpaceOnUse" x1="65" y1="98.8979" x2="65" y2="51.2298">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#F99B1C"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_14_);" d="M60,70.531v29.193h10V70.531"/>
+	<linearGradient id="SVGID_15_" gradientUnits="userSpaceOnUse" x1="65" y1="99.4105" x2="65" y2="73.3619">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#FFDE17"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_15_);" d="M60,83.909v15.953h10V83.909"/>
+</g>
+<g>
+	<linearGradient id="SVGID_16_" gradientUnits="userSpaceOnUse" x1="80" y1="104.4108" x2="80" y2="19.0293">
+		<stop  offset="0" style="stop-color:#FFF33B"/>
+		<stop  offset="0" style="stop-color:#FFD53F"/>
+		<stop  offset="0" style="stop-color:#FBBC40"/>
+		<stop  offset="0" style="stop-color:#F7A840"/>
+		<stop  offset="0" style="stop-color:#F59B40"/>
+		<stop  offset="0" style="stop-color:#F3933F"/>
+		<stop  offset="0" style="stop-color:#F3903F"/>
+		<stop  offset="0.8423" style="stop-color:#ED683C"/>
+		<stop  offset="1" style="stop-color:#E93E3A"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_16_);" d="M84.001,40.769h-8.002c-0.552,0-0.999,0.447-0.999,0.999V100h10V41.768
+		C85,41.216,84.553,40.769,84.001,40.769z"/>
+	<linearGradient id="SVGID_17_" gradientUnits="userSpaceOnUse" x1="80" y1="98.9423" x2="80" y2="53.1961">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#F99B1C"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_17_);" d="M75,64.716v35.02h10v-35.02"/>
+	<linearGradient id="SVGID_18_" gradientUnits="userSpaceOnUse" x1="80" y1="99.4343" x2="80" y2="74.4359">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#FFDE17"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_18_);" d="M75,80.731v19.137h10V80.731"/>
+</g>
+<g>
+	<linearGradient id="SVGID_19_" gradientUnits="userSpaceOnUse" x1="95" y1="103.5838" x2="95" y2="34.2115">
+		<stop  offset="0" style="stop-color:#FFF33B"/>
+		<stop  offset="0" style="stop-color:#FFD53F"/>
+		<stop  offset="0" style="stop-color:#FBBC40"/>
+		<stop  offset="0" style="stop-color:#F7A840"/>
+		<stop  offset="0" style="stop-color:#F59B40"/>
+		<stop  offset="0" style="stop-color:#F3933F"/>
+		<stop  offset="0" style="stop-color:#F3903F"/>
+		<stop  offset="0.8423" style="stop-color:#ED683C"/>
+		<stop  offset="1" style="stop-color:#E93E3A"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_19_);" d="M99.001,21.157h-8.002c-0.552,0-0.999,0.447-0.999,0.999V100h10V22.156
+		C100,21.604,99.553,21.157,99.001,21.157z"/>
+	<linearGradient id="SVGID_20_" gradientUnits="userSpaceOnUse" x1="95" y1="98.9761" x2="95" y2="54.69">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#F99B1C"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_20_);" d="M90,52.898v46.846h10V52.898"/>
+	<linearGradient id="SVGID_21_" gradientUnits="userSpaceOnUse" x1="95" y1="99.4524" x2="95" y2="75.2518">
+		<stop  offset="0" style="stop-color:#FEBC11"/>
+		<stop  offset="1" style="stop-color:#FFDE17"/>
+	</linearGradient>
+	<path style="fill:url(#SVGID_21_);" d="M90,74.272v25.6h10v-25.6"/>
+</g>
+</svg>

+ 21 - 0
public/app/plugins/panel/graph2/module.tsx

@@ -0,0 +1,21 @@
+import React, { PureComponent } from 'react';
+import { PanelProps } from 'app/features/dashboard/dashgrid/DataPanel';
+
+export class ReactTestPanel extends PureComponent<PanelProps> {
+  constructor(props) {
+    super(props);
+  }
+
+  render() {
+    const { data } = this.props;
+    let value = 0;
+
+    if (data.length) {
+      value = data[0].value;
+    }
+
+    return <h2>Graph Panel! {value}</h2>;
+  }
+}
+
+export { ReactTestPanel as PanelComponent };

+ 17 - 0
public/app/plugins/panel/graph2/plugin.json

@@ -0,0 +1,17 @@
+{
+  "type": "panel",
+  "name": "React Graph",
+  "id": "graph2",
+
+  "info": {
+    "author": {
+      "name": "Grafana Project",
+      "url": "https://grafana.com"
+    },
+    "logos": {
+      "small": "img/icn-graph-panel.svg",
+      "large": "img/icn-graph-panel.svg"
+    }
+  }
+}
+

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

@@ -14,7 +14,7 @@ export class ReactTestPanel extends PureComponent<PanelProps> {
       value = data[0].value;
     }
 
-    return <h2>I am a react value: {value}</h2>;
+    return <h2>Text Panel {value}</h2>;
   }
 }
 

+ 20 - 0
public/app/types/plugins.ts

@@ -0,0 +1,20 @@
+export interface PluginExports {
+  PanelCtrl?;
+  PanelComponent?: any;
+  Datasource?: any;
+  QueryCtrl?: any;
+  ConfigCtrl?: any;
+  AnnotationsQueryCtrl?: any;
+}
+
+export interface PanelPlugin {
+  id: string;
+  name: string;
+  meta: any;
+  hideFromList: boolean;
+  module: string;
+  baseUrl: string;
+  info: any;
+  sort: number;
+  exports?: PluginExports;
+}

+ 9 - 0
public/sass/pages/_dashboard.scss

@@ -35,11 +35,20 @@ div.flot-text {
   height: 100%;
 }
 
+.panel-editor-container__panel {
+  height: 35%;
+}
+
+.panel-editor-container__editor {
+  height: 65%;
+}
+
 .panel-container {
   background-color: $panel-bg;
   border: $panel-border;
   position: relative;
   border-radius: 3px;
+  height: 100%;
 
   &.panel-transparent {
     background-color: transparent;