mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Merge pull request #15864 from ryantxu/all-data-as-table
Use TableData for all data in react
This commit is contained in:
commit
abd8948478
@ -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: {},
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<T = any> {
|
||||
panelData: PanelData;
|
||||
data?: TableData[];
|
||||
timeRange: TimeRange;
|
||||
loading: LoadingState;
|
||||
options: T;
|
||||
@ -16,11 +16,6 @@ export interface PanelProps<T = any> {
|
||||
replaceVariables: InterpolateFunction;
|
||||
}
|
||||
|
||||
export interface PanelData {
|
||||
timeSeries?: TimeSeries[];
|
||||
tableData?: TableData;
|
||||
}
|
||||
|
||||
export interface PanelEditorProps<T = any> {
|
||||
options: T;
|
||||
onOptionsChange: (options: T) => void;
|
||||
|
@ -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",
|
||||
}
|
||||
`;
|
||||
|
@ -1,5 +1,5 @@
|
||||
export * from './processTimeSeries';
|
||||
export * from './singlestat';
|
||||
export * from './processTableData';
|
||||
export * from './valueFormats/valueFormats';
|
||||
export * from './colors';
|
||||
export * from './namedColorsPalette';
|
||||
|
@ -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([]);
|
||||
});
|
||||
});
|
||||
|
@ -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 = {
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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 [];
|
||||
}
|
@ -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;
|
||||
|
@ -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<Props, State> {
|
||||
@ -64,7 +63,6 @@ export class DataPanel extends Component<Props, State> {
|
||||
response: {
|
||||
data: [],
|
||||
},
|
||||
panelData: {},
|
||||
isFirstLoad: true,
|
||||
};
|
||||
}
|
||||
@ -149,7 +147,7 @@ export class DataPanel extends Component<Props, State> {
|
||||
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<Props, State> {
|
||||
}
|
||||
};
|
||||
|
||||
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<Props, State> {
|
||||
return (
|
||||
<>
|
||||
{loading === LoadingState.Loading && this.renderLoadingState()}
|
||||
{this.props.children({ loading, panelData })}
|
||||
{this.props.children({ loading, data })}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -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<Props, State> {
|
||||
}
|
||||
|
||||
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<Props, State> {
|
||||
<div className="panel-content">
|
||||
<PanelComponent
|
||||
loading={loading}
|
||||
panelData={panelData}
|
||||
data={data}
|
||||
timeRange={timeRange}
|
||||
options={panel.getOptions(plugin.exports.reactPanel.defaults)}
|
||||
width={width - 2 * config.theme.panelPadding.horizontal}
|
||||
@ -188,8 +188,8 @@ export class PanelChrome extends PureComponent<Props, State> {
|
||||
onDataResponse={this.onDataResponse}
|
||||
onError={this.onDataError}
|
||||
>
|
||||
{({ loading, panelData }) => {
|
||||
return this.renderPanelPlugin(loading, panelData, width, height);
|
||||
{({ loading, data }) => {
|
||||
return this.renderPanelPlugin(loading, data, width, height);
|
||||
}}
|
||||
</DataPanel>
|
||||
) : (
|
||||
|
@ -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());
|
||||
};
|
||||
|
@ -32,14 +32,14 @@ export class BarGaugePanel extends PureComponent<PanelProps<BarGaugeOptions>> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { height, width, options, panelData, renderCounter } = this.props;
|
||||
const { height, width, options, data, renderCounter } = this.props;
|
||||
return (
|
||||
<ProcessedValuesRepeater
|
||||
getProcessedValues={this.getProcessedValues}
|
||||
renderValue={this.renderValue}
|
||||
width={width}
|
||||
height={height}
|
||||
source={panelData}
|
||||
source={data}
|
||||
renderCounter={renderCounter}
|
||||
orientation={options.orientation}
|
||||
/>
|
||||
|
@ -37,14 +37,14 @@ export class GaugePanel extends PureComponent<PanelProps<GaugeOptions>> {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { height, width, options, panelData, renderCounter } = this.props;
|
||||
const { height, width, options, data, renderCounter } = this.props;
|
||||
return (
|
||||
<ProcessedValuesRepeater
|
||||
getProcessedValues={this.getProcessedValues}
|
||||
renderValue={this.renderValue}
|
||||
width={width}
|
||||
height={height}
|
||||
source={panelData}
|
||||
source={data}
|
||||
renderCounter={renderCounter}
|
||||
orientation={options.orientation}
|
||||
/>
|
||||
|
@ -16,13 +16,13 @@ interface Props extends PanelProps<Options> {}
|
||||
|
||||
export class GraphPanel extends PureComponent<Props> {
|
||||
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,
|
||||
});
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<SingleStatBaseOptions>): 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<PanelProps<SingleStatOptions>> {
|
||||
@ -49,14 +55,14 @@ export class SingleStatPanel extends PureComponent<PanelProps<SingleStatOptions>
|
||||
};
|
||||
|
||||
render() {
|
||||
const { height, width, options, panelData, renderCounter } = this.props;
|
||||
const { height, width, options, data, renderCounter } = this.props;
|
||||
return (
|
||||
<ProcessedValuesRepeater
|
||||
getProcessedValues={this.getProcessedValues}
|
||||
renderValue={this.renderValue}
|
||||
width={width}
|
||||
height={height}
|
||||
source={panelData}
|
||||
source={data}
|
||||
renderCounter={renderCounter}
|
||||
orientation={options.orientation}
|
||||
/>
|
||||
|
@ -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') {
|
||||
|
@ -14,15 +14,15 @@ export class TablePanel extends Component<Props> {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { panelData, options } = this.props;
|
||||
const { data, options } = this.props;
|
||||
|
||||
if (!panelData || !panelData.tableData) {
|
||||
if (data.length < 1) {
|
||||
return <div>No Table Data...</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<ThemeContext.Consumer>
|
||||
{theme => <Table {...this.props} {...options} theme={theme} data={panelData.tableData} />}
|
||||
{theme => <Table {...this.props} {...options} theme={theme} data={data[0]} />}
|
||||
</ThemeContext.Consumer>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user