Commit eae754a0 by Torkel Ödegaard Committed by GitHub

Merge pull request #13971 from grafana/react-panel-options

React panel options
parents a3196a13 562411af
......@@ -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>
......
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,21 @@ 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);
this.forceUpdate();
};
renderVizTab() {
return (
<div className="viz-editor">
......@@ -70,6 +78,7 @@ export class PanelEditor extends React.Component<PanelEditorProps, any> {
partial: true,
})
);
this.forceUpdate();
};
render() {
......
......@@ -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;
......
......@@ -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();
});
});
......
// 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;
interface Props extends PanelProps {
options: Options;
onChange: (options: Options) => void;
}
interface Props extends PanelProps<Options> {}
export class Graph2 extends PureComponent<Props> {
constructor(props) {
super(props);
......@@ -25,27 +24,52 @@ 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<any> {
onChange = () => {};
export class GraphOptions extends PureComponent<PanelOptionsProps<Options>> {
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 (
<div className="section gf-form-group">
<h5 className="section-heading">Draw Modes</h5>
<Switch label="Lines" checked={true} onChange={this.onChange} />
<div>
<div className="section gf-form-group">
<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.onToggleBars} />
<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 };
......@@ -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,
......
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 PanelOptionsProps<T = any> {
options: T;
onChange: (options: T) => void;
}
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<PanelProps>;
PanelOptionsComponent: ComponentClass<PanelOptionsProps>;
}
export interface PanelPlugin {
......
......@@ -8,63 +8,22 @@ import 'vendor/flot/jquery.flot.time';
// Types
import { TimeRange, TimeSeriesVMs } from 'app/types';
// Copied from graph.ts
function time_format(ticks, min, max) {
if (min && max && ticks) {
const range = max - min;
const secPerTick = range / ticks / 1000;
const oneDay = 86400000;
const oneYear = 31536000000;
if (secPerTick <= 45) {
return '%H:%M:%S';
}
if (secPerTick <= 7200 || range <= oneDay) {
return '%H:%M';
}
if (secPerTick <= 80000) {
return '%m/%d %H:%M';
}
if (secPerTick <= 2419200 || range <= oneYear) {
return '%m/%d';
}
return '%Y-%m';
}
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 +41,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 +51,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 +84,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() {
......@@ -121,4 +113,30 @@ export class Graph extends PureComponent<GraphProps> {
}
}
// Copied from graph.ts
function time_format(ticks, min, max) {
if (min && max && ticks) {
const range = max - min;
const secPerTick = range / ticks / 1000;
const oneDay = 86400000;
const oneYear = 31536000000;
if (secPerTick <= 45) {
return '%H:%M:%S';
}
if (secPerTick <= 7200 || range <= oneDay) {
return '%H:%M';
}
if (secPerTick <= 80000) {
return '%m/%d %H:%M';
}
if (secPerTick <= 2419200 || range <= oneYear) {
return '%m/%d';
}
return '%Y-%m';
}
return '%H:%M';
}
export default withSize()(Graph);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment