mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -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);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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[];
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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} />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user