diff --git a/packages/grafana-ui/src/components/Table/Table.story.tsx b/packages/grafana-ui/src/components/Table/Table.story.tsx index 03765730343..8ab53a1ac77 100644 --- a/packages/grafana-ui/src/components/Table/Table.story.tsx +++ b/packages/grafana-ui/src/components/Table/Table.story.tsx @@ -40,8 +40,6 @@ export function makeDummyTable(columnCount: number, rowCount: number): TableData const suffix = (rowId + 1).toString(); return Array.from(new Array(columnCount), (x, colId) => columnIndexToLeter(colId) + suffix); }), - type: 'table', - columnMap: {}, }; } diff --git a/packages/grafana-ui/src/types/data.ts b/packages/grafana-ui/src/types/data.ts index b36e23ae6c0..ab177eb8888 100644 --- a/packages/grafana-ui/src/types/data.ts +++ b/packages/grafana-ui/src/types/data.ts @@ -51,27 +51,13 @@ export enum NullValueMode { export type TimeSeriesVMs = TimeSeriesVM[]; export interface Column { - text: string; - title?: string; - type?: string; - sort?: boolean; - desc?: boolean; - filterable?: boolean; + text: string; // The column name + type?: 'time' | 'number' | 'string' | 'object'; // not used anywhere? can we remove? + filterable?: boolean; // currently only set by elasticsearch, and used in the table panel unit?: string; } export interface TableData { columns: Column[]; rows: any[]; - type: string; - columnMap: any; -} - -export type SingleStatValue = number | string | null; - -/* - * So we can add meta info like tags & series name - */ -export interface SingleStatValueInfo { - value: SingleStatValue; } diff --git a/packages/grafana-ui/src/types/panel.ts b/packages/grafana-ui/src/types/panel.ts index 2307f446a98..6c42ecfceb9 100644 --- a/packages/grafana-ui/src/types/panel.ts +++ b/packages/grafana-ui/src/types/panel.ts @@ -1,12 +1,12 @@ import { ComponentClass } from 'react'; -import { TimeSeries, LoadingState, TableData } from './data'; +import { LoadingState, TableData } from './data'; import { TimeRange } from './time'; import { ScopedVars } from './datasource'; export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string; export interface PanelProps { - panelData: PanelData; + data?: TableData[]; timeRange: TimeRange; loading: LoadingState; options: T; @@ -16,11 +16,6 @@ export interface PanelProps { replaceVariables: InterpolateFunction; } -export interface PanelData { - timeSeries?: TimeSeries[]; - tableData?: TableData; -} - export interface PanelEditorProps { options: T; onOptionsChange: (options: T) => void; diff --git a/packages/grafana-ui/src/utils/__snapshots__/processTableData.test.ts.snap b/packages/grafana-ui/src/utils/__snapshots__/processTableData.test.ts.snap index 42e25cb38e4..f894917b7e0 100644 --- a/packages/grafana-ui/src/utils/__snapshots__/processTableData.test.ts.snap +++ b/packages/grafana-ui/src/utils/__snapshots__/processTableData.test.ts.snap @@ -2,7 +2,6 @@ exports[`processTableData basic processing should generate a header and fix widths 1`] = ` Object { - "columnMap": Object {}, "columns": Array [ Object { "text": "Column 1", @@ -31,13 +30,11 @@ Object { null, ], ], - "type": "table", } `; exports[`processTableData basic processing should read header and two rows 1`] = ` Object { - "columnMap": Object {}, "columns": Array [ Object { "text": "a", @@ -61,6 +58,5 @@ Object { 6, ], ], - "type": "table", } `; diff --git a/packages/grafana-ui/src/utils/index.ts b/packages/grafana-ui/src/utils/index.ts index df923f19a56..7e7c2e30ebf 100644 --- a/packages/grafana-ui/src/utils/index.ts +++ b/packages/grafana-ui/src/utils/index.ts @@ -1,5 +1,5 @@ export * from './processTimeSeries'; -export * from './singlestat'; +export * from './processTableData'; export * from './valueFormats/valueFormats'; export * from './colors'; export * from './namedColorsPalette'; diff --git a/packages/grafana-ui/src/utils/processTableData.test.ts b/packages/grafana-ui/src/utils/processTableData.test.ts index fc2545b1706..ba9edcacebd 100644 --- a/packages/grafana-ui/src/utils/processTableData.test.ts +++ b/packages/grafana-ui/src/utils/processTableData.test.ts @@ -1,4 +1,4 @@ -import { parseCSV } from './processTableData'; +import { parseCSV, toTableData } from './processTableData'; describe('processTableData', () => { describe('basic processing', () => { @@ -18,3 +18,41 @@ describe('processTableData', () => { }); }); }); + +describe('toTableData', () => { + it('converts timeseries to table skipping nulls', () => { + const input1 = { + target: 'Field Name', + datapoints: [[100, 1], [200, 2]], + }; + const input2 = { + // without target + target: '', + datapoints: [[100, 1], [200, 2]], + }; + const data = toTableData([null, input1, input2, null, null]); + expect(data.length).toBe(2); + expect(data[0].columns[0].text).toBe(input1.target); + expect(data[0].rows).toBe(input1.datapoints); + + // Default name + expect(data[1].columns[0].text).toEqual('Value'); + }); + + it('keeps tableData unchanged', () => { + const input = { + columns: [{ text: 'A' }, { text: 'B' }, { text: 'C' }], + rows: [[100, 'A', 1], [200, 'B', 2], [300, 'C', 3]], + }; + const data = toTableData([null, input, null, null]); + expect(data.length).toBe(1); + expect(data[0]).toBe(input); + }); + + it('supports null values OK', () => { + expect(toTableData([null, null, null, null])).toEqual([]); + expect(toTableData(undefined)).toEqual([]); + expect(toTableData((null as unknown) as any[])).toEqual([]); + expect(toTableData([])).toEqual([]); + }); +}); diff --git a/packages/grafana-ui/src/utils/processTableData.ts b/packages/grafana-ui/src/utils/processTableData.ts index d65e42827e0..d4a91efd622 100644 --- a/packages/grafana-ui/src/utils/processTableData.ts +++ b/packages/grafana-ui/src/utils/processTableData.ts @@ -3,7 +3,7 @@ import isNumber from 'lodash/isNumber'; import Papa, { ParseError, ParseMeta } from 'papaparse'; // Types -import { TableData, Column } from '../types'; +import { TableData, Column, TimeSeries } from '../types'; // Subset of all parse options export interface TableParseOptions { @@ -70,8 +70,6 @@ export function matchRowSizes(table: TableData): TableData { return { columns, rows: fixedRows, - type: table.type, - columnMap: table.columnMap, }; } @@ -118,8 +116,6 @@ export function parseCSV(text: string, options?: TableParseOptions, details?: Ta return { columns: [], rows: [], - type: 'table', - columnMap: {}, }; } @@ -130,11 +126,48 @@ export function parseCSV(text: string, options?: TableParseOptions, details?: Ta return matchRowSizes({ columns: makeColumns(header), rows: results.data, - type: 'table', - columnMap: {}, }); } +function convertTimeSeriesToTableData(timeSeries: TimeSeries): TableData { + return { + columns: [ + { + text: timeSeries.target || 'Value', + unit: timeSeries.unit, + }, + { + text: 'Time', + type: 'time', + unit: 'dateTimeAsIso', + }, + ], + rows: timeSeries.datapoints, + }; +} + +export const isTableData = (data: any): data is TableData => data && data.hasOwnProperty('columns'); + +export const toTableData = (results?: any[]): TableData[] => { + if (!results) { + return []; + } + + return results + .filter(d => !!d) + .map(data => { + if (data.hasOwnProperty('columns')) { + return data as TableData; + } + if (data.hasOwnProperty('datapoints')) { + return convertTimeSeriesToTableData(data); + } + // TODO, try to convert JSON to table? + console.warn('Can not convert', data); + throw new Error('Unsupported data format'); + }); +}; + export function sortTableData(data: TableData, sortIndex?: number, reverse = false): TableData { if (isNumber(sortIndex)) { const copy = { diff --git a/packages/grafana-ui/src/utils/processTimeSeries.ts b/packages/grafana-ui/src/utils/processTimeSeries.ts index f5e9f96efba..9a6aeac6e69 100644 --- a/packages/grafana-ui/src/utils/processTimeSeries.ts +++ b/packages/grafana-ui/src/utils/processTimeSeries.ts @@ -4,17 +4,36 @@ import isNumber from 'lodash/isNumber'; import { colors } from './colors'; // Types -import { TimeSeries, TimeSeriesVMs, NullValueMode, TimeSeriesValue } from '../types'; +import { TimeSeriesVMs, NullValueMode, TimeSeriesValue, TableData } from '../types'; interface Options { - timeSeries: TimeSeries[]; + data: TableData[]; + xColumn?: number; // Time (or null to guess) + yColumn?: number; // Value (or null to guess) nullValueMode: NullValueMode; } -export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeSeriesVMs { - const vmSeries = timeSeries.map((item, index) => { +// NOTE: this should move to processTableData.ts +// I left it as is so the merge changes are more clear. +export function processTimeSeries({ data, xColumn, yColumn, nullValueMode }: Options): TimeSeriesVMs { + const vmSeries = data.map((item, index) => { + if (!isNumber(xColumn)) { + xColumn = 1; // Default timeseries colum. TODO, find first time field! + } + if (!isNumber(yColumn)) { + yColumn = 0; // TODO, find first non-time field + } + + // TODO? either % or throw error? + if (xColumn >= item.columns.length) { + throw new Error('invalid colum: ' + xColumn); + } + if (yColumn >= item.columns.length) { + throw new Error('invalid colum: ' + yColumn); + } + const colorIndex = index % colors.length; - const label = item.target; + const label = item.columns[yColumn].text; const result = []; // stat defaults @@ -42,9 +61,9 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS let previousValue = 0; let previousDeltaUp = true; - for (let i = 0; i < item.datapoints.length; i++) { - currentValue = item.datapoints[i][0]; - currentTime = item.datapoints[i][1]; + for (let i = 0; i < item.rows.length; i++) { + currentValue = item.rows[i][yColumn]; + currentTime = item.rows[i][xColumn]; if (typeof currentTime !== 'number') { continue; @@ -95,7 +114,7 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS if (previousValue > currentValue) { // counter reset previousDeltaUp = false; - if (i === item.datapoints.length - 1) { + if (i === item.rows.length - 1) { // reset on last delta += currentValue; } diff --git a/packages/grafana-ui/src/utils/singlestat.ts b/packages/grafana-ui/src/utils/singlestat.ts deleted file mode 100644 index 5f5fbb8f247..00000000000 --- a/packages/grafana-ui/src/utils/singlestat.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { PanelData, NullValueMode, SingleStatValueInfo } from '../types'; -import { processTimeSeries } from './processTimeSeries'; - -export interface SingleStatProcessingOptions { - panelData: PanelData; - stat: string; -} - -// -// This is a temporary thing, waiting for a better data model and maybe unification between time series & table data -// -export function processSingleStatPanelData(options: SingleStatProcessingOptions): SingleStatValueInfo[] { - const { panelData, stat } = options; - - if (panelData.timeSeries) { - const timeSeries = processTimeSeries({ - timeSeries: panelData.timeSeries, - nullValueMode: NullValueMode.Null, - }); - - return timeSeries.map((series, index) => { - const value = stat !== 'name' ? series.stats[stat] : series.label; - - return { - value: value, - }; - }); - } else if (panelData.tableData) { - throw { message: 'Panel data not supported' }; - } - - return []; -} diff --git a/public/app/core/table_model.ts b/public/app/core/table_model.ts index fa7170bed13..291689941f7 100644 --- a/public/app/core/table_model.ts +++ b/public/app/core/table_model.ts @@ -1,17 +1,18 @@ import _ from 'lodash'; +import { Column, TableData } from '@grafana/ui'; -interface Column { - text: string; +/** + * Extends the standard Column class with variables that get + * mutated in the angular table panel. + */ +interface MutableColumn extends Column { title?: string; - type?: string; sort?: boolean; desc?: boolean; - filterable?: boolean; - unit?: string; } -export default class TableModel { - columns: Column[]; +export default class TableModel implements TableData { + columns: MutableColumn[]; rows: any[]; type: string; columnMap: any; diff --git a/public/app/features/dashboard/dashgrid/DataPanel.tsx b/public/app/features/dashboard/dashgrid/DataPanel.tsx index 82d94669cd6..6941832c273 100644 --- a/public/app/features/dashboard/dashgrid/DataPanel.tsx +++ b/public/app/features/dashboard/dashgrid/DataPanel.tsx @@ -11,16 +11,15 @@ import { DataQueryResponse, DataQueryError, LoadingState, - PanelData, TableData, TimeRange, - TimeSeries, ScopedVars, + toTableData, } from '@grafana/ui'; interface RenderProps { loading: LoadingState; - panelData: PanelData; + data: TableData[]; } export interface Props { @@ -44,7 +43,7 @@ export interface State { isFirstLoad: boolean; loading: LoadingState; response: DataQueryResponse; - panelData: PanelData; + data?: TableData[]; } export class DataPanel extends Component { @@ -64,7 +63,6 @@ export class DataPanel extends Component { response: { data: [], }, - panelData: {}, isFirstLoad: true, }; } @@ -149,7 +147,7 @@ export class DataPanel extends Component { this.setState({ loading: LoadingState.Done, response: resp, - panelData: this.getPanelData(resp), + data: toTableData(resp.data), isFirstLoad: false, }); } catch (err) { @@ -172,23 +170,9 @@ export class DataPanel extends Component { } }; - getPanelData(response: DataQueryResponse) { - if (response.data.length > 0 && (response.data[0] as TableData).type === 'table') { - return { - tableData: response.data[0] as TableData, - timeSeries: null, - }; - } - - return { - timeSeries: response.data as TimeSeries[], - tableData: null, - }; - } - render() { const { queries } = this.props; - const { loading, isFirstLoad, panelData } = this.state; + const { loading, isFirstLoad, data } = this.state; // do not render component until we have first data if (isFirstLoad && (loading === LoadingState.Loading || loading === LoadingState.NotStarted)) { @@ -206,7 +190,7 @@ export class DataPanel extends Component { return ( <> {loading === LoadingState.Loading && this.renderLoadingState()} - {this.props.children({ loading, panelData })} + {this.props.children({ loading, data })} ); } diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index deb14f130ec..9a2dec5acd8 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -11,7 +11,7 @@ import { DataPanel } from './DataPanel'; import ErrorBoundary from '../../../core/components/ErrorBoundary/ErrorBoundary'; // Utils -import { applyPanelTimeOverrides, snapshotDataToPanelData } from 'app/features/dashboard/utils/panel'; +import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel'; import { PANEL_HEADER_HEIGHT } from 'app/core/constants'; import { profiler } from 'app/core/profiler'; import config from 'app/core/config'; @@ -19,7 +19,7 @@ import config from 'app/core/config'; // Types import { DashboardModel, PanelModel } from '../state'; import { PanelPlugin } from 'app/types'; -import { DataQueryResponse, TimeRange, LoadingState, PanelData, DataQueryError } from '@grafana/ui'; +import { DataQueryResponse, TimeRange, LoadingState, TableData, DataQueryError, toTableData } from '@grafana/ui'; import { ScopedVars } from '@grafana/ui'; import templateSrv from 'app/features/templating/template_srv'; @@ -139,10 +139,10 @@ export class PanelChrome extends PureComponent { } get getDataForPanel() { - return this.hasPanelSnapshot ? snapshotDataToPanelData(this.props.panel) : null; + return this.hasPanelSnapshot ? toTableData(this.props.panel.snapshotData) : null; } - renderPanelPlugin(loading: LoadingState, panelData: PanelData, width: number, height: number): JSX.Element { + renderPanelPlugin(loading: LoadingState, data: TableData[], width: number, height: number): JSX.Element { const { panel, plugin } = this.props; const { timeRange, renderCounter } = this.state; const PanelComponent = plugin.exports.reactPanel.panel; @@ -157,7 +157,7 @@ export class PanelChrome extends PureComponent {
{ onDataResponse={this.onDataResponse} onError={this.onDataError} > - {({ loading, panelData }) => { - return this.renderPanelPlugin(loading, panelData, width, height); + {({ loading, data }) => { + return this.renderPanelPlugin(loading, data, width, height); }} ) : ( diff --git a/public/app/features/dashboard/utils/panel.ts b/public/app/features/dashboard/utils/panel.ts index d14432cb2eb..57f4b81a0e0 100644 --- a/public/app/features/dashboard/utils/panel.ts +++ b/public/app/features/dashboard/utils/panel.ts @@ -4,8 +4,7 @@ import store from 'app/core/store'; // Models import { DashboardModel } from 'app/features/dashboard/state/DashboardModel'; import { PanelModel } from 'app/features/dashboard/state/PanelModel'; -import { PanelData, TimeRange, TimeSeries } from '@grafana/ui'; -import { TableData } from '@grafana/ui/src'; +import { TimeRange } from '@grafana/ui'; // Utils import { isString as _isString } from 'lodash'; @@ -170,19 +169,3 @@ 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/bargauge/BarGaugePanel.tsx b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx index 2c3b5efe78d..2358b1ddab4 100644 --- a/public/app/plugins/panel/bargauge/BarGaugePanel.tsx +++ b/public/app/plugins/panel/bargauge/BarGaugePanel.tsx @@ -32,14 +32,14 @@ export class BarGaugePanel extends PureComponent> { }; render() { - const { height, width, options, panelData, renderCounter } = this.props; + const { height, width, options, data, renderCounter } = this.props; return ( diff --git a/public/app/plugins/panel/gauge/GaugePanel.tsx b/public/app/plugins/panel/gauge/GaugePanel.tsx index d091812f23c..a0fdf9ed405 100644 --- a/public/app/plugins/panel/gauge/GaugePanel.tsx +++ b/public/app/plugins/panel/gauge/GaugePanel.tsx @@ -37,14 +37,14 @@ export class GaugePanel extends PureComponent> { }; render() { - const { height, width, options, panelData, renderCounter } = this.props; + const { height, width, options, data, renderCounter } = this.props; return ( diff --git a/public/app/plugins/panel/graph2/GraphPanel.tsx b/public/app/plugins/panel/graph2/GraphPanel.tsx index f1fc2b43d51..f04e73e56fb 100644 --- a/public/app/plugins/panel/graph2/GraphPanel.tsx +++ b/public/app/plugins/panel/graph2/GraphPanel.tsx @@ -16,13 +16,13 @@ interface Props extends PanelProps {} export class GraphPanel extends PureComponent { render() { - const { panelData, timeRange, width, height } = this.props; + const { data, timeRange, width, height } = this.props; const { showLines, showBars, showPoints } = this.props.options; let vmSeries: TimeSeriesVMs; - if (panelData.timeSeries) { + if (data) { vmSeries = processTimeSeries({ - timeSeries: panelData.timeSeries, + data, nullValueMode: NullValueMode.Ignore, }); } diff --git a/public/app/plugins/panel/singlestat/module.ts b/public/app/plugins/panel/singlestat/module.ts index bcf09297cf7..5b75de44949 100644 --- a/public/app/plugins/panel/singlestat/module.ts +++ b/public/app/plugins/panel/singlestat/module.ts @@ -8,7 +8,7 @@ import kbn from 'app/core/utils/kbn'; import config from 'app/core/config'; import TimeSeries from 'app/core/time_series2'; import { MetricsPanelCtrl } from 'app/plugins/sdk'; -import { GrafanaThemeType, getValueFormat, getColorFromHexRgbOrName } from '@grafana/ui'; +import { GrafanaThemeType, getValueFormat, getColorFromHexRgbOrName, isTableData } from '@grafana/ui'; class SingleStatCtrl extends MetricsPanelCtrl { static templateUrl = 'module.html'; @@ -112,7 +112,7 @@ class SingleStatCtrl extends MetricsPanelCtrl { scopedVars: _.extend({}, this.panel.scopedVars), }; - if (dataList.length > 0 && dataList[0].type === 'table') { + if (dataList.length > 0 && isTableData(dataList[0])) { this.dataType = 'table'; const tableData = dataList.map(this.tableHandler.bind(this)); this.setTableValues(tableData, data); diff --git a/public/app/plugins/panel/singlestat2/SingleStatPanel.tsx b/public/app/plugins/panel/singlestat2/SingleStatPanel.tsx index df162f31400..1193a031c94 100644 --- a/public/app/plugins/panel/singlestat2/SingleStatPanel.tsx +++ b/public/app/plugins/panel/singlestat2/SingleStatPanel.tsx @@ -4,27 +4,33 @@ import React, { PureComponent, CSSProperties } from 'react'; // Types import { SingleStatOptions, SingleStatBaseOptions } from './types'; -import { processSingleStatPanelData, DisplayValue, PanelProps } from '@grafana/ui'; +import { DisplayValue, PanelProps, processTimeSeries, NullValueMode } from '@grafana/ui'; import { config } from 'app/core/config'; import { getDisplayProcessor } from '@grafana/ui'; import { ProcessedValuesRepeater } from './ProcessedValuesRepeater'; export const getSingleStatValues = (props: PanelProps): DisplayValue[] => { - const { panelData, replaceVariables, options } = props; + const { data, replaceVariables, options } = props; const { valueOptions, valueMappings } = options; + const { unit, decimals, stat } = valueOptions; + const processor = getDisplayProcessor({ - unit: valueOptions.unit, - decimals: valueOptions.decimals, + unit, + decimals, mappings: valueMappings, thresholds: options.thresholds, prefix: replaceVariables(valueOptions.prefix), suffix: replaceVariables(valueOptions.suffix), theme: config.theme, }); - return processSingleStatPanelData({ - panelData: panelData, - stat: valueOptions.stat, - }).map(stat => processor(stat.value)); + + return processTimeSeries({ + data, + nullValueMode: NullValueMode.Null, + }).map((series, index) => { + const value = stat !== 'name' ? series.stats[stat] : series.label; + return processor(value); + }); }; export class SingleStatPanel extends PureComponent> { @@ -49,14 +55,14 @@ export class SingleStatPanel extends PureComponent }; render() { - const { height, width, options, panelData, renderCounter } = this.props; + const { height, width, options, data, renderCounter } = this.props; return ( diff --git a/public/app/plugins/panel/table/module.ts b/public/app/plugins/panel/table/module.ts index 268f5aa7ac4..b7b3c0312a3 100644 --- a/public/app/plugins/panel/table/module.ts +++ b/public/app/plugins/panel/table/module.ts @@ -6,6 +6,7 @@ import { transformDataToTable } from './transformers'; import { tablePanelEditor } from './editor'; import { columnOptionsTab } from './column_options'; import { TableRenderer } from './renderer'; +import { isTableData } from '@grafana/ui'; class TablePanelCtrl extends MetricsPanelCtrl { static templateUrl = 'module.html'; @@ -104,7 +105,7 @@ class TablePanelCtrl extends MetricsPanelCtrl { // automatically correct transform mode based on data if (this.dataRaw && this.dataRaw.length) { - if (this.dataRaw[0].type === 'table') { + if (isTableData(this.dataRaw[0])) { this.panel.transform = 'table'; } else { if (this.dataRaw[0].type === 'docs') { diff --git a/public/app/plugins/panel/table2/TablePanel.tsx b/public/app/plugins/panel/table2/TablePanel.tsx index a7cd84f8ecb..70b652a8d2c 100644 --- a/public/app/plugins/panel/table2/TablePanel.tsx +++ b/public/app/plugins/panel/table2/TablePanel.tsx @@ -14,15 +14,15 @@ export class TablePanel extends Component { } render() { - const { panelData, options } = this.props; + const { data, options } = this.props; - if (!panelData || !panelData.tableData) { + if (data.length < 1) { return
No Table Data...
; } return ( - {theme => } + {theme =>
} ); }