Commit 1db5d86b by Peter Holmberg

Merge branch '15217-panels-without-queries' into move-error-boundry

parents 7d0edb28 be58e275
...@@ -145,6 +145,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf ...@@ -145,6 +145,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf
"info": panel.Info, "info": panel.Info,
"hideFromList": panel.HideFromList, "hideFromList": panel.HideFromList,
"sort": getPanelSort(panel.Id), "sort": getPanelSort(panel.Id),
"noQueries": panel.NoQueries,
} }
} }
......
...@@ -47,6 +47,7 @@ type PluginBase struct { ...@@ -47,6 +47,7 @@ type PluginBase struct {
BaseUrl string `json:"baseUrl"` BaseUrl string `json:"baseUrl"`
HideFromList bool `json:"hideFromList,omitempty"` HideFromList bool `json:"hideFromList,omitempty"`
State PluginState `json:"state,omitempty"` State PluginState `json:"state,omitempty"`
NoQueries bool `json:"noQueries"`
IncludedInAppId string `json:"-"` IncludedInAppId string `json:"-"`
PluginDir string `json:"-"` PluginDir string `json:"-"`
......
...@@ -10,14 +10,14 @@ import { PanelHeader } from './PanelHeader/PanelHeader'; ...@@ -10,14 +10,14 @@ import { PanelHeader } from './PanelHeader/PanelHeader';
import { DataPanel } from './DataPanel'; import { DataPanel } from './DataPanel';
// Utils // Utils
import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel'; import { applyPanelTimeOverrides, snapshotDataToPanelData } from 'app/features/dashboard/utils/panel';
import { PANEL_HEADER_HEIGHT } from 'app/core/constants'; import { PANEL_HEADER_HEIGHT } from 'app/core/constants';
import { profiler } from 'app/core/profiler'; import { profiler } from 'app/core/profiler';
// Types // Types
import { DashboardModel, PanelModel } from '../state'; import { DashboardModel, PanelModel } from '../state';
import { PanelPlugin } from 'app/types'; import { PanelPlugin } from 'app/types';
import { TimeRange, LoadingState } from '@grafana/ui'; import { TimeRange, LoadingState, PanelData } from '@grafana/ui';
import variables from 'sass/_variables.scss'; import variables from 'sass/_variables.scss';
import templateSrv from 'app/features/templating/template_srv'; import templateSrv from 'app/features/templating/template_srv';
...@@ -94,7 +94,7 @@ export class PanelChrome extends PureComponent<Props, State> { ...@@ -94,7 +94,7 @@ export class PanelChrome extends PureComponent<Props, State> {
return !this.props.dashboard.otherPanelInFullscreen(this.props.panel); return !this.props.dashboard.otherPanelInFullscreen(this.props.panel);
} }
renderPanel(loading, panelData, width, height): JSX.Element { renderPanelPlugin(loading: LoadingState, panelData: PanelData, width: number, height: number): JSX.Element {
const { panel, plugin } = this.props; const { panel, plugin } = this.props;
const { timeRange, renderCounter } = this.state; const { timeRange, renderCounter } = this.state;
const PanelComponent = plugin.exports.Panel; const PanelComponent = plugin.exports.Panel;
...@@ -121,11 +121,45 @@ export class PanelChrome extends PureComponent<Props, State> { ...@@ -121,11 +121,45 @@ export class PanelChrome extends PureComponent<Props, State> {
); );
} }
renderHelper = (width: number, height: number): JSX.Element => {
const { panel, plugin } = this.props;
const { refreshCounter, timeRange } = this.state;
const { datasource, targets } = panel;
return (
<>
{panel.snapshotData && panel.snapshotData.length > 0 ? (
this.renderPanelPlugin(LoadingState.Done, snapshotDataToPanelData(panel), width, height)
) : (
<>
{plugin.noQueries ?
this.renderPanelPlugin(LoadingState.Done, null, width, height)
: (
<DataPanel
datasource={datasource}
queries={targets}
timeRange={timeRange}
isVisible={this.isVisible}
widthPixels={width}
refreshCounter={refreshCounter}
onDataResponse={this.onDataResponse}
>
{({ loading, panelData }) => {
return this.renderPanelPlugin(loading, panelData, width, height);
}}
</DataPanel>
)}
</>
)}
</>
);
}
render() { render() {
const { panel, dashboard } = this.props; const { dashboard, panel } = this.props;
const { refreshCounter, timeRange, timeInfo } = this.state; const { timeInfo } = this.state;
const { transparent } = panel;
const { datasource, targets, transparent } = panel;
const containerClassNames = `panel-container panel-container--absolute ${transparent ? 'panel-transparent' : ''}`; const containerClassNames = `panel-container panel-container--absolute ${transparent ? 'panel-transparent' : ''}`;
return ( return (
<AutoSizer> <AutoSizer>
...@@ -145,23 +179,7 @@ export class PanelChrome extends PureComponent<Props, State> { ...@@ -145,23 +179,7 @@ export class PanelChrome extends PureComponent<Props, State> {
scopedVars={panel.scopedVars} scopedVars={panel.scopedVars}
links={panel.links} links={panel.links}
/> />
{panel.snapshotData ? ( {this.renderHelper(width, height)}
this.renderPanel(false, panel.snapshotData, width, height)
) : (
<DataPanel
datasource={datasource}
queries={targets}
timeRange={timeRange}
isVisible={this.isVisible}
widthPixels={width}
refreshCounter={refreshCounter}
onDataResponse={this.onDataResponse}
>
{({ loading, panelData }) => {
return this.renderPanel(loading, panelData, width, height);
}}
</DataPanel>
)}
</div> </div>
); );
}} }}
......
...@@ -30,6 +30,32 @@ interface PanelEditorTab { ...@@ -30,6 +30,32 @@ interface PanelEditorTab {
text: string; text: string;
} }
enum PanelEditorTabIds {
Queries = 'queries',
Visualization = 'visualization',
Advanced = 'advanced',
Alert = 'alert'
}
interface PanelEditorTab {
id: string;
text: string;
}
const panelEditorTabTexts = {
[PanelEditorTabIds.Queries]: 'Queries',
[PanelEditorTabIds.Visualization]: 'Visualization',
[PanelEditorTabIds.Advanced]: 'Panel Options',
[PanelEditorTabIds.Alert]: 'Alert',
};
const getPanelEditorTab = (tabId: PanelEditorTabIds): PanelEditorTab => {
return {
id: tabId,
text: panelEditorTabTexts[tabId]
};
};
export class PanelEditor extends PureComponent<PanelEditorProps> { export class PanelEditor extends PureComponent<PanelEditorProps> {
constructor(props) { constructor(props) {
super(props); super(props);
...@@ -72,31 +98,26 @@ export class PanelEditor extends PureComponent<PanelEditorProps> { ...@@ -72,31 +98,26 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
render() { render() {
const { plugin } = this.props; const { plugin } = this.props;
let activeTab = store.getState().location.query.tab || 'queries'; let activeTab: PanelEditorTabIds = store.getState().location.query.tab || PanelEditorTabIds.Queries;
const tabs: PanelEditorTab[] = [ const tabs: PanelEditorTab[] = [
{ id: 'queries', text: 'Queries' }, getPanelEditorTab(PanelEditorTabIds.Queries),
{ id: 'visualization', text: 'Visualization' }, getPanelEditorTab(PanelEditorTabIds.Visualization),
{ id: 'advanced', text: 'Panel Options' }, getPanelEditorTab(PanelEditorTabIds.Advanced),
]; ];
// handle panels that do not have queries tab // handle panels that do not have queries tab
if (plugin.exports.PanelCtrl) { if (plugin.noQueries) {
if (!plugin.exports.PanelCtrl.prototype.onDataReceived) {
// remove queries tab // remove queries tab
tabs.shift(); tabs.shift();
// switch tab // switch tab
if (activeTab === 'queries') { if (activeTab === PanelEditorTabIds.Queries) {
activeTab = 'visualization'; activeTab = PanelEditorTabIds.Visualization;
}
} }
} }
if (config.alertingEnabled && plugin.id === 'graph') { if (config.alertingEnabled && plugin.id === 'graph') {
tabs.push({ tabs.push(getPanelEditorTab(PanelEditorTabIds.Alert));
id: 'alert',
text: 'Alert',
});
} }
return ( return (
......
...@@ -4,7 +4,8 @@ import store from 'app/core/store'; ...@@ -4,7 +4,8 @@ import store from 'app/core/store';
// Models // Models
import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { DashboardModel } from 'app/features/dashboard/state/DashboardModel';
import { PanelModel } from 'app/features/dashboard/state/PanelModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel';
import { TimeRange } from '@grafana/ui'; import { PanelData, TimeRange, TimeSeries } from '@grafana/ui';
import { TableData } from '@grafana/ui/src';
// Utils // Utils
import { isString as _isString } from 'lodash'; import { isString as _isString } from 'lodash';
...@@ -168,3 +169,19 @@ export function getResolution(panel: PanelModel): number { ...@@ -168,3 +169,19 @@ export function getResolution(panel: PanelModel): number {
return panel.maxDataPoints ? panel.maxDataPoints : Math.ceil(width * (panel.gridPos.w / 24)); return panel.maxDataPoints ? panel.maxDataPoints : Math.ceil(width * (panel.gridPos.w / 24));
} }
const isTimeSeries = (data: any): data is TimeSeries => data && data.hasOwnProperty('datapoints');
const isTableData = (data: any): data is TableData => data && data.hasOwnProperty('columns');
export const snapshotDataToPanelData = (panel: PanelModel): PanelData => {
const snapshotData = panel.snapshotData;
if (isTimeSeries(snapshotData[0])) {
return {
timeSeries: snapshotData
} as PanelData;
} else if (isTableData(snapshotData[0])) {
return {
tableData: snapshotData[0]
} as PanelData;
}
throw new Error('snapshotData is invalid:' + snapshotData.toString());
};
...@@ -9,7 +9,7 @@ import { processTimeSeries } from '@grafana/ui/src/utils'; ...@@ -9,7 +9,7 @@ import { processTimeSeries } from '@grafana/ui/src/utils';
import { Graph } from '@grafana/ui'; import { Graph } from '@grafana/ui';
// Types // Types
import { PanelProps, NullValueMode } from '@grafana/ui/src/types'; import { PanelProps, NullValueMode, TimeSeriesVMs } from '@grafana/ui/src/types';
import { Options } from './types'; import { Options } from './types';
interface Props extends PanelProps<Options> {} interface Props extends PanelProps<Options> {}
...@@ -19,7 +19,7 @@ export class GraphPanel extends PureComponent<Props> { ...@@ -19,7 +19,7 @@ export class GraphPanel extends PureComponent<Props> {
const { panelData, timeRange, width, height } = this.props; const { panelData, timeRange, width, height } = this.props;
const { showLines, showBars, showPoints } = this.props.options; const { showLines, showBars, showPoints } = this.props.options;
let vmSeries; let vmSeries: TimeSeriesVMs;
if (panelData.timeSeries) { if (panelData.timeSeries) {
vmSeries = processTimeSeries({ vmSeries = processTimeSeries({
timeSeries: panelData.timeSeries, timeSeries: panelData.timeSeries,
......
...@@ -2,7 +2,6 @@ ...@@ -2,7 +2,6 @@
"type": "panel", "type": "panel",
"name": "React Graph", "name": "React Graph",
"id": "graph2", "id": "graph2",
"state": "alpha", "state": "alpha",
"info": { "info": {
......
...@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; ...@@ -2,7 +2,7 @@ import React, { PureComponent } from 'react';
import { PanelProps } from '@grafana/ui'; import { PanelProps } from '@grafana/ui';
export class Text2 extends PureComponent<PanelProps> { export class Text2 extends PureComponent<PanelProps> {
constructor(props) { constructor(props: PanelProps) {
super(props); super(props);
} }
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
"type": "panel", "type": "panel",
"name": "Text v2", "name": "Text v2",
"id": "text2", "id": "text2",
"state": "alpha", "state": "alpha",
"noQueries": true,
"info": { "info": {
"author": { "author": {
......
...@@ -9,6 +9,7 @@ export interface PanelPlugin { ...@@ -9,6 +9,7 @@ export interface PanelPlugin {
info: any; info: any;
sort: number; sort: number;
exports?: PluginExports; exports?: PluginExports;
noQueries?: boolean;
} }
export interface Plugin { export interface Plugin {
......
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