瀏覽代碼

wip: react panel options architecture

Torkel Ödegaard 7 年之前
父節點
當前提交
35e62bbbe0

+ 23 - 2
public/app/features/dashboard/dashgrid/PanelChrome.tsx

@@ -21,6 +21,7 @@ export interface Props {
 
 export interface State {
   refreshCounter: number;
+  renderCounter: number;
   timeRange?: TimeRange;
 }
 
@@ -30,11 +31,13 @@ export class PanelChrome extends PureComponent<Props, State> {
 
     this.state = {
       refreshCounter: 0,
+      renderCounter: 0,
     };
   }
 
   componentDidMount() {
     this.props.panel.events.on('refresh', this.onRefresh);
+    this.props.panel.events.on('render', this.onRender);
     this.props.dashboard.panelInitialized(this.props.panel);
   }
 
@@ -52,6 +55,13 @@ export class PanelChrome extends PureComponent<Props, State> {
     });
   };
 
+  onRender = () => {
+    console.log('onRender');
+    this.setState({
+      renderCounter: this.state.renderCounter + 1,
+    });
+  };
+
   get isVisible() {
     return !this.props.dashboard.otherPanelInFullscreen(this.props.panel);
   }
@@ -59,9 +69,11 @@ export class PanelChrome extends PureComponent<Props, State> {
   render() {
     const { panel, dashboard } = this.props;
     const { datasource, targets } = panel;
-    const { refreshCounter, timeRange } = this.state;
+    const { timeRange, renderCounter, refreshCounter } = this.state;
     const PanelComponent = this.props.component;
 
+    console.log('Panel chrome render');
+
     return (
       <div className="panel-container">
         <PanelHeader panel={panel} dashboard={dashboard} />
@@ -74,7 +86,16 @@ export class PanelChrome extends PureComponent<Props, State> {
             refreshCounter={refreshCounter}
           >
             {({ loading, timeSeries }) => {
-              return <PanelComponent loading={loading} timeSeries={timeSeries} timeRange={timeRange} />;
+              console.log('panelcrome inner render');
+              return (
+                <PanelComponent
+                  loading={loading}
+                  timeSeries={timeSeries}
+                  timeRange={timeRange}
+                  options={panel.getOptions()}
+                  renderCounter={renderCounter}
+                />
+              );
             }}
           </DataPanel>
         </div>

+ 18 - 10
public/app/features/dashboard/dashgrid/PanelEditor.tsx

@@ -1,13 +1,16 @@
-import React from 'react';
+import React, { PureComponent } from 'react';
 import classNames from 'classnames';
-import { PanelModel } from '../panel_model';
-import { DashboardModel } from '../dashboard_model';
-import { store } from 'app/store/configureStore';
+
 import { QueriesTab } from './QueriesTab';
-import { PanelPlugin, PluginExports } from 'app/types/plugins';
 import { VizTypePicker } from './VizTypePicker';
+
+import { store } from 'app/store/configureStore';
 import { updateLocation } from 'app/core/actions';
 
+import { PanelModel } from '../panel_model';
+import { DashboardModel } from '../dashboard_model';
+import { PanelPlugin, PluginExports } from 'app/types/plugins';
+
 interface PanelEditorProps {
   panel: PanelModel;
   dashboard: DashboardModel;
@@ -22,7 +25,7 @@ interface PanelEditorTab {
   icon: string;
 }
 
-export class PanelEditor extends React.Component<PanelEditorProps, any> {
+export class PanelEditor extends PureComponent<PanelEditorProps> {
   tabs: PanelEditorTab[];
 
   constructor(props) {
@@ -39,16 +42,20 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> {
   }
 
   renderPanelOptions() {
-    const { pluginExports } = this.props;
+    const { pluginExports, panel } = this.props;
 
-    if (pluginExports.PanelOptions) {
-      const PanelOptions = pluginExports.PanelOptions;
-      return <PanelOptions />;
+    if (pluginExports.PanelOptionsComponent) {
+      const OptionsComponent = pluginExports.PanelOptionsComponent;
+      return <OptionsComponent options={panel.getOptions()} onChange={this.onPanelOptionsChanged} />;
     } else {
       return <p>Visualization has no options</p>;
     }
   }
 
+  onPanelOptionsChanged = (options: any) => {
+    this.props.panel.updateOptions(options);
+  };
+
   renderVizTab() {
     return (
       <div className="viz-editor">
@@ -70,6 +77,7 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> {
         partial: true,
       })
     );
+    this.forceUpdate();
   };
 
   render() {

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

@@ -60,6 +60,21 @@ export class PanelModel {
     _.defaultsDeep(this, _.cloneDeep(defaults));
   }
 
+  getOptions() {
+    return this[this.getOptionsKey()] || {};
+  }
+
+  updateOptions(options: object) {
+    const update: any = {};
+    update[this.getOptionsKey()] = options;
+    Object.assign(this, update);
+    this.render();
+  }
+
+  private getOptionsKey() {
+    return this.type + 'Options';
+  }
+
   getSaveModel() {
     const model: any = {};
     for (const property in this) {
@@ -121,10 +136,6 @@ export class PanelModel {
     this.events.emit('panel-initialized');
   }
 
-  initEditMode() {
-    this.events.emit('panel-init-edit-mode');
-  }
-
   changeType(pluginId: string) {
     this.type = pluginId;
 

+ 1 - 1
public/app/features/dashboard/settings/settings.ts

@@ -32,9 +32,9 @@ export class SettingsCtrl {
 
     this.$scope.$on('$destroy', () => {
       this.dashboard.updateSubmenuVisibility();
-      this.dashboard.startRefresh();
       setTimeout(() => {
         this.$rootScope.appEvent('dash-scroll', { restore: true });
+        this.dashboard.startRefresh();
       });
     });
 

+ 24 - 11
public/app/plugins/panel/graph2/module.tsx

@@ -1,13 +1,10 @@
-// Libraries
 import _ from 'lodash';
 import React, { PureComponent } from 'react';
 
-// Components
 import Graph from 'app/viz/Graph';
-import { getTimeSeriesVMs } from 'app/viz/state/timeSeries';
 import { Switch } from 'app/core/components/Switch/Switch';
 
-// Types
+import { getTimeSeriesVMs } from 'app/viz/state/timeSeries';
 import { PanelProps, NullValueMode } from 'app/types';
 
 interface Options {
@@ -18,9 +15,7 @@ interface Options {
   onChange: (options: Options) => void;
 }
 
-interface Props extends PanelProps {
-  options: Options;
-}
+interface Props extends PanelProps<Options> {}
 
 export class Graph2 extends PureComponent<Props> {
   constructor(props) {
@@ -29,17 +24,26 @@ export class Graph2 extends PureComponent<Props> {
 
   render() {
     const { timeSeries, timeRange } = this.props;
+    const { showLines, showBars, showPoints } = this.props.options;
 
     const vmSeries = getTimeSeriesVMs({
       timeSeries: timeSeries,
       nullValueMode: NullValueMode.Ignore,
     });
 
-    return <Graph timeSeries={vmSeries} timeRange={timeRange} />;
+    return (
+      <Graph
+        timeSeries={vmSeries}
+        timeRange={timeRange}
+        showLines={showLines}
+        showPoints={showPoints}
+        showBars={showBars}
+      />
+    );
   }
 }
 
-export class TextOptions extends PureComponent<Options> {
+export class GraphOptions extends PureComponent<Options> {
   onToggleLines = () => {
     const options = this.props as Options;
 
@@ -49,6 +53,15 @@ export class TextOptions extends PureComponent<Options> {
     });
   };
 
+  onTogglePoints = () => {
+    const options = this.props as Options;
+
+    this.props.onChange({
+      ...options,
+      showPoints: !this.props.showPoints,
+    });
+  };
+
   render() {
     const { showBars, showPoints, showLines } = this.props;
 
@@ -58,11 +71,11 @@ export class TextOptions extends PureComponent<Options> {
           <h5 className="page-heading">Draw Modes</h5>
           <Switch label="Lines" labelClass="width-5" checked={showLines} onChange={this.onToggleLines} />
           <Switch label="Bars" labelClass="width-5" checked={showBars} onChange={this.onToggleLines} />
-          <Switch label="Points" labelClass="width-5" checked={showPoints} onChange={this.onToggleLines} />
+          <Switch label="Points" labelClass="width-5" checked={showPoints} onChange={this.onTogglePoints} />
         </div>
       </div>
     );
   }
 }
 
-export { Graph2 as PanelComponent, TextOptions as PanelOptions };
+export { Graph2 as PanelComponent, GraphOptions as PanelOptionsComponent };

+ 8 - 1
public/app/types/panel.ts

@@ -1,7 +1,14 @@
 import { LoadingState, TimeSeries, TimeRange } from './series';
 
-export interface PanelProps {
+export interface PanelProps<T = any> {
   timeSeries: TimeSeries[];
   timeRange: TimeRange;
   loading: LoadingState;
+  options: T;
+  renderCounter: number;
+}
+
+export interface PanelOptionProps<T = any> {
+  options: T;
+  onChange: (options: T) => void;
 }

+ 8 - 3
public/app/types/plugins.ts

@@ -1,13 +1,18 @@
+import { ComponentClass } from 'react';
+import { PanelProps, PanelOptionProps } from './panel';
+
 export interface PluginExports {
-  PanelCtrl?;
-  PanelComponent?: any;
   Datasource?: any;
   QueryCtrl?: any;
   ConfigCtrl?: any;
   AnnotationsQueryCtrl?: any;
-  PanelOptions?: any;
   ExploreQueryField?: any;
   ExploreStartPage?: any;
+
+  // Panel plugin
+  PanelCtrl?;
+  PanelComponent?: ComponentClass<PanelProps>;
+  PanelOptionsComponent: ComponentClass<PanelOptionProps>;
 }
 
 export interface PanelPlugin {

+ 51 - 33
public/app/viz/Graph.tsx

@@ -34,37 +34,22 @@ function time_format(ticks, min, max) {
   return '%H:%M';
 }
 
-const FLOT_OPTIONS = {
-  legend: {
-    show: false,
-  },
-  series: {
-    lines: {
-      linewidth: 1,
-      zero: false,
-    },
-    shadowSize: 0,
-  },
-  grid: {
-    minBorderMargin: 0,
-    markings: [],
-    backgroundColor: null,
-    borderWidth: 0,
-    // hoverable: true,
-    clickable: true,
-    color: '#a1a1a1',
-    margin: { left: 0, right: 0 },
-    labelMarginX: 0,
-  },
-};
-
 interface GraphProps {
   timeSeries: TimeSeriesVMs;
   timeRange: TimeRange;
+  showLines?: boolean;
+  showPoints?: boolean;
+  showBars?: boolean;
   size?: { width: number; height: number };
 }
 
 export class Graph extends PureComponent<GraphProps> {
+  static defaultProps = {
+    showLines: true,
+    showPoints: false,
+    showBars: false,
+  };
+
   element: any;
 
   componentDidUpdate(prevProps: GraphProps) {
@@ -82,7 +67,7 @@ export class Graph extends PureComponent<GraphProps> {
   }
 
   draw() {
-    const { size, timeSeries, timeRange } = this.props;
+    const { size, timeSeries, timeRange, showLines, showBars, showPoints } = this.props;
 
     if (!size) {
       return;
@@ -92,7 +77,31 @@ export class Graph extends PureComponent<GraphProps> {
     const min = timeRange.from.valueOf();
     const max = timeRange.to.valueOf();
 
-    const dynamicOptions = {
+    const flotOptions = {
+      legend: {
+        show: false,
+      },
+      series: {
+        lines: {
+          show: showLines,
+          linewidth: 1,
+          zero: false,
+        },
+        points: {
+          show: showPoints,
+          fill: 1,
+          fillColor: false,
+          radius: 2,
+        },
+        bars: {
+          show: showBars,
+          fill: 1,
+          barWidth: 1,
+          zero: false,
+          lineWidth: 0,
+        },
+        shadowSize: 0,
+      },
       xaxis: {
         mode: 'time',
         min: min,
@@ -101,15 +110,24 @@ export class Graph extends PureComponent<GraphProps> {
         ticks: ticks,
         timeformat: time_format(ticks, min, max),
       },
+      grid: {
+        minBorderMargin: 0,
+        markings: [],
+        backgroundColor: null,
+        borderWidth: 0,
+        // hoverable: true,
+        clickable: true,
+        color: '#a1a1a1',
+        margin: { left: 0, right: 0 },
+        labelMarginX: 0,
+      },
     };
 
-    const options = {
-      ...FLOT_OPTIONS,
-      ...dynamicOptions,
-    };
-
-    console.log('plot', timeSeries, options);
-    $.plot(this.element, timeSeries, options);
+    try {
+      $.plot(this.element, timeSeries, flotOptions);
+    } catch (err) {
+      console.log('Graph rendering error', err, flotOptions, timeSeries);
+    }
   }
 
   render() {