diff --git a/pkg/api/frontendsettings.go b/pkg/api/frontendsettings.go index ed7054050e4..cb401577140 100644 --- a/pkg/api/frontendsettings.go +++ b/pkg/api/frontendsettings.go @@ -145,6 +145,7 @@ func (hs *HTTPServer) getFrontendSettingsMap(c *m.ReqContext) (map[string]interf "info": panel.Info, "hideFromList": panel.HideFromList, "sort": getPanelSort(panel.Id), + "noQueries": panel.NoQueries, } } diff --git a/pkg/plugins/models.go b/pkg/plugins/models.go index 5ac436205c1..7584981fc6c 100644 --- a/pkg/plugins/models.go +++ b/pkg/plugins/models.go @@ -47,6 +47,7 @@ type PluginBase struct { BaseUrl string `json:"baseUrl"` HideFromList bool `json:"hideFromList,omitempty"` State PluginState `json:"state,omitempty"` + NoQueries bool `json:"noQueries"` IncludedInAppId string `json:"-"` PluginDir string `json:"-"` diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index b02d9479dcc..3be9c361a83 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -10,14 +10,14 @@ import { PanelHeader } from './PanelHeader/PanelHeader'; import { DataPanel } from './DataPanel'; // 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 { profiler } from 'app/core/profiler'; // Types import { DashboardModel, PanelModel } from '../state'; 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 templateSrv from 'app/features/templating/template_srv'; @@ -94,7 +94,7 @@ export class PanelChrome extends PureComponent { 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 { timeRange, renderCounter } = this.state; const PanelComponent = plugin.exports.Panel; @@ -121,11 +121,45 @@ export class PanelChrome extends PureComponent { ); } - render() { - const { panel, dashboard } = this.props; - const { refreshCounter, timeRange, timeInfo } = this.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) + : ( + + {({ loading, panelData }) => { + return this.renderPanelPlugin(loading, panelData, width, height); + }} + + )} + + )} + + ); + } + + + render() { + const { dashboard, panel } = this.props; + const { timeInfo } = this.state; + const { transparent } = panel; - const { datasource, targets, transparent } = panel; const containerClassNames = `panel-container panel-container--absolute ${transparent ? 'panel-transparent' : ''}`; return ( @@ -145,23 +179,7 @@ export class PanelChrome extends PureComponent { scopedVars={panel.scopedVars} links={panel.links} /> - {panel.snapshotData ? ( - this.renderPanel(false, panel.snapshotData, width, height) - ) : ( - - {({ loading, panelData }) => { - return this.renderPanel(loading, panelData, width, height); - }} - - )} + {this.renderHelper(width, height)} ); }} diff --git a/public/app/features/dashboard/panel_editor/PanelEditor.tsx b/public/app/features/dashboard/panel_editor/PanelEditor.tsx index bfdc13bc8f2..7b8298deebf 100644 --- a/public/app/features/dashboard/panel_editor/PanelEditor.tsx +++ b/public/app/features/dashboard/panel_editor/PanelEditor.tsx @@ -30,6 +30,32 @@ interface PanelEditorTab { 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 { constructor(props) { super(props); @@ -72,31 +98,26 @@ export class PanelEditor extends PureComponent { render() { 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[] = [ - { id: 'queries', text: 'Queries' }, - { id: 'visualization', text: 'Visualization' }, - { id: 'advanced', text: 'Panel Options' }, + getPanelEditorTab(PanelEditorTabIds.Queries), + getPanelEditorTab(PanelEditorTabIds.Visualization), + getPanelEditorTab(PanelEditorTabIds.Advanced), ]; // handle panels that do not have queries tab - if (plugin.exports.PanelCtrl) { - if (!plugin.exports.PanelCtrl.prototype.onDataReceived) { - // remove queries tab - tabs.shift(); - // switch tab - if (activeTab === 'queries') { - activeTab = 'visualization'; - } + if (plugin.noQueries) { + // remove queries tab + tabs.shift(); + // switch tab + if (activeTab === PanelEditorTabIds.Queries) { + activeTab = PanelEditorTabIds.Visualization; } } if (config.alertingEnabled && plugin.id === 'graph') { - tabs.push({ - id: 'alert', - text: 'Alert', - }); + tabs.push(getPanelEditorTab(PanelEditorTabIds.Alert)); } return ( diff --git a/public/app/features/dashboard/utils/panel.ts b/public/app/features/dashboard/utils/panel.ts index c0d753477a7..c60a153d889 100644 --- a/public/app/features/dashboard/utils/panel.ts +++ b/public/app/features/dashboard/utils/panel.ts @@ -4,7 +4,8 @@ import store from 'app/core/store'; // Models import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; 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 import { isString as _isString } from 'lodash'; @@ -168,3 +169,19 @@ export function getResolution(panel: PanelModel): number { 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()); +}; diff --git a/public/app/plugins/panel/graph2/GraphPanel.tsx b/public/app/plugins/panel/graph2/GraphPanel.tsx index 01e5b2d819d..f1fc2b43d51 100644 --- a/public/app/plugins/panel/graph2/GraphPanel.tsx +++ b/public/app/plugins/panel/graph2/GraphPanel.tsx @@ -9,7 +9,7 @@ import { processTimeSeries } from '@grafana/ui/src/utils'; import { Graph } from '@grafana/ui'; // Types -import { PanelProps, NullValueMode } from '@grafana/ui/src/types'; +import { PanelProps, NullValueMode, TimeSeriesVMs } from '@grafana/ui/src/types'; import { Options } from './types'; interface Props extends PanelProps {} @@ -19,7 +19,7 @@ export class GraphPanel extends PureComponent { const { panelData, timeRange, width, height } = this.props; const { showLines, showBars, showPoints } = this.props.options; - let vmSeries; + let vmSeries: TimeSeriesVMs; if (panelData.timeSeries) { vmSeries = processTimeSeries({ timeSeries: panelData.timeSeries, diff --git a/public/app/plugins/panel/graph2/plugin.json b/public/app/plugins/panel/graph2/plugin.json index 9cb6a1f78a4..b11f93c9adc 100644 --- a/public/app/plugins/panel/graph2/plugin.json +++ b/public/app/plugins/panel/graph2/plugin.json @@ -2,7 +2,6 @@ "type": "panel", "name": "React Graph", "id": "graph2", - "state": "alpha", "info": { diff --git a/public/app/plugins/panel/text2/module.tsx b/public/app/plugins/panel/text2/module.tsx index 68523ff0880..cc3ec016273 100644 --- a/public/app/plugins/panel/text2/module.tsx +++ b/public/app/plugins/panel/text2/module.tsx @@ -2,7 +2,7 @@ import React, { PureComponent } from 'react'; import { PanelProps } from '@grafana/ui'; export class Text2 extends PureComponent { - constructor(props) { + constructor(props: PanelProps) { super(props); } diff --git a/public/app/plugins/panel/text2/plugin.json b/public/app/plugins/panel/text2/plugin.json index 53885dbd0f4..661ac4671ef 100644 --- a/public/app/plugins/panel/text2/plugin.json +++ b/public/app/plugins/panel/text2/plugin.json @@ -2,8 +2,8 @@ "type": "panel", "name": "Text v2", "id": "text2", - "state": "alpha", + "noQueries": true, "info": { "author": { diff --git a/public/app/types/plugins.ts b/public/app/types/plugins.ts index 51c3b7b0476..101f649eda9 100644 --- a/public/app/types/plugins.ts +++ b/public/app/types/plugins.ts @@ -9,6 +9,7 @@ export interface PanelPlugin { info: any; sort: number; exports?: PluginExports; + noQueries?: boolean; } export interface Plugin {