diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 82b366d8126..953dfd62368 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/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 { 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 { }); }; + 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 { 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 (
@@ -74,7 +86,16 @@ export class PanelChrome extends PureComponent { refreshCounter={refreshCounter} > {({ loading, timeSeries }) => { - return ; + console.log('panelcrome inner render'); + return ( + + ); }}
diff --git a/public/app/features/dashboard/dashgrid/PanelEditor.tsx b/public/app/features/dashboard/dashgrid/PanelEditor.tsx index 26ac8b7d2c1..371ed22fff7 100644 --- a/public/app/features/dashboard/dashgrid/PanelEditor.tsx +++ b/public/app/features/dashboard/dashgrid/PanelEditor.tsx @@ -1,12 +1,15 @@ -import React from 'react'; +import React, { PureComponent } from 'react'; import classNames from 'classnames'; + +import { QueriesTab } from './QueriesTab'; +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 { store } from 'app/store/configureStore'; -import { QueriesTab } from './QueriesTab'; import { PanelPlugin, PluginExports } from 'app/types/plugins'; -import { VizTypePicker } from './VizTypePicker'; -import { updateLocation } from 'app/core/actions'; interface PanelEditorProps { panel: PanelModel; @@ -22,7 +25,7 @@ interface PanelEditorTab { icon: string; } -export class PanelEditor extends React.Component { +export class PanelEditor extends PureComponent { tabs: PanelEditorTab[]; constructor(props) { @@ -39,16 +42,21 @@ export class PanelEditor extends React.Component { } renderPanelOptions() { - const { pluginExports } = this.props; + const { pluginExports, panel } = this.props; - if (pluginExports.PanelOptions) { - const PanelOptions = pluginExports.PanelOptions; - return ; + if (pluginExports.PanelOptionsComponent) { + const OptionsComponent = pluginExports.PanelOptionsComponent; + return ; } else { return

Visualization has no options

; } } + onPanelOptionsChanged = (options: any) => { + this.props.panel.updateOptions(options); + this.forceUpdate(); + }; + renderVizTab() { return (
@@ -70,6 +78,7 @@ export class PanelEditor extends React.Component { partial: true, }) ); + this.forceUpdate(); }; render() { diff --git a/public/app/features/dashboard/panel_model.ts b/public/app/features/dashboard/panel_model.ts index d82368d8dd7..ed032a118fe 100644 --- a/public/app/features/dashboard/panel_model.ts +++ b/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; diff --git a/public/app/features/dashboard/settings/settings.ts b/public/app/features/dashboard/settings/settings.ts index b6a70ee4b98..1e8d96a54cb 100755 --- a/public/app/features/dashboard/settings/settings.ts +++ b/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(); }); }); diff --git a/public/app/plugins/panel/graph2/module.tsx b/public/app/plugins/panel/graph2/module.tsx index 4011458bea9..b132d3374f1 100644 --- a/public/app/plugins/panel/graph2/module.tsx +++ b/public/app/plugins/panel/graph2/module.tsx @@ -1,22 +1,21 @@ -// 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 { PanelProps, NullValueMode } from 'app/types'; +import { getTimeSeriesVMs } from 'app/viz/state/timeSeries'; +import { PanelProps, PanelOptionsProps, NullValueMode } from 'app/types'; interface Options { showBars: boolean; + showLines: boolean; + showPoints: boolean; + + onChange: (options: Options) => void; } -interface Props extends PanelProps { - options: Options; -} +interface Props extends PanelProps {} export class Graph2 extends PureComponent { constructor(props) { @@ -25,27 +24,52 @@ export class Graph2 extends PureComponent { render() { const { timeSeries, timeRange } = this.props; + const { showLines, showBars, showPoints } = this.props.options; const vmSeries = getTimeSeriesVMs({ timeSeries: timeSeries, nullValueMode: NullValueMode.Ignore, }); - return ; + return ( + + ); } } -export class TextOptions extends PureComponent { - onChange = () => {}; +export class GraphOptions extends PureComponent> { + onToggleLines = () => { + this.props.onChange({ ...this.props.options, showLines: !this.props.options.showLines }); + }; + + onToggleBars = () => { + this.props.onChange({ ...this.props.options, showBars: !this.props.options.showBars }); + }; + + onTogglePoints = () => { + this.props.onChange({ ...this.props.options, showPoints: !this.props.options.showPoints }); + }; render() { + const { showBars, showPoints, showLines } = this.props.options; + return ( -
-
Draw Modes
- +
+
+
Draw Modes
+ + + +
); } } -export { Graph2 as PanelComponent, TextOptions as PanelOptions }; +export { Graph2 as PanelComponent, GraphOptions as PanelOptionsComponent }; diff --git a/public/app/types/index.ts b/public/app/types/index.ts index c51622682d4..fc176fed7e2 100644 --- a/public/app/types/index.ts +++ b/public/app/types/index.ts @@ -20,7 +20,7 @@ import { DataQueryResponse, DataQueryOptions, } from './series'; -import { PanelProps } from './panel'; +import { PanelProps, PanelOptionsProps } from './panel'; import { PluginDashboard, PluginMeta, Plugin, PluginsState } from './plugins'; import { Organization, OrganizationPreferences, OrganizationState } from './organization'; import { @@ -69,6 +69,7 @@ export { TimeRange, LoadingState, PanelProps, + PanelOptionsProps, TimeSeries, TimeSeriesVM, TimeSeriesVMs, diff --git a/public/app/types/panel.ts b/public/app/types/panel.ts index 5ece77fc5aa..7febd0cad26 100644 --- a/public/app/types/panel.ts +++ b/public/app/types/panel.ts @@ -1,7 +1,14 @@ import { LoadingState, TimeSeries, TimeRange } from './series'; -export interface PanelProps { +export interface PanelProps { timeSeries: TimeSeries[]; timeRange: TimeRange; loading: LoadingState; + options: T; + renderCounter: number; +} + +export interface PanelOptionsProps { + options: T; + onChange: (options: T) => void; } diff --git a/public/app/types/plugins.ts b/public/app/types/plugins.ts index 1b5499f88b7..817777669d8 100644 --- a/public/app/types/plugins.ts +++ b/public/app/types/plugins.ts @@ -1,13 +1,18 @@ +import { ComponentClass } from 'react'; +import { PanelProps, PanelOptionsProps } 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; + PanelOptionsComponent: ComponentClass; } export interface PanelPlugin { diff --git a/public/app/viz/Graph.tsx b/public/app/viz/Graph.tsx index fab65225715..566080fbc92 100644 --- a/public/app/viz/Graph.tsx +++ b/public/app/viz/Graph.tsx @@ -8,6 +8,111 @@ import 'vendor/flot/jquery.flot.time'; // Types import { TimeRange, TimeSeriesVMs } from 'app/types'; +interface GraphProps { + timeSeries: TimeSeriesVMs; + timeRange: TimeRange; + showLines?: boolean; + showPoints?: boolean; + showBars?: boolean; + size?: { width: number; height: number }; +} + +export class Graph extends PureComponent { + static defaultProps = { + showLines: true, + showPoints: false, + showBars: false, + }; + + element: any; + + componentDidUpdate(prevProps: GraphProps) { + if ( + prevProps.timeSeries !== this.props.timeSeries || + prevProps.timeRange !== this.props.timeRange || + prevProps.size !== this.props.size + ) { + this.draw(); + } + } + + componentDidMount() { + this.draw(); + } + + draw() { + const { size, timeSeries, timeRange, showLines, showBars, showPoints } = this.props; + + if (!size) { + return; + } + + const ticks = (size.width || 0) / 100; + const min = timeRange.from.valueOf(); + const max = timeRange.to.valueOf(); + + 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, + max: max, + label: 'Datetime', + 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, + }, + }; + + try { + $.plot(this.element, timeSeries, flotOptions); + } catch (err) { + console.log('Graph rendering error', err, flotOptions, timeSeries); + } + } + + render() { + return ( +
+
(this.element = e)} /> +
+ ); + } +} + // Copied from graph.ts function time_format(ticks, min, max) { if (min && max && ticks) { @@ -34,91 +139,4 @@ 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; - size?: { width: number; height: number }; -} - -export class Graph extends PureComponent { - element: any; - - componentDidUpdate(prevProps: GraphProps) { - if ( - prevProps.timeSeries !== this.props.timeSeries || - prevProps.timeRange !== this.props.timeRange || - prevProps.size !== this.props.size - ) { - this.draw(); - } - } - - componentDidMount() { - this.draw(); - } - - draw() { - const { size, timeSeries, timeRange } = this.props; - - if (!size) { - return; - } - - const ticks = (size.width || 0) / 100; - const min = timeRange.from.valueOf(); - const max = timeRange.to.valueOf(); - - const dynamicOptions = { - xaxis: { - mode: 'time', - min: min, - max: max, - label: 'Datetime', - ticks: ticks, - timeformat: time_format(ticks, min, max), - }, - }; - - const options = { - ...FLOT_OPTIONS, - ...dynamicOptions, - }; - - console.log('plot', timeSeries, options); - $.plot(this.element, timeSeries, options); - } - - render() { - return ( -
-
(this.element = e)} /> -
- ); - } -} - export default withSize()(Graph);