Table: Component progress & custom FieldConfig options (#21231)

* Table: Set & use field display processor

* Use applyFieldOverrides outside in story instead

* Change types a bit

* Table: Move to flexible layout

* Simplest possible custom field option

* Skip default column

* Added textAlign

* Explore: Set display processor for table data frame

* Fixed storybook

* Refactoring

* Progress on cell display mode

* Major progress

* Progress & refactoring

* Fixes

* Updated tests

* Added more tests

* Table: Progress on cell style customization

* Restored testdata random walk table scenario

* put back unrelated change

* remove unused things

* Updated table story

* Renamed property

Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
This commit is contained in:
Torkel Ödegaard
2019-12-23 06:22:54 +01:00
committed by GitHub
parent 8d537b7afb
commit 3347b45a95
43 changed files with 750 additions and 1918 deletions

View File

@@ -130,8 +130,15 @@ describe('ResultProcessor', () => {
describe('when calling getTableResult', () => {
it('then it should return correct table result', () => {
const { resultProcessor } = testContext();
const theResult = resultProcessor.getTableResult();
const resultDataFrame = toDataFrame(
let theResult = resultProcessor.getTableResult();
expect(theResult.fields[0].name).toEqual('value');
expect(theResult.fields[1].name).toEqual('time');
expect(theResult.fields[2].name).toEqual('message');
expect(theResult.fields[1].display).not.toBeNull();
expect(theResult.length).toBe(3);
// Same data though a DataFrame
theResult = toDataFrame(
new TableModel({
columns: [
{ text: 'value', type: 'number' },
@@ -146,8 +153,11 @@ describe('ResultProcessor', () => {
type: 'table',
})
);
expect(theResult).toEqual(resultDataFrame);
expect(theResult.fields[0].name).toEqual('value');
expect(theResult.fields[1].name).toEqual('time');
expect(theResult.fields[2].name).toEqual('message');
expect(theResult.fields[1].display).not.toBeNull();
expect(theResult.length).toBe(3);
});
});

View File

@@ -1,9 +1,18 @@
import { LogsModel, GraphSeriesXY, DataFrame, FieldType, TimeZone, toDataFrame } from '@grafana/data';
import {
LogsModel,
GraphSeriesXY,
DataFrame,
FieldType,
TimeZone,
toDataFrame,
getDisplayProcessor,
} from '@grafana/data';
import { ExploreItemState, ExploreMode } from 'app/types/explore';
import TableModel, { mergeTablesIntoModel } from 'app/core/table_model';
import { sortLogsResult, refreshIntervalToSortOrder } from 'app/core/utils/explore';
import { dataFrameToLogsModel } from 'app/core/logs_model';
import { getGraphSeriesModel } from 'app/plugins/panel/graph2/getGraphSeriesModel';
import { config } from 'app/core/config';
export class ResultProcessor {
constructor(
@@ -75,7 +84,17 @@ export class ResultProcessor {
});
const mergedTable = mergeTablesIntoModel(new TableModel(), ...tables);
return toDataFrame(mergedTable);
const data = toDataFrame(mergedTable);
// set display processor
for (const field of data.fields) {
field.display = getDisplayProcessor({
config: field.config,
theme: config.theme,
});
}
return data;
}
getLogsResult(): LogsModel | null {

View File

@@ -1,20 +1,20 @@
import { SingleStatBaseOptions } from '@grafana/ui';
import { SingleStatBaseOptions, BarGaugeDisplayMode } from '@grafana/ui';
import { standardGaugeFieldOptions } from '../gauge/types';
import { VizOrientation, SelectableValue } from '@grafana/data';
export interface BarGaugeOptions extends SingleStatBaseOptions {
displayMode: 'basic' | 'lcd' | 'gradient';
displayMode: BarGaugeDisplayMode;
showUnfilled: boolean;
}
export const displayModes: Array<SelectableValue<string>> = [
{ value: 'gradient', label: 'Gradient' },
{ value: 'lcd', label: 'Retro LCD' },
{ value: 'basic', label: 'Basic' },
{ value: BarGaugeDisplayMode.Gradient, label: 'Gradient' },
{ value: BarGaugeDisplayMode.Lcd, label: 'Retro LCD' },
{ value: BarGaugeDisplayMode.Basic, label: 'Basic' },
];
export const defaults: BarGaugeOptions = {
displayMode: 'lcd',
displayMode: BarGaugeDisplayMode.Lcd,
orientation: VizOrientation.Horizontal,
fieldOptions: standardGaugeFieldOptions,
showUnfilled: true,

View File

@@ -11,9 +11,8 @@ import {
stringToJsRegex,
unEscapeStringFromRegex,
} from '@grafana/data';
import { ColumnStyle } from '@grafana/ui/src/components/Table/TableCellBuilder';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { ColumnRender, TableRenderModel } from './types';
import { ColumnRender, TableRenderModel, ColumnStyle } from './types';
export class TableRenderer {
formatters: any[];

View File

@@ -1,6 +1,5 @@
import TableModel from 'app/core/table_model';
import { Column } from '@grafana/data';
import { ColumnStyle } from '@grafana/ui/src/components/Table/TableCellBuilder';
export interface TableTransform {
description: string;
@@ -18,3 +17,26 @@ export interface TableRenderModel {
columns: ColumnRender[];
rows: any[][];
}
export interface ColumnStyle {
pattern: string;
alias?: string;
colorMode?: 'cell' | 'value';
colors?: any[];
decimals?: number;
thresholds?: any[];
type?: 'date' | 'number' | 'string' | 'hidden';
unit?: string;
dateFormat?: string;
sanitize?: boolean; // not used in react
mappingType?: any;
valueMaps?: any;
rangeMaps?: any;
link?: any;
linkUrl?: any;
linkTooltip?: any;
linkTargetBlank?: boolean;
preserveFormat?: boolean;
}

View File

@@ -3,13 +3,13 @@ import React, { Component } from 'react';
// Types
import { Table } from '@grafana/ui';
import { PanelProps } from '@grafana/data';
import { PanelProps, applyFieldOverrides } from '@grafana/data';
import { Options } from './types';
import { config } from 'app/core/config';
interface Props extends PanelProps<Options> {}
// So that the table does not go all the way to the edge of the panel chrome
const paddingBottom = 35;
const paddingBottom = 16;
export class TablePanel extends Component<Props> {
constructor(props: Props) {
@@ -17,12 +17,19 @@ export class TablePanel extends Component<Props> {
}
render() {
const { data, height, width } = this.props;
const { data, height, width, replaceVariables, options } = this.props;
if (data.series.length < 1) {
return <div>No Table Data...</div>;
}
return <Table height={height - paddingBottom} width={width} data={data.series[0]} />;
const dataProcessed = applyFieldOverrides({
data: data.series,
fieldOptions: options.fieldOptions,
theme: config.theme,
replaceVariables,
})[0];
return <Table height={height - paddingBottom} width={width} data={dataProcessed} />;
}
}

View File

@@ -4,7 +4,7 @@ import React, { PureComponent } from 'react';
// Types
import { PanelEditorProps } from '@grafana/data';
import { Switch, FormField } from '@grafana/ui';
import { Switch } from '@grafana/ui';
import { Options } from './types';
export class TablePanelEditor extends PureComponent<PanelEditorProps<Options>> {
@@ -12,43 +12,14 @@ export class TablePanelEditor extends PureComponent<PanelEditorProps<Options>> {
this.props.onOptionsChange({ ...this.props.options, showHeader: !this.props.options.showHeader });
};
onToggleFixedHeader = () => {
this.props.onOptionsChange({ ...this.props.options, fixedHeader: !this.props.options.fixedHeader });
};
onToggleRotate = () => {
this.props.onOptionsChange({ ...this.props.options, rotate: !this.props.options.rotate });
};
onFixedColumnsChange = ({ target }: any) => {
this.props.onOptionsChange({ ...this.props.options, fixedColumns: target.value });
};
render() {
const { showHeader, fixedHeader, rotate, fixedColumns } = this.props.options;
const { showHeader } = this.props.options;
return (
<div>
<div className="section gf-form-group">
<h5 className="section-heading">Header</h5>
<Switch label="Show" labelClass="width-6" checked={showHeader} onChange={this.onToggleShowHeader} />
<Switch label="Fixed" labelClass="width-6" checked={fixedHeader} onChange={this.onToggleFixedHeader} />
</div>
<div className="section gf-form-group">
<h5 className="section-heading">Display</h5>
<Switch label="Rotate" labelClass="width-8" checked={rotate} onChange={this.onToggleRotate} />
<FormField
label="Fixed Columns"
labelWidth={8}
inputWidth={4}
type="number"
step="1"
min="0"
max="100"
onChange={this.onFixedColumnsChange}
value={fixedColumns}
/>
</div>
</div>
);

View File

@@ -4,4 +4,7 @@ import { TablePanelEditor } from './TablePanelEditor';
import { TablePanel } from './TablePanel';
import { Options, defaults } from './types';
export const plugin = new PanelPlugin<Options>(TablePanel).setDefaults(defaults).setEditor(TablePanelEditor);
export const plugin = new PanelPlugin<Options>(TablePanel)
.setNoPadding()
.setDefaults(defaults)
.setEditor(TablePanelEditor);

View File

@@ -1,34 +1,14 @@
import { ColumnStyle } from '@grafana/ui/src/components/Table/TableCellBuilder';
import { FieldConfigSource } from '@grafana/data';
export interface Options {
fieldOptions: FieldConfigSource;
showHeader: boolean;
fixedHeader: boolean;
fixedColumns: number;
rotate: boolean;
styles: ColumnStyle[];
}
export const defaults: Options = {
fieldOptions: {
defaults: {},
overrides: [],
},
showHeader: true,
fixedHeader: true,
fixedColumns: 0,
rotate: false,
styles: [
{
type: 'date',
pattern: 'Time',
alias: 'Time',
dateFormat: 'YYYY-MM-DD HH:mm:ss',
},
{
unit: 'short',
type: 'number',
alias: '',
decimals: 2,
colors: ['rgba(245, 54, 54, 0.9)', 'rgba(237, 129, 40, 0.89)', 'rgba(50, 172, 45, 0.97)'],
pattern: '/.*/',
thresholds: [],
},
],
};