From 0d8384e05d09a08eb077bca2e577a44cfec1f5e0 Mon Sep 17 00:00:00 2001 From: ryan Date: Wed, 6 Mar 2019 14:39:04 -0800 Subject: [PATCH] sortable class --- .../app/plugins/panel/table2/TablePanel.tsx | 134 +++++++++++------- public/app/plugins/panel/table2/renderer.tsx | 30 ++-- public/app/plugins/panel/table2/sortable.tsx | 41 ++++++ .../panel/table2/specs/renderer.test.ts | 7 +- public/app/plugins/panel/table2/types.ts | 11 -- public/sass/components/_panel_table2.scss | 8 +- 6 files changed, 150 insertions(+), 81 deletions(-) create mode 100644 public/app/plugins/panel/table2/sortable.tsx diff --git a/public/app/plugins/panel/table2/TablePanel.tsx b/public/app/plugins/panel/table2/TablePanel.tsx index 21af18024ba..cf9ddd83094 100644 --- a/public/app/plugins/panel/table2/TablePanel.tsx +++ b/public/app/plugins/panel/table2/TablePanel.tsx @@ -3,18 +3,19 @@ import _ from 'lodash'; import React, { Component, ReactNode } from 'react'; // Types -import { PanelProps } from '@grafana/ui/src/types'; +import { PanelProps, ThemeContext } from '@grafana/ui'; import { Options } from './types'; import { Table, SortDirectionType, SortIndicator, Column, TableHeaderProps, TableCellProps } from 'react-virtualized'; import { TableRenderer } from './renderer'; +import { SortedTableData } from './sortable'; interface Props extends PanelProps {} interface State { - sortBy: string; - sortDirection: SortDirectionType; - sortedList: any[]; + sortBy?: number; // but really is a number! + sortDirection?: SortDirectionType; + data: SortedTableData; } export class TablePanel extends Component { @@ -22,55 +23,87 @@ export class TablePanel extends Component { constructor(props: Props) { super(props); - this.state = { - sortBy: 'XX', - sortDirection: 'ASC', - sortedList: [], - }; const { panelData, options, replaceVariables } = this.props; - const theme = null; // TODO? - this.renderer = new TableRenderer(panelData.tableData, options, replaceVariables, theme); + this.state = { + data: new SortedTableData(panelData.tableData), + }; + + this.renderer = new TableRenderer(options.styles, this.state.data, this._rowGetter, replaceVariables); + } + + componentDidUpdate(prevProps: Props, prevState: State) { + const { panelData, options } = this.props; + const { sortBy, sortDirection } = this.state; + + console.log('componentDidUpdate', this.props); + + // Update the renderer if options change + if (options !== prevProps.options) { + console.log('Options Changed, update renderer', options); + this.renderer = new TableRenderer(options.styles, this.state.data, this._rowGetter, this.props.replaceVariables); + } + + // Update the data when data or sort changes + if (panelData !== prevProps.panelData || sortBy !== prevState.sortBy || sortDirection !== prevState.sortDirection) { + const data = new SortedTableData(panelData.tableData, sortBy, sortDirection === 'DESC'); + this.setState({ data }); + console.log('Data Changed, update', data); + } } _rowGetter = ({ index }) => { - return this.props.panelData.tableData.rows[index]; + return this.state.data.getRow(index); }; - _sort = ({ sortBy, sortDirection }) => { - // const sortedList = this._sortList({sortBy, sortDirection}); + _sort = ({ sortBy }) => { + let sortDirection = this.state.sortDirection; + if (sortBy !== this.state.sortBy) { + sortDirection = 'DESC'; + } else if (sortDirection === 'DESC') { + sortDirection = 'ASC'; + } else { + sortBy = null; + } - // this.setState({sortBy, sortDirection, sortedList}); - console.log('TODO, sort!', sortBy, sortDirection); + // This will trigger sort via properties + console.log('SORT', sortBy, typeof sortBy, sortDirection); + + this.setState({ sortBy, sortDirection }); }; _headerRenderer = (header: TableHeaderProps): ReactNode => { - const { sortBy, dataKey, sortDirection } = header; - const tableData = this.props.panelData.tableData!; - const col = tableData.columns[dataKey]; + const dataKey = header.dataKey as any; // types say string, but it is number? + const { data, sortBy, sortDirection } = this.state; + + const col = data.getInfo()[dataKey]; if (!col) { return
??{dataKey}??
; } + const isSorted = sortBy === dataKey; + + console.log('header SORT', sortBy, isSorted); + return (
- {col.text} {sortBy === dataKey && } + {col.text} {isSorted && }
); }; _cellRenderer = (cell: TableCellProps) => { const { columnIndex, rowIndex } = cell; - const tableData = this.props.panelData.tableData!; - const val = tableData.rows[rowIndex][columnIndex]; + const row = this.state.data.getRow(rowIndex); + const val = row[columnIndex]; return this.renderer.renderCell(columnIndex, rowIndex, val); }; render() { const { panelData, width, height, options } = this.props; const { showHeader } = options; - const { sortBy, sortDirection } = this.state; + // const { sortBy, sortDirection } = this.state; const { tableData } = panelData; if (!tableData || tableData.rows.length < 1) { @@ -78,32 +111,35 @@ export class TablePanel extends Component { } return ( - - {tableData.columns.map((col, index) => { - return ( - - ); - })} -
+ + {( + theme // ??? { this.renderer.setTheme(theme) } + ) => ( + + {tableData.columns.map((col, index) => { + return ( + + ); + })} +
+ )} +
); } } diff --git a/public/app/plugins/panel/table2/renderer.tsx b/public/app/plugins/panel/table2/renderer.tsx index f5779b13f0d..a663a5b1a05 100644 --- a/public/app/plugins/panel/table2/renderer.tsx +++ b/public/app/plugins/panel/table2/renderer.tsx @@ -7,14 +7,10 @@ import { sanitize } from 'app/core/utils/text'; // Types import kbn from 'app/core/utils/kbn'; -import { - getValueFormat, - getColorFromHexRgbOrName, - GrafanaThemeType, - InterpolateFunction, - TableData, -} from '@grafana/ui'; -import { Options, Style } from './types'; +import { getValueFormat, getColorFromHexRgbOrName, GrafanaThemeType, InterpolateFunction } from '@grafana/ui'; +import { Style } from './types'; +import { SortedTableData } from './sortable'; +import { Index } from 'react-virtualized'; type CellFormatter = (v: any, style: Style) => string; @@ -32,12 +28,13 @@ export class TableRenderer { columns: ColumnInfo[]; colorState: any; + theme?: GrafanaThemeType; constructor( - private data: TableData, - options: Options, - private replaceVariables: InterpolateFunction, - private theme?: GrafanaThemeType + styles: Style[], + data: SortedTableData, + private rowGetter: (info: Index) => any[], // matches the table rowGetter + private replaceVariables: InterpolateFunction ) { this.colorState = {}; @@ -45,9 +42,8 @@ export class TableRenderer { this.columns = []; return; } - const { styles } = options; - this.columns = data.columns.map((col, index) => { + this.columns = data.getInfo().map((col, index) => { let title = col.text; let style: Style = null; @@ -72,6 +68,10 @@ export class TableRenderer { }); } + setTheme(theme: GrafanaThemeType) { + this.theme = theme; + } + getColorForValue(value, style: Style) { if (!style.thresholds) { return null; @@ -222,7 +222,7 @@ export class TableRenderer { renderRowVariables(rowIndex: number) { const scopedVars = {}; - const row = this.data.rows[rowIndex]; + const row = this.rowGetter({ index: rowIndex }); for (let i = 0; i < row.length; i++) { scopedVars[`__cell_${i}`] = { value: row[i] }; } diff --git a/public/app/plugins/panel/table2/sortable.tsx b/public/app/plugins/panel/table2/sortable.tsx new file mode 100644 index 00000000000..90e4fd961cb --- /dev/null +++ b/public/app/plugins/panel/table2/sortable.tsx @@ -0,0 +1,41 @@ +// Libraries +import _ from 'lodash'; + +import { TableData } from '@grafana/ui'; + +export class SortedTableData { + rows: any[]; + + constructor(private data: TableData, sortIndex?: number, reverse?: boolean) { + if (_.isNumber(sortIndex)) { + // Make a copy of all the rows + this.rows = this.data.rows.map((row, index) => { + return row; + }); + this.rows.sort((a, b) => { + a = a[sortIndex]; + b = b[sortIndex]; + // Sort null or undefined separately from comparable values + return +(a == null) - +(b == null) || +(a > b) || -(a < b); + }); + + if (reverse) { + this.rows.reverse(); + } + } else { + this.rows = data.rows; + } + } + + getInfo(): any[] { + return this.data.columns; + } + + getRow(index: number): any[] { + return this.rows[index]; + } + + getCount(): number { + return this.rows.length; + } +} diff --git a/public/app/plugins/panel/table2/specs/renderer.test.ts b/public/app/plugins/panel/table2/specs/renderer.test.ts index 038b7a0f744..f826ec20649 100644 --- a/public/app/plugins/panel/table2/specs/renderer.test.ts +++ b/public/app/plugins/panel/table2/specs/renderer.test.ts @@ -6,6 +6,7 @@ import { Options } from '../types'; import { PanelProps, LoadingState } from '@grafana/ui/src/types'; import moment from 'moment'; import { TableRenderer } from '../renderer'; +import { SortedTableData } from '../sortable'; // TODO: this is commented out with *x* describe! // Essentially all the elements need to replace the with
@@ -203,8 +204,10 @@ xdescribe('when rendering table', () => { renderCounter: 1, options: panel, }; - const theme = null; - const renderer = new TableRenderer(table, panel, props.replaceVariables, theme); //panel, table, 'utc', sanitize, templateSrv); + const data = new SortedTableData(table); + const rowGetter = ({ index }) => data.getRow(index); + const renderer = new TableRenderer(panel.styles, data, rowGetter, props.replaceVariables); + renderer.setTheme(null); it('time column should be formated', () => { const html = renderer.renderCell(0, 0, 1388556366666); diff --git a/public/app/plugins/panel/table2/types.ts b/public/app/plugins/panel/table2/types.ts index 6ff384b06e0..c0a3b2c8561 100644 --- a/public/app/plugins/panel/table2/types.ts +++ b/public/app/plugins/panel/table2/types.ts @@ -22,17 +22,6 @@ export interface Style { preserveFormat?: boolean; } -export type CellFormatter = (v: any, style: Style) => string; - -export interface ColumnInfo { - header: string; - accessor: string; // the field name - style?: Style; - hidden?: boolean; - formatter: CellFormatter; - filterable?: boolean; -} - export interface Options { showHeader: boolean; styles: Style[]; diff --git a/public/sass/components/_panel_table2.scss b/public/sass/components/_panel_table2.scss index 891588a8687..b0aa2d6b742 100644 --- a/public/sass/components/_panel_table2.scss +++ b/public/sass/components/_panel_table2.scss @@ -1,8 +1,8 @@ -.ReactVirtualized__Table { -} +// .ReactVirtualized__Table { +// } -.ReactVirtualized__Table__Grid { -} +// .ReactVirtualized__Table__Grid { +// } .ReactVirtualized__Table__headerRow { font-weight: 700;