소스 검색

Changed how react panels store their options (#15468)

Changed how react panels store their options

* Added a ReactPanelPlugin as the interface that react panels export, this way react panels have clearer api, and gives us hooks to handle migrations and a way for panel to handle panel changes in the future
* Moved gauge value options into a sub oject and made editor more generic, will be moved out of gauge pane later and shared between singlestat, gauge, bargauge, honecomb
* Also remove nested options prop that was there due to bug
* Added missing Gauge props
* Fixed gauge issue that will require migration later and also value options editor did not handle null decimals or 0 decimals
* Fixed unit tests
* More fixes for react panels
Torkel Ödegaard 6 년 전
부모
커밋
abddb442a1

+ 1 - 1
packages/grafana-ui/src/components/Gauge/Gauge.tsx

@@ -9,7 +9,7 @@ import { Themeable } from '../../index';
 type TimeSeriesValue = string | number | null;
 
 export interface Props extends Themeable {
-  decimals: number;
+  decimals?: number | null;
   height: number;
   valueMappings: ValueMapping[];
   maxValue: number;

+ 20 - 1
packages/grafana-ui/src/types/panel.ts

@@ -1,3 +1,4 @@
+import { ComponentClass } from 'react';
 import { TimeSeries, LoadingState, TableData } from './data';
 import { TimeRange } from './time';
 
@@ -19,11 +20,29 @@ export interface PanelData {
   tableData?: TableData;
 }
 
-export interface PanelOptionsProps<T = any> {
+export interface PanelEditorProps<T = any> {
   options: T;
   onChange: (options: T) => void;
 }
 
+export class ReactPanelPlugin<TOptions = any> {
+  panel: ComponentClass<PanelProps<TOptions>>;
+  editor?: ComponentClass<PanelEditorProps<TOptions>>;
+  defaults?: TOptions;
+
+  constructor(panel: ComponentClass<PanelProps<TOptions>>) {
+    this.panel = panel;
+  }
+
+  setEditor(editor: ComponentClass<PanelEditorProps<TOptions>>) {
+    this.editor = editor;
+  }
+
+  setDefaults(defaults: TOptions) {
+    this.defaults = defaults;
+  }
+}
+
 export interface PanelSize {
   width: number;
   height: number;

+ 2 - 4
packages/grafana-ui/src/types/plugin.ts

@@ -1,5 +1,5 @@
 import { ComponentClass } from 'react';
-import { PanelProps, PanelOptionsProps } from './panel';
+import { ReactPanelPlugin } from './panel';
 import { DataQueryOptions, DataQuery, DataQueryResponse, QueryHint, QueryFixAction } from './datasource';
 
 export interface DataSourceApi<TQuery extends DataQuery = DataQuery> {
@@ -81,9 +81,7 @@ export interface PluginExports {
 
   // Panel plugin
   PanelCtrl?: any;
-  Panel?: ComponentClass<PanelProps>;
-  PanelOptions?: ComponentClass<PanelOptionsProps>;
-  PanelDefaults?: any;
+  reactPanel: ReactPanelPlugin;
 }
 
 export interface PluginMeta {

+ 0 - 1
public/app/core/constants.ts

@@ -14,4 +14,3 @@ export const DASHBOARD_TOP_PADDING = 20;
 
 export const PANEL_HEADER_HEIGHT = 27;
 export const PANEL_BORDER = 2;
-export const PANEL_OPTIONS_KEY_PREFIX = 'options-';

+ 5 - 5
public/app/features/dashboard/containers/__snapshots__/DashboardPage.test.tsx.snap

@@ -78,7 +78,7 @@ exports[`DashboardPage Dashboard init completed  Should render dashboard grid 1`
         ],
         "refresh": undefined,
         "revision": undefined,
-        "schemaVersion": 17,
+        "schemaVersion": 18,
         "snapshot": undefined,
         "style": "dark",
         "tags": Array [],
@@ -190,7 +190,7 @@ exports[`DashboardPage Dashboard init completed  Should render dashboard grid 1`
               ],
               "refresh": undefined,
               "revision": undefined,
-              "schemaVersion": 17,
+              "schemaVersion": 18,
               "snapshot": undefined,
               "style": "dark",
               "tags": Array [],
@@ -313,7 +313,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
         ],
         "refresh": undefined,
         "revision": undefined,
-        "schemaVersion": 17,
+        "schemaVersion": 18,
         "snapshot": undefined,
         "style": "dark",
         "tags": Array [],
@@ -423,7 +423,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
             ],
             "refresh": undefined,
             "revision": undefined,
-            "schemaVersion": 17,
+            "schemaVersion": 18,
             "snapshot": undefined,
             "style": "dark",
             "tags": Array [],
@@ -518,7 +518,7 @@ exports[`DashboardPage When dashboard has editview url state should render setti
               ],
               "refresh": undefined,
               "revision": undefined,
-              "schemaVersion": 17,
+              "schemaVersion": 18,
               "snapshot": undefined,
               "style": "dark",
               "tags": Array [],

+ 1 - 1
public/app/features/dashboard/dashgrid/DashboardPanel.tsx

@@ -173,7 +173,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
               onMouseLeave={this.onMouseLeave}
               style={styles}
             >
-              {plugin.exports.Panel && this.renderReactPanel()}
+              {plugin.exports.reactPanel && this.renderReactPanel()}
               {plugin.exports.PanelCtrl && this.renderAngularPanel()}
             </div>
           )}

+ 9 - 12
public/app/features/dashboard/dashgrid/DataPanel.tsx

@@ -162,7 +162,7 @@ export class DataPanel extends Component<Props, State> {
       }
 
       onError(message, err);
-      this.setState({ isFirstLoad: false });
+      this.setState({ isFirstLoad: false, loading: LoadingState.Error });
     }
   };
 
@@ -187,7 +187,8 @@ export class DataPanel extends Component<Props, State> {
     const { loading, isFirstLoad } = this.state;
     const panelData = this.getPanelData();
 
-    if (isFirstLoad && loading === LoadingState.Loading) {
+    // do not render component until we have first data
+    if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) {
       return this.renderLoadingState();
     }
 
@@ -201,21 +202,17 @@ export class DataPanel extends Component<Props, State> {
 
     return (
       <>
-        {this.renderLoadingState()}
+        {loading === LoadingState.Loading && this.renderLoadingState()}
         {this.props.children({ loading, panelData })}
       </>
     );
   }
 
   private renderLoadingState(): JSX.Element {
-    const { loading } = this.state;
-    if (loading === LoadingState.Loading) {
-      return (
-        <div className="panel-loading">
-          <i className="fa fa-spinner fa-spin" />
-        </div>
-      );
-    }
-    return null;
+    return (
+      <div className="panel-loading">
+        <i className="fa fa-spinner fa-spin" />
+      </div>
+    );
   }
 }

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

@@ -140,7 +140,7 @@ export class PanelChrome extends PureComponent<Props, State> {
   renderPanelPlugin(loading: LoadingState, panelData: PanelData, width: number, height: number): JSX.Element {
     const { panel, plugin } = this.props;
     const { timeRange, renderCounter } = this.state;
-    const PanelComponent = plugin.exports.Panel;
+    const PanelComponent = plugin.exports.reactPanel.panel;
 
     // This is only done to increase a counter that is used by backend
     // image rendering (phantomjs/headless chrome) to know when to capture image
@@ -154,7 +154,7 @@ export class PanelChrome extends PureComponent<Props, State> {
           loading={loading}
           panelData={panelData}
           timeRange={timeRange}
-          options={panel.getOptions(plugin.exports.PanelDefaults)}
+          options={panel.getOptions(plugin.exports.reactPanel.defaults)}
           width={width - 2 * variables.panelhorizontalpadding}
           height={height - PANEL_HEADER_HEIGHT - variables.panelverticalpadding}
           renderCounter={renderCounter}

+ 2 - 2
public/app/features/dashboard/dashgrid/PanelPluginNotFound.tsx

@@ -3,7 +3,7 @@ import _ from 'lodash';
 import React, { PureComponent } from 'react';
 
 // Types
-import { PanelProps } from '@grafana/ui';
+import { PanelProps, ReactPanelPlugin } from '@grafana/ui';
 import { PanelPlugin } from 'app/types';
 
 interface Props {
@@ -63,7 +63,7 @@ export function getPanelPluginNotFound(id: string): PanelPlugin {
     },
 
     exports: {
-      Panel: NotFound,
+      reactPanel: new ReactPanelPlugin(NotFound),
     },
   };
 }

+ 11 - 17
public/app/features/dashboard/panel_editor/VisualizationTab.tsx

@@ -50,33 +50,27 @@ export class VisualizationTab extends PureComponent<Props, State> {
     };
   }
 
-  getPanelDefaultOptions = () => {
+  getReactPanelOptions = () => {
     const { panel, plugin } = this.props;
-
-    if (plugin.exports.PanelDefaults) {
-      return panel.getOptions(plugin.exports.PanelDefaults.options);
-    }
-
-    return panel.getOptions(plugin.exports.PanelDefaults);
+    return panel.getOptions(plugin.exports.reactPanel.defaults);
   };
 
   renderPanelOptions() {
     const { plugin, angularPanel } = this.props;
-    const { PanelOptions } = plugin.exports;
 
     if (angularPanel) {
       return <div ref={element => (this.element = element)} />;
     }
 
-    return (
-      <>
-        {PanelOptions ? (
-          <PanelOptions options={this.getPanelDefaultOptions()} onChange={this.onPanelOptionsChanged} />
-        ) : (
-          <p>Visualization has no options</p>
-        )}
-      </>
-    );
+    if (plugin.exports.reactPanel) {
+      const PanelEditor = plugin.exports.reactPanel.editor;
+
+      if (PanelEditor) {
+        return <PanelEditor options={this.getReactPanelOptions()} onChange={this.onPanelOptionsChanged} />;
+      }
+    }
+
+    return <p>Visualization has no options</p>;
   }
 
   componentDidMount() {

+ 1 - 1
public/app/features/dashboard/state/DashboardMigrator.test.ts

@@ -127,7 +127,7 @@ describe('DashboardModel', () => {
     });
 
     it('dashboard schema version should be set to latest', () => {
-      expect(model.schemaVersion).toBe(17);
+      expect(model.schemaVersion).toBe(18);
     });
 
     it('graph thresholds should be migrated', () => {

+ 25 - 1
public/app/features/dashboard/state/DashboardMigrator.ts

@@ -22,7 +22,7 @@ export class DashboardMigrator {
     let i, j, k, n;
     const oldVersion = this.dashboard.schemaVersion;
     const panelUpgrades = [];
-    this.dashboard.schemaVersion = 17;
+    this.dashboard.schemaVersion = 18;
 
     if (oldVersion === this.dashboard.schemaVersion) {
       return;
@@ -387,6 +387,30 @@ export class DashboardMigrator {
       });
     }
 
+    if (oldVersion < 18) {
+      // migrate change to gauge options
+      panelUpgrades.push(panel => {
+        if (panel['options-gauge']) {
+          panel.options = panel['options-gauge'];
+          panel.options.valueOptions = {
+            unit: panel.options.unit,
+            stat: panel.options.stat,
+            decimals: panel.options.decimals,
+            prefix: panel.options.prefix,
+            suffix: panel.options.suffix,
+          };
+          // this options prop was due to a bug
+          delete panel.options.options;
+          delete panel.options.unit;
+          delete panel.options.stat;
+          delete panel.options.decimals;
+          delete panel.options.prefix;
+          delete panel.options.suffix;
+          delete panel['options-gauge'];
+        }
+      });
+    }
+
     if (panelUpgrades.length === 0) {
       return;
     }

+ 14 - 0
public/app/features/dashboard/state/PanelModel.test.ts

@@ -55,5 +55,19 @@ describe('PanelModel', () => {
         expect(model.alert).toBe(undefined);
       });
     });
+
+    describe('get panel options', () => {
+      it('should apply defaults', () => {
+        model.options = { existingProp: 10 };
+        const options = model.getOptions({
+          defaultProp: true,
+          existingProp: 0,
+        });
+
+        expect(options.defaultProp).toBe(true);
+        expect(options.existingProp).toBe(10);
+        expect(model.options).toBe(options);
+      });
+    });
   });
 });

+ 10 - 17
public/app/features/dashboard/state/PanelModel.ts

@@ -3,7 +3,6 @@ import _ from 'lodash';
 
 // Types
 import { Emitter } from 'app/core/utils/emitter';
-import { PANEL_OPTIONS_KEY_PREFIX } from 'app/core/constants';
 import { DataQuery, TimeSeries } from '@grafana/ui';
 import { TableData } from '@grafana/ui/src';
 
@@ -92,6 +91,7 @@ export class PanelModel {
   timeFrom?: any;
   timeShift?: any;
   hideTimeOverride?: any;
+  options: object;
 
   maxDataPoints?: number;
   interval?: string;
@@ -105,8 +105,6 @@ export class PanelModel {
   hasRefreshed: boolean;
   events: Emitter;
   cacheTimeout?: any;
-
-  // cache props between plugins
   cachedPluginOptions?: any;
 
   constructor(model) {
@@ -134,20 +132,14 @@ export class PanelModel {
   }
 
   getOptions(panelDefaults) {
-    return _.defaultsDeep(this[this.getOptionsKey()] || {}, panelDefaults);
+    return _.defaultsDeep(this.options || {}, panelDefaults);
   }
 
   updateOptions(options: object) {
-    const update: any = {};
-    update[this.getOptionsKey()] = options;
-    Object.assign(this, update);
+    this.options = options;
     this.render();
   }
 
-  private getOptionsKey() {
-    return PANEL_OPTIONS_KEY_PREFIX + this.type;
-  }
-
   getSaveModel() {
     const model: any = {};
     for (const property in this) {
@@ -240,14 +232,15 @@ export class PanelModel {
     // for angular panels only we need to remove all events and let angular panels do some cleanup
     if (fromAngularPanel) {
       this.destroy();
+    }
 
-      for (const key of _.keys(this)) {
-        if (mustKeepProps[key]) {
-          continue;
-        }
-
-        delete this[key];
+    // remove panel type specific  options
+    for (const key of _.keys(this)) {
+      if (mustKeepProps[key]) {
+        continue;
       }
+
+      delete this[key];
     }
 
     this.restorePanelOptions(pluginId);

+ 7 - 2
public/app/plugins/panel/gauge/GaugeOptionsEditor.tsx → public/app/plugins/panel/gauge/GaugeOptionsBox.tsx

@@ -1,9 +1,14 @@
+// Libraries
 import React, { PureComponent } from 'react';
-import { FormField, PanelOptionsProps, PanelOptionsGroup, Switch } from '@grafana/ui';
 
+// Components
+import { Switch, PanelOptionsGroup } from '@grafana/ui';
+
+// Types
+import { FormField, PanelEditorProps } from '@grafana/ui';
 import { GaugeOptions } from './types';
 
-export default class GaugeOptionsEditor extends PureComponent<PanelOptionsProps<GaugeOptions>> {
+export class GaugeOptionsBox extends PureComponent<PanelEditorProps<GaugeOptions>> {
   onToggleThresholdLabels = () =>
     this.props.onChange({ ...this.props.options, showThresholdLabels: !this.props.options.showThresholdLabels });
 

+ 12 - 4
public/app/plugins/panel/gauge/GaugePanel.tsx

@@ -16,9 +16,10 @@ interface Props extends PanelProps<GaugeOptions> {}
 export class GaugePanel extends PureComponent<Props> {
   render() {
     const { panelData, width, height, onInterpolate, options } = this.props;
+    const { valueOptions } = options;
 
-    const prefix = onInterpolate(options.prefix);
-    const suffix = onInterpolate(options.suffix);
+    const prefix = onInterpolate(valueOptions.prefix);
+    const suffix = onInterpolate(valueOptions.suffix);
     let value: TimeSeriesValue;
 
     if (panelData.timeSeries) {
@@ -28,7 +29,7 @@ export class GaugePanel extends PureComponent<Props> {
       });
 
       if (vmSeries[0]) {
-        value = vmSeries[0].stats[options.stat];
+        value = vmSeries[0].stats[valueOptions.stat];
       } else {
         value = null;
       }
@@ -41,11 +42,18 @@ export class GaugePanel extends PureComponent<Props> {
         {theme => (
           <Gauge
             value={value}
-            {...this.props.options}
             width={width}
             height={height}
             prefix={prefix}
             suffix={suffix}
+            unit={valueOptions.unit}
+            decimals={valueOptions.decimals}
+            thresholds={options.thresholds}
+            valueMappings={options.valueMappings}
+            showThresholdLabels={options.showThresholdLabels}
+            showThresholdMarkers={options.showThresholdMarkers}
+            minValue={options.minValue}
+            maxValue={options.maxValue}
             theme={theme}
           />
         )}

+ 13 - 25
public/app/plugins/panel/gauge/GaugePanelOptions.tsx → public/app/plugins/panel/gauge/GaugePanelEditor.tsx

@@ -1,6 +1,6 @@
 import React, { PureComponent } from 'react';
 import {
-  PanelOptionsProps,
+  PanelEditorProps,
   ThresholdsEditor,
   Threshold,
   PanelOptionsGrid,
@@ -8,29 +8,11 @@ import {
   ValueMapping,
 } from '@grafana/ui';
 
-import ValueOptions from 'app/plugins/panel/gauge/ValueOptions';
-import GaugeOptionsEditor from './GaugeOptionsEditor';
-import { GaugeOptions } from './types';
-
-export const defaultProps = {
-  options: {
-    minValue: 0,
-    maxValue: 100,
-    prefix: '',
-    showThresholdMarkers: true,
-    showThresholdLabels: false,
-    suffix: '',
-    decimals: 0,
-    stat: 'avg',
-    unit: 'none',
-    valueMappings: [],
-    thresholds: [],
-  },
-};
-
-export default class GaugePanelOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> {
-  static defaultProps = defaultProps;
+import { SingleStatValueEditor } from 'app/plugins/panel/gauge/SingleStatValueEditor';
+import { GaugeOptionsBox } from './GaugeOptionsBox';
+import { GaugeOptions, SingleStatValueOptions } from './types';
 
+export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOptions>> {
   onThresholdsChanged = (thresholds: Threshold[]) =>
     this.props.onChange({
       ...this.props.options,
@@ -43,14 +25,20 @@ export default class GaugePanelOptions extends PureComponent<PanelOptionsProps<G
       valueMappings,
     });
 
+  onValueOptionsChanged = (valueOptions: SingleStatValueOptions) =>
+    this.props.onChange({
+      ...this.props.options,
+      valueOptions,
+    });
+
   render() {
     const { onChange, options } = this.props;
 
     return (
       <>
         <PanelOptionsGrid>
-          <ValueOptions onChange={onChange} options={options} />
-          <GaugeOptionsEditor onChange={onChange} options={options} />
+          <SingleStatValueEditor onChange={this.onValueOptionsChanged} options={options.valueOptions} />
+          <GaugeOptionsBox onChange={onChange} options={options} />
           <ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={options.thresholds} />
         </PanelOptionsGrid>
 

+ 28 - 7
public/app/plugins/panel/gauge/ValueOptions.tsx → public/app/plugins/panel/gauge/SingleStatValueEditor.tsx

@@ -1,7 +1,12 @@
+// Libraries
 import React, { PureComponent } from 'react';
-import { FormField, FormLabel, PanelOptionsProps, PanelOptionsGroup, Select } from '@grafana/ui';
+
+// Components
 import UnitPicker from 'app/core/components/Select/UnitPicker';
-import { GaugeOptions } from './types';
+import { FormField, FormLabel, PanelOptionsGroup, Select } from '@grafana/ui';
+
+// Types
+import { SingleStatValueOptions } from './types';
 
 const statOptions = [
   { value: 'min', label: 'Min' },
@@ -19,24 +24,40 @@ const statOptions = [
 
 const labelWidth = 6;
 
-export default class ValueOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> {
-  onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value });
+export interface Props {
+  options: SingleStatValueOptions;
+  onChange: (valueOptions: SingleStatValueOptions) => void;
+}
 
+export class SingleStatValueEditor extends PureComponent<Props> {
+  onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value });
   onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value });
 
   onDecimalChange = event => {
     if (!isNaN(event.target.value)) {
-      this.props.onChange({ ...this.props.options, decimals: event.target.value });
+      this.props.onChange({
+        ...this.props.options,
+        decimals: parseInt(event.target.value, 10),
+      });
+    } else {
+      this.props.onChange({
+        ...this.props.options,
+        decimals: null,
+      });
     }
   };
 
   onPrefixChange = event => this.props.onChange({ ...this.props.options, prefix: event.target.value });
-
   onSuffixChange = event => this.props.onChange({ ...this.props.options, suffix: event.target.value });
 
   render() {
     const { stat, unit, decimals, prefix, suffix } = this.props.options;
 
+    let decimalsString = '';
+    if (Number.isFinite(decimals)) {
+      decimalsString = decimals.toString();
+    }
+
     return (
       <PanelOptionsGroup title="Value">
         <div className="gf-form">
@@ -57,7 +78,7 @@ export default class ValueOptions extends PureComponent<PanelOptionsProps<GaugeO
           labelWidth={labelWidth}
           placeholder="auto"
           onChange={this.onDecimalChange}
-          value={decimals || ''}
+          value={decimalsString}
           type="number"
         />
         <FormField label="Prefix" labelWidth={labelWidth} onChange={this.onPrefixChange} value={prefix || ''} />

+ 8 - 2
public/app/plugins/panel/gauge/module.tsx

@@ -1,4 +1,10 @@
-import GaugePanelOptions, { defaultProps } from './GaugePanelOptions';
+import { ReactPanelPlugin } from '@grafana/ui';
+
+import { GaugePanelEditor } from './GaugePanelEditor';
 import { GaugePanel } from './GaugePanel';
+import { GaugeOptions, defaults } from './types';
+
+export const reactPanel = new ReactPanelPlugin<GaugeOptions>(GaugePanel);
 
-export { GaugePanel as Panel, GaugePanelOptions as PanelOptions, defaultProps as PanelDefaults };
+reactPanel.setEditor(GaugePanelEditor);
+reactPanel.setDefaults(defaults);

+ 24 - 4
public/app/plugins/panel/gauge/types.ts

@@ -1,15 +1,35 @@
 import { Threshold, ValueMapping } from '@grafana/ui';
 
 export interface GaugeOptions {
-  decimals: number;
   valueMappings: ValueMapping[];
   maxValue: number;
   minValue: number;
-  prefix: string;
   showThresholdLabels: boolean;
   showThresholdMarkers: boolean;
-  stat: string;
-  suffix: string;
   thresholds: Threshold[];
+  valueOptions: SingleStatValueOptions;
+}
+
+export interface SingleStatValueOptions {
   unit: string;
+  suffix: string;
+  stat: string;
+  prefix: string;
+  decimals?: number | null;
 }
+
+export const defaults: GaugeOptions = {
+  minValue: 0,
+  maxValue: 100,
+  showThresholdMarkers: true,
+  showThresholdLabels: false,
+  valueOptions: {
+    prefix: '',
+    suffix: '',
+    decimals: null,
+    stat: 'avg',
+    unit: 'none',
+  },
+  valueMappings: [],
+  thresholds: [],
+};

+ 2 - 2
public/app/plugins/panel/graph2/GraphPanelOptions.tsx → public/app/plugins/panel/graph2/GraphPanelEditor.tsx

@@ -3,10 +3,10 @@ import _ from 'lodash';
 import React, { PureComponent } from 'react';
 
 // Types
-import { PanelOptionsProps, Switch } from '@grafana/ui';
+import { PanelEditorProps, Switch } from '@grafana/ui';
 import { Options } from './types';
 
-export class GraphPanelOptions extends PureComponent<PanelOptionsProps<Options>> {
+export class GraphPanelEditor extends PureComponent<PanelEditorProps<Options>> {
   onToggleLines = () => {
     this.props.onChange({ ...this.props.options, showLines: !this.props.options.showLines });
   };

+ 2 - 2
public/app/plugins/panel/graph2/module.tsx

@@ -1,4 +1,4 @@
 import { GraphPanel } from './GraphPanel';
-import { GraphPanelOptions } from './GraphPanelOptions';
+import { GraphPanelEditor } from './GraphPanelEditor';
 
-export { GraphPanel as Panel, GraphPanelOptions as PanelOptions };
+export { GraphPanel as Panel, GraphPanelEditor as PanelOptions };

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

@@ -1,5 +1,5 @@
 import React, { PureComponent } from 'react';
-import { PanelProps } from '@grafana/ui';
+import { PanelProps, ReactPanelPlugin } from '@grafana/ui';
 
 export class Text2 extends PureComponent<PanelProps> {
   constructor(props: PanelProps) {
@@ -11,4 +11,4 @@ export class Text2 extends PureComponent<PanelProps> {
   }
 }
 
-export { Text2 as Panel };
+export const reactPanel = new ReactPanelPlugin(Text2);