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

This commit is contained in:
Torkel Ödegaard 2019-02-16 13:54:15 +01:00
parent 9035ce4d18
commit 074073787d
14 changed files with 87 additions and 63 deletions

View File

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

View File

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

View File

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

View File

@ -139,7 +139,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
@ -153,7 +153,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}

View File

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

View File

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

View File

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

View File

@ -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 { ValueOptions } from 'app/plugins/panel/gauge/ValueOptions';
import { GaugeOptionsBox } from './GaugeOptionsBox';
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;
export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOptions>> {
onThresholdsChanged = (thresholds: Threshold[]) =>
this.props.onChange({
...this.props.options,
@ -50,7 +32,7 @@ export default class GaugePanelOptions extends PureComponent<PanelOptionsProps<G
<>
<PanelOptionsGrid>
<ValueOptions onChange={onChange} options={options} />
<GaugeOptionsEditor onChange={onChange} options={options} />
<GaugeOptionsBox onChange={onChange} options={options} />
<ThresholdsEditor onChange={this.onThresholdsChanged} thresholds={options.thresholds} />
</PanelOptionsGrid>

View File

@ -1,7 +1,13 @@
// 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 { FormField, FormLabel, PanelOptionsGroup, Select } from '@grafana/ui';
// Types
import { GaugeOptions } from './types';
import { PanelEditorProps } from '@grafana/ui';
const statOptions = [
{ value: 'min', label: 'Min' },
@ -19,7 +25,7 @@ const statOptions = [
const labelWidth = 6;
export default class ValueOptions extends PureComponent<PanelOptionsProps<GaugeOptions>> {
export class ValueOptions extends PureComponent<PanelEditorProps<GaugeOptions>> {
onUnitChange = unit => this.props.onChange({ ...this.props.options, unit: unit.value });
onStatChange = stat => this.props.onChange({ ...this.props.options, stat: stat.value });

View File

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

View File

@ -13,3 +13,17 @@ export interface GaugeOptions {
thresholds: Threshold[];
unit: string;
}
export const defaults: GaugeOptions = {
minValue: 0,
maxValue: 100,
prefix: '',
showThresholdMarkers: true,
showThresholdLabels: false,
suffix: '',
decimals: 0,
stat: 'avg',
unit: 'none',
valueMappings: [],
thresholds: [],
};

View File

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

View File

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

View File

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