ryan 6 лет назад
Родитель
Сommit
0d8384e05d

+ 84 - 48
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<Options> {}
 
 interface State {
-  sortBy: string;
-  sortDirection: SortDirectionType;
-  sortedList: any[];
+  sortBy?: number; // but really is a number!
+  sortDirection?: SortDirectionType;
+  data: SortedTableData;
 }
 
 export class TablePanel extends Component<Props, State> {
@@ -22,55 +23,87 @@ export class TablePanel extends Component<Props, State> {
 
   constructor(props: Props) {
     super(props);
+
+    const { panelData, options, replaceVariables } = this.props;
+
     this.state = {
-      sortBy: 'XX',
-      sortDirection: 'ASC',
-      sortedList: [],
+      data: new SortedTableData(panelData.tableData),
     };
 
-    const { panelData, options, replaceVariables } = this.props;
-    const theme = null; // TODO?
+    this.renderer = new TableRenderer(options.styles, this.state.data, this._rowGetter, replaceVariables);
+  }
 
-    this.renderer = new TableRenderer(panelData.tableData, options, replaceVariables, theme);
+  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 will trigger sort via properties
+    console.log('SORT', sortBy, typeof sortBy, sortDirection);
 
-    // this.setState({sortBy, sortDirection, sortedList});
-    console.log('TODO, sort!', 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 <div>??{dataKey}??</div>;
     }
 
+    const isSorted = sortBy === dataKey;
+
+    console.log('header SORT', sortBy, isSorted);
+
     return (
       <div>
-        {col.text} {sortBy === dataKey && <SortIndicator sortDirection={sortDirection} />}
+        {col.text} {isSorted && <SortIndicator sortDirection={sortDirection} />}
       </div>
     );
   };
 
   _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<Props, State> {
     }
 
     return (
-      <Table
-        disableHeader={!showHeader}
-        headerHeight={30}
-        height={height}
-        overscanRowCount={10}
-        rowHeight={30}
-        rowGetter={this._rowGetter}
-        rowCount={tableData.rows.length}
-        sort={this._sort}
-        sortBy={sortBy}
-        sortDirection={sortDirection}
-        width={width}
-      >
-        {tableData.columns.map((col, index) => {
-          return (
-            <Column
-              key={index}
-              dataKey={index}
-              headerRenderer={this._headerRenderer}
-              cellRenderer={this._cellRenderer}
-              flexGrow={1}
-              width={60}
-            />
-          );
-        })}
-      </Table>
+      <ThemeContext.Consumer>
+        {(
+          theme // ??? { this.renderer.setTheme(theme) }
+        ) => (
+          <Table
+            disableHeader={!showHeader}
+            headerHeight={30}
+            height={height}
+            overscanRowCount={10}
+            rowHeight={30}
+            rowGetter={this._rowGetter}
+            rowCount={tableData.rows.length}
+            sort={this._sort}
+            width={width}
+          >
+            {tableData.columns.map((col, index) => {
+              return (
+                <Column
+                  key={index}
+                  dataKey={index}
+                  headerRenderer={this._headerRenderer}
+                  cellRenderer={this._cellRenderer}
+                  width={300}
+                />
+              );
+            })}
+          </Table>
+        )}
+      </ThemeContext.Consumer>
     );
   }
 }

+ 15 - 15
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] };
     }

+ 41 - 0
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;
+  }
+}

+ 5 - 2
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 <td> with <div>
@@ -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);

+ 0 - 11
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[];

+ 4 - 4
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;