ryan 6 роки тому
батько
коміт
d7b1fd75e3

+ 186 - 0
packages/grafana-ui/src/components/DataTable/DataTable.tsx

@@ -0,0 +1,186 @@
+// Libraries
+import React, { Component, ReactNode } from 'react';
+import {
+  Table,
+  SortDirectionType,
+  SortIndicator,
+  Column,
+  TableHeaderProps,
+  TableCellProps,
+  Index,
+} from 'react-virtualized';
+import { Themeable } from '../../types/theme';
+
+import { sortTableData } from '../../utils/processTimeSeries';
+
+// Types
+import { TableData, InterpolateFunction } from '../../types/index';
+import { TableRenderer } from './renderer';
+
+// Made to match the existing (untyped) settings in the angular table
+export interface ColumnStyle {
+  pattern?: string;
+
+  alias?: string;
+  colorMode?: string;
+  colors?: any[];
+  decimals?: number;
+  thresholds?: any[];
+  type?: 'date' | 'number' | 'string' | 'hidden';
+  unit?: string;
+  dateFormat?: string;
+  sanitize?: boolean;
+  mappingType?: any;
+  valueMaps?: any;
+  rangeMaps?: any;
+
+  link?: any;
+  linkUrl?: any;
+  linkTooltip?: any;
+  linkTargetBlank?: boolean;
+
+  preserveFormat?: boolean;
+}
+
+interface Props extends Themeable {
+  data?: TableData;
+  showHeader: boolean;
+  styles: ColumnStyle[];
+  replaceVariables: InterpolateFunction;
+  width: number;
+  height: number;
+}
+
+interface State {
+  sortBy?: number;
+  sortDirection?: SortDirectionType;
+  data?: TableData;
+}
+
+export class DataTable extends Component<Props, State> {
+  renderer: TableRenderer;
+
+  static defaultProps = {
+    showHeader: true,
+  };
+
+  constructor(props: Props) {
+    super(props);
+
+    this.state = {
+      data: props.data,
+    };
+
+    this.renderer = this.createRenderer();
+  }
+
+  componentDidUpdate(prevProps: Props, prevState: State) {
+    const { data, styles } = this.props;
+    const { sortBy, sortDirection } = this.state;
+    const dataChanged = data !== prevProps.data;
+
+    // Update the renderer if options change
+    if (dataChanged || styles !== prevProps.styles) {
+      this.renderer = this.createRenderer();
+    }
+
+    // Update the data when data or sort changes
+    if (dataChanged || sortBy !== prevState.sortBy || sortDirection !== prevState.sortDirection) {
+      this.setState({ data: sortTableData(data, sortBy, sortDirection === 'DESC') });
+    }
+  }
+
+  // styles: ColumnStyle[],
+  // schema: Column[],
+  // rowGetter: (info: Index) => any[], // matches the table rowGetter
+  // replaceVariables: InterpolateFunction,
+  // isUTC?: boolean, // TODO? get UTC from props?
+  // theme?: GrafanaThemeType | undefined,
+
+  createRenderer(): TableRenderer {
+    const { styles, replaceVariables, theme } = this.props;
+    const { data } = this.state;
+
+    return new TableRenderer({
+      styles,
+      schema: data ? data.columns : [],
+      rowGetter: this.rowGetter,
+      replaceVariables,
+      isUTC: false,
+      theme: theme.type,
+    });
+  }
+
+  rowGetter = ({ index }: Index) => {
+    return this.state.data!.rows[index];
+  };
+
+  doSort = (info: any) => {
+    let dir = info.sortDirection;
+    let sort = info.sortBy;
+    if (sort !== this.state.sortBy) {
+      dir = 'DESC';
+    } else if (dir === 'DESC') {
+      dir = 'ASC';
+    } else {
+      sort = null;
+    }
+    this.setState({ sortBy: sort, sortDirection: dir });
+  };
+
+  headerRenderer = (header: TableHeaderProps): ReactNode => {
+    const dataKey = header.dataKey as any; // types say string, but it is number!
+    const { data, sortBy, sortDirection } = this.state;
+    const col = data!.columns[dataKey];
+
+    return (
+      <div>
+        {col.text} {sortBy === dataKey && <SortIndicator sortDirection={sortDirection} />}
+      </div>
+    );
+  };
+
+  cellRenderer = (cell: TableCellProps) => {
+    const { columnIndex, rowIndex } = cell;
+    const row = this.state.data!.rows[rowIndex];
+    const val = row[columnIndex];
+    return this.renderer.renderCell(columnIndex, rowIndex, val);
+  };
+
+  render() {
+    const { width, height, showHeader } = this.props;
+    const { data } = this.props;
+    if (!data) {
+      return <div>NO Data</div>;
+    }
+    return (
+      <Table
+        disableHeader={!showHeader}
+        headerHeight={30}
+        height={height}
+        overscanRowCount={10}
+        rowHeight={30}
+        rowGetter={this.rowGetter}
+        rowCount={data.rows.length}
+        sort={this.doSort}
+        width={width}
+      >
+        {data.columns.map((col, index) => {
+          return (
+            <Column
+              key={index}
+              dataKey={index}
+              headerRenderer={this.headerRenderer}
+              cellRenderer={this.cellRenderer}
+              width={150}
+              minWidth={50}
+              flexGrow={1}
+            />
+          );
+        })}
+      </Table>
+    );
+  }
+}
+
+export default DataTable;

+ 155 - 172
public/app/plugins/panel/table2/specs/renderer.test.ts → packages/grafana-ui/src/components/DataTable/renderer.test.ts

@@ -2,10 +2,11 @@ import _ from 'lodash';
 import TableModel from 'app/core/table_model';
 
 import { getColorDefinitionByName } from '@grafana/ui';
-import { Options } from '../types';
-import { PanelProps, LoadingState } from '@grafana/ui/src/types';
+import { ScopedVars } from '@grafana/ui/src/types';
 import moment from 'moment';
-import { TableRenderer } from '../renderer';
+import { TableRenderer } from './renderer';
+import { Index } from 'react-virtualized';
+import { ColumnStyle } from './DataTable';
 
 // TODO: this is commented out with *x* describe!
 // Essentially all the elements need to replace the <td> with <div>
@@ -33,179 +34,161 @@ xdescribe('when rendering table', () => {
       [1388556366666, 1230, 40, undefined, '', '', 'my.host.com', 'host1', ['value1', 'value2'], 1, 2, 1, 2],
     ];
 
-    const panel: Options = {
-      showHeader: true,
-      pageSize: 10,
-      styles: [
-        {
-          pattern: 'Time',
-          type: 'date',
-          alias: 'Timestamp',
-        },
-        {
-          pattern: '/(Val)ue/',
-          type: 'number',
-          unit: 'ms',
-          decimals: 3,
-          alias: '$1',
-        },
-        {
-          pattern: 'Colored',
-          type: 'number',
-          unit: 'none',
-          decimals: 1,
-          colorMode: 'value',
-          thresholds: [50, 80],
-          colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'],
-        },
-        {
-          pattern: 'String',
-          type: 'string',
-        },
-        {
-          pattern: 'String',
-          type: 'string',
-        },
-        {
-          pattern: 'United',
-          type: 'number',
-          unit: 'ms',
-          decimals: 2,
-        },
-        {
-          pattern: 'Sanitized',
-          type: 'string',
-          sanitize: true,
-        },
-        {
-          pattern: 'Link',
-          type: 'string',
-          link: true,
-          linkUrl: '/dashboard?param=$__cell&param_1=$__cell_1&param_2=$__cell_2',
-          linkTooltip: '$__cell $__cell_1 $__cell_6',
-          linkTargetBlank: true,
-        },
-        {
-          pattern: 'Array',
-          type: 'number',
-          unit: 'ms',
-          decimals: 3,
-        },
-        {
-          pattern: 'Mapping',
-          type: 'string',
-          mappingType: 1,
-          valueMaps: [
-            {
-              value: '1',
-              text: 'on',
-            },
-            {
-              value: '0',
-              text: 'off',
-            },
-            {
-              value: 'HELLO WORLD',
-              text: 'HELLO GRAFANA',
-            },
-            {
-              value: 'value1, value2',
-              text: 'value3, value4',
-            },
-          ],
-        },
-        {
-          pattern: 'RangeMapping',
-          type: 'string',
-          mappingType: 2,
-          rangeMaps: [
-            {
-              from: '1',
-              to: '3',
-              text: 'on',
-            },
-            {
-              from: '3',
-              to: '6',
-              text: 'off',
-            },
-          ],
-        },
-        {
-          pattern: 'MappingColored',
-          type: 'string',
-          mappingType: 1,
-          valueMaps: [
-            {
-              value: '1',
-              text: 'on',
-            },
-            {
-              value: '0',
-              text: 'off',
-            },
-          ],
-          colorMode: 'value',
-          thresholds: [1, 2],
-          colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'],
-        },
-        {
-          pattern: 'RangeMappingColored',
-          type: 'string',
-          mappingType: 2,
-          rangeMaps: [
-            {
-              from: '1',
-              to: '3',
-              text: 'on',
-            },
-            {
-              from: '3',
-              to: '6',
-              text: 'off',
-            },
-          ],
-          colorMode: 'value',
-          thresholds: [2, 5],
-          colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'],
-        },
-      ],
-    };
+    const styles: ColumnStyle[] = [
+      {
+        pattern: 'Time',
+        type: 'date',
+        alias: 'Timestamp',
+      },
+      {
+        pattern: '/(Val)ue/',
+        type: 'number',
+        unit: 'ms',
+        decimals: 3,
+        alias: '$1',
+      },
+      {
+        pattern: 'Colored',
+        type: 'number',
+        unit: 'none',
+        decimals: 1,
+        colorMode: 'value',
+        thresholds: [50, 80],
+        colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'],
+      },
+      {
+        pattern: 'String',
+        type: 'string',
+      },
+      {
+        pattern: 'String',
+        type: 'string',
+      },
+      {
+        pattern: 'United',
+        type: 'number',
+        unit: 'ms',
+        decimals: 2,
+      },
+      {
+        pattern: 'Sanitized',
+        type: 'string',
+        sanitize: true,
+      },
+      {
+        pattern: 'Link',
+        type: 'string',
+        link: true,
+        linkUrl: '/dashboard?param=$__cell&param_1=$__cell_1&param_2=$__cell_2',
+        linkTooltip: '$__cell $__cell_1 $__cell_6',
+        linkTargetBlank: true,
+      },
+      {
+        pattern: 'Array',
+        type: 'number',
+        unit: 'ms',
+        decimals: 3,
+      },
+      {
+        pattern: 'Mapping',
+        type: 'string',
+        mappingType: 1,
+        valueMaps: [
+          {
+            value: '1',
+            text: 'on',
+          },
+          {
+            value: '0',
+            text: 'off',
+          },
+          {
+            value: 'HELLO WORLD',
+            text: 'HELLO GRAFANA',
+          },
+          {
+            value: 'value1, value2',
+            text: 'value3, value4',
+          },
+        ],
+      },
+      {
+        pattern: 'RangeMapping',
+        type: 'string',
+        mappingType: 2,
+        rangeMaps: [
+          {
+            from: '1',
+            to: '3',
+            text: 'on',
+          },
+          {
+            from: '3',
+            to: '6',
+            text: 'off',
+          },
+        ],
+      },
+      {
+        pattern: 'MappingColored',
+        type: 'string',
+        mappingType: 1,
+        valueMaps: [
+          {
+            value: '1',
+            text: 'on',
+          },
+          {
+            value: '0',
+            text: 'off',
+          },
+        ],
+        colorMode: 'value',
+        thresholds: [1, 2],
+        colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'],
+      },
+      {
+        pattern: 'RangeMappingColored',
+        type: 'string',
+        mappingType: 2,
+        rangeMaps: [
+          {
+            from: '1',
+            to: '3',
+            text: 'on',
+          },
+          {
+            from: '3',
+            to: '6',
+            text: 'off',
+          },
+        ],
+        colorMode: 'value',
+        thresholds: [2, 5],
+        colors: ['#00ff00', SemiDarkOrange.name, 'rgb(1,0,0)'],
+      },
+    ];
 
     // const sanitize = value => {
     //   return 'sanitized';
     // };
 
-    const props: PanelProps<Options> = {
-      panelData: {
-        tableData: table,
-      },
-      width: 100,
-      height: 100,
-      timeRange: {
-        from: moment(),
-        to: moment(),
-        raw: {
-          from: moment(),
-          to: moment(),
-        },
-      },
-      loading: LoadingState.Done,
-      replaceVariables: (value, scopedVars) => {
-        if (scopedVars) {
-          // For testing variables replacement in link
-          _.each(scopedVars, (val, key) => {
-            value = value.replace('$' + key, val.value);
-          });
-        }
-        return value;
-      },
-      renderCounter: 1,
-      options: panel,
+    const replaceVariables = (value: any, scopedVars: ScopedVars | undefined) => {
+      if (scopedVars) {
+        // For testing variables replacement in link
+        _.each(scopedVars, (val, key) => {
+          value = value.replace('$' + key, val.value);
+        });
+      }
+      return value;
     };
-    const rowGetter = ({ index }) => table.rows[index];
-    const renderer = new TableRenderer(panel.styles, table.columns, rowGetter, props.replaceVariables);
-    renderer.setTheme(null);
+    const rowGetter = ({ index }: Index) => table.rows[index];
+    const renderer = new TableRenderer({
+      styles,
+      schema: table.columns,
+      rowGetter,
+      replaceVariables,
+    });
 
     it('time column should be formated', () => {
       const html = renderer.renderCell(0, 0, 1388556366666);
@@ -314,7 +297,7 @@ xdescribe('when rendering table', () => {
           </a>
         </td>
       `;
-      expect(normalize(html)).toBe(normalize(expectedHtml));
+      expect(normalize(html + '')).toBe(normalize(expectedHtml));
     });
 
     it('Array column should not use number as formatter', () => {
@@ -404,6 +387,6 @@ xdescribe('when rendering table', () => {
   });
 });
 
-function normalize(str) {
+function normalize(str: string) {
   return str.replace(/\s+/gm, ' ').trim();
 }

+ 32 - 32
public/app/plugins/panel/table2/renderer.tsx → packages/grafana-ui/src/components/DataTable/renderer.tsx

@@ -8,33 +8,35 @@ import { sanitize } from 'app/core/utils/text';
 // Types
 import kbn from 'app/core/utils/kbn';
 import { getValueFormat, getColorFromHexRgbOrName, GrafanaThemeType, InterpolateFunction, Column } from '@grafana/ui';
-import { Style } from './types';
 import { Index } from 'react-virtualized';
+import { ColumnStyle } from './DataTable';
 
-type CellFormatter = (v: any, style: Style) => string;
+type CellFormatter = (v: any, style?: ColumnStyle) => string | undefined;
 
 interface ColumnInfo {
   header: string;
   accessor: string; // the field name
-  style?: Style;
+  style?: ColumnStyle;
   hidden?: boolean;
   formatter: CellFormatter;
   filterable?: boolean;
 }
 
-export class TableRenderer {
-  isUTC: false; // TODO? get UTC from props?
+interface RendererOptions {
+  styles: ColumnStyle[];
+  schema: Column[];
+  rowGetter: (info: Index) => any[]; // matches the table rowGetter
+  replaceVariables: InterpolateFunction;
+  isUTC?: boolean; // TODO? get UTC from props?
+  theme?: GrafanaThemeType | undefined;
+}
 
+export class TableRenderer {
   columns: ColumnInfo[];
   colorState: any;
-  theme?: GrafanaThemeType;
-
-  constructor(
-    styles: Style[],
-    schema: Column[],
-    private rowGetter: (info: Index) => any[], // matches the table rowGetter
-    private replaceVariables: InterpolateFunction
-  ) {
+
+  constructor(private options: RendererOptions) {
+    const { schema, styles } = options;
     this.colorState = {};
 
     if (!schema) {
@@ -42,10 +44,11 @@ export class TableRenderer {
       return;
     }
 
-    this.columns = schema.map((col, index) => {
+    this.columns = options.schema.map((col, index) => {
       let title = col.text;
-      let style: Style = null;
+      let style; // ColumnStyle
 
+      // Find the style based on the text
       for (let i = 0; i < styles.length; i++) {
         const s = styles[i];
         const regex = kbn.stringToJsRegex(s.pattern);
@@ -62,28 +65,24 @@ export class TableRenderer {
         header: title,
         accessor: col.text, // unique?
         style: style,
-        formatter: this.createColumnFormatter(style, col),
+        formatter: this.createColumnFormatter(col, style),
       };
     });
   }
 
-  setTheme(theme: GrafanaThemeType) {
-    this.theme = theme;
-  }
-
-  getColorForValue(value, style: Style) {
+  getColorForValue(value: any, style: ColumnStyle) {
     if (!style.thresholds) {
       return null;
     }
     for (let i = style.thresholds.length; i > 0; i--) {
       if (value >= style.thresholds[i - 1]) {
-        return getColorFromHexRgbOrName(style.colors[i], this.theme);
+        return getColorFromHexRgbOrName(style.colors![i], this.options.theme);
       }
     }
-    return getColorFromHexRgbOrName(_.first(style.colors), this.theme);
+    return getColorFromHexRgbOrName(_.first(style.colors), this.options.theme);
   }
 
-  defaultCellFormatter(v: any, style: Style): string {
+  defaultCellFormatter(v: any, style?: ColumnStyle): string {
     if (v === null || v === void 0 || v === undefined) {
       return '';
     }
@@ -99,7 +98,7 @@ export class TableRenderer {
     }
   }
 
-  createColumnFormatter(style: Style, header: any): CellFormatter {
+  createColumnFormatter(header: Column, style?: ColumnStyle): CellFormatter {
     if (!style) {
       return this.defaultCellFormatter;
     }
@@ -120,7 +119,7 @@ export class TableRenderer {
           v = v[0];
         }
         let date = moment(v);
-        if (this.isUTC) {
+        if (this.options.isUTC) {
           date = date.utc();
         }
         return date.format(style.dateFormat);
@@ -203,7 +202,7 @@ export class TableRenderer {
     };
   }
 
-  setColorState(value: any, style: Style) {
+  setColorState(value: any, style: ColumnStyle) {
     if (!style.colorMode) {
       return;
     }
@@ -220,8 +219,8 @@ export class TableRenderer {
   }
 
   renderRowVariables(rowIndex: number) {
-    const scopedVars = {};
-    const row = this.rowGetter({ index: rowIndex });
+    const scopedVars: any = {};
+    const row = this.options.rowGetter({ index: rowIndex });
     for (let i = 0; i < row.length; i++) {
       scopedVars[`__cell_${i}`] = { value: row[i] };
     }
@@ -261,11 +260,12 @@ export class TableRenderer {
     let columnHtml: JSX.Element;
     if (column.style && column.style.link) {
       // Render cell as link
+      const { replaceVariables } = this.options;
       const scopedVars = this.renderRowVariables(rowIndex);
       scopedVars['__cell'] = { value: value };
 
-      const cellLink = this.replaceVariables(column.style.linkUrl, scopedVars, encodeURIComponent);
-      const cellLinkTooltip = this.replaceVariables(column.style.linkTooltip, scopedVars);
+      const cellLink = replaceVariables(column.style.linkUrl, scopedVars, encodeURIComponent);
+      const cellLinkTooltip = replaceVariables(column.style.linkTooltip, scopedVars);
       const cellTarget = column.style.linkTargetBlank ? '_blank' : '';
 
       cellClasses.push('table-panel-cell-link');
@@ -284,7 +284,7 @@ export class TableRenderer {
       columnHtml = <span>{value}</span>;
     }
 
-    let filterLink: JSX.Element;
+    let filterLink: JSX.Element | null = null;
     if (column.filterable) {
       cellClasses.push('table-panel-cell-filterable');
       filterLink = (

+ 22 - 1
packages/grafana-ui/src/utils/processTimeSeries.ts

@@ -4,7 +4,7 @@ import isNumber from 'lodash/isNumber';
 import { colors } from './colors';
 
 // Types
-import { TimeSeries, TimeSeriesVMs, NullValueMode, TimeSeriesValue } from '../types';
+import { TimeSeries, TableData, TimeSeriesVMs, NullValueMode, TimeSeriesValue } from '../types';
 
 interface Options {
   timeSeries: TimeSeries[];
@@ -173,3 +173,24 @@ export function processTimeSeries({ timeSeries, nullValueMode }: Options): TimeS
 
   return vmSeries;
 }
+
+export function sortTableData(data?: TableData, sortIndex?: number, reverse = false): TableData {
+  if (data && isNumber(sortIndex)) {
+    const copy = {
+      ...data,
+      rows: [...data.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) {
+      copy.rows.reverse();
+    }
+
+    return copy;
+  }
+  return data;
+}

+ 4 - 4
public/app/plugins/panel/table/renderer.ts

@@ -2,7 +2,7 @@ import _ from 'lodash';
 import moment from 'moment';
 import kbn from 'app/core/utils/kbn';
 import { getValueFormat, getColorFromHexRgbOrName, GrafanaThemeType } from '@grafana/ui';
-import { Style } from '../table2/types';
+import { ColumnStyle } from '@grafana/ui/src/components/DataTable/DataTable';
 
 export class TableRenderer {
   formatters: any[];
@@ -52,7 +52,7 @@ export class TableRenderer {
     }
   }
 
-  getColorForValue(value, style: Style) {
+  getColorForValue(value, style: ColumnStyle) {
     if (!style.thresholds) {
       return null;
     }
@@ -64,7 +64,7 @@ export class TableRenderer {
     return getColorFromHexRgbOrName(_.first(style.colors), this.theme);
   }
 
-  defaultCellFormatter(v, style: Style) {
+  defaultCellFormatter(v, style: ColumnStyle) {
     if (v === null || v === void 0 || v === undefined) {
       return '';
     }
@@ -191,7 +191,7 @@ export class TableRenderer {
     };
   }
 
-  setColorState(value, style: Style) {
+  setColorState(value, style: ColumnStyle) {
     if (!style.colorMode) {
       return;
     }

+ 7 - 114
public/app/plugins/panel/table2/TablePanel.tsx

@@ -1,136 +1,29 @@
 // Libraries
 import _ from 'lodash';
-import React, { Component, ReactNode } from 'react';
+import React, { Component } from 'react';
 
 // Types
-import { PanelProps, ThemeContext, TableData } from '@grafana/ui';
+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 { sortTableData } from './sortable';
+import DataTable from '@grafana/ui/src/components/DataTable/DataTable';
 
 interface Props extends PanelProps<Options> {}
 
-interface State {
-  sortBy?: number;
-  sortDirection?: SortDirectionType;
-  data: TableData;
-}
-
-export class TablePanel extends Component<Props, State> {
-  renderer: TableRenderer;
-
+export class TablePanel extends Component<Props> {
   constructor(props: Props) {
     super(props);
-
-    const { panelData, options, replaceVariables } = this.props;
-
-    this.state = {
-      data: panelData.tableData,
-    };
-
-    this.renderer = new TableRenderer(options.styles, this.state.data.columns, this.rowGetter, replaceVariables);
-  }
-
-  componentDidUpdate(prevProps: Props, prevState: State) {
-    const { panelData, options } = this.props;
-    const { sortBy, sortDirection } = this.state;
-
-    // Update the renderer if options change
-    if (options !== prevProps.options) {
-      this.renderer = new TableRenderer(
-        options.styles,
-        this.state.data.columns,
-        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 = sortTableData(panelData.tableData, sortBy, sortDirection === 'DESC');
-      this.setState({ data });
-    }
   }
 
-  rowGetter = ({ index }) => {
-    return this.state.data.rows[index];
-  };
-
-  doSort = ({ 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 });
-  };
-
-  headerRenderer = (header: TableHeaderProps): ReactNode => {
-    const dataKey = header.dataKey as any; // types say string, but it is number!
-    const { data, sortBy, sortDirection } = this.state;
-    const col = data.columns[dataKey];
-
-    return (
-      <div>
-        {col.text} {sortBy === dataKey && <SortIndicator sortDirection={sortDirection} />}
-      </div>
-    );
-  };
-
-  cellRenderer = (cell: TableCellProps) => {
-    const { columnIndex, rowIndex } = cell;
-    const row = this.state.data.rows[rowIndex];
-    const val = row[columnIndex];
-    return this.renderer.renderCell(columnIndex, rowIndex, val);
-  };
-
   render() {
-    const { width, height, options } = this.props;
-    const { showHeader } = options;
-    //   const { sortBy, sortDirection } = this.state;
-    const { data } = this.state;
+    const { panelData, options } = this.props;
 
-    if (!data) {
+    if (!panelData || !panelData.tableData) {
       return <div>No Table Data...</div>;
     }
 
     return (
       <ThemeContext.Consumer>
-        {(
-          theme // ??? { this.renderer.setTheme(theme) }
-        ) => (
-          <Table
-            disableHeader={!showHeader}
-            headerHeight={30}
-            height={height}
-            overscanRowCount={10}
-            rowHeight={30}
-            rowGetter={this.rowGetter}
-            rowCount={data.rows.length}
-            sort={this.doSort}
-            width={width}
-          >
-            {data.columns.map((col, index) => {
-              return (
-                <Column
-                  key={index}
-                  dataKey={index}
-                  headerRenderer={this.headerRenderer}
-                  cellRenderer={this.cellRenderer}
-                  width={150}
-                  minWidth={50}
-                  flexGrow={1}
-                />
-              );
-            })}
-          </Table>
-        )}
+        {theme => <DataTable {...this.props} {...options} theme={theme} data={panelData.tableData} />}
       </ThemeContext.Consumer>
     );
   }

+ 2 - 9
public/app/plugins/panel/table2/TablePanelEditor.tsx

@@ -3,7 +3,7 @@ import _ from 'lodash';
 import React, { PureComponent } from 'react';
 
 // Types
-import { PanelEditorProps, Switch, FormField } from '@grafana/ui';
+import { PanelEditorProps, Switch } from '@grafana/ui';
 import { Options } from './types';
 
 export class TablePanelEditor extends PureComponent<PanelEditorProps<Options>> {
@@ -11,10 +11,8 @@ export class TablePanelEditor extends PureComponent<PanelEditorProps<Options>> {
     this.props.onOptionsChange({ ...this.props.options, showHeader: !this.props.options.showHeader });
   };
 
-  onRowsPerPageChange = ({ target }) => this.props.onOptionsChange({ ...this.props.options, pageSize: target.value });
-
   render() {
-    const { showHeader, pageSize } = this.props.options;
+    const { showHeader } = this.props.options;
 
     return (
       <div>
@@ -22,11 +20,6 @@ export class TablePanelEditor extends PureComponent<PanelEditorProps<Options>> {
           <h5 className="section-heading">Header</h5>
           <Switch label="Show" labelClass="width-5" checked={showHeader} onChange={this.onToggleShowHeader} />
         </div>
-
-        <div className="section gf-form-group">
-          <h5 className="section-heading">Paging</h5>
-          <FormField label="Rows per page" labelWidth={8} onChange={this.onRowsPerPageChange} value={pageSize} />
-        </div>
       </div>
     );
   }

+ 0 - 25
public/app/plugins/panel/table2/sortable.tsx

@@ -1,25 +0,0 @@
-// Libraries
-import isNumber from 'lodash/isNumber';
-
-import { TableData } from '@grafana/ui';
-
-export function sortTableData(data: TableData, sortIndex?: number, reverse = false): TableData {
-  if (isNumber(sortIndex)) {
-    const copy = {
-      ...data,
-      rows: [...data.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) {
-      copy.rows.reverse();
-    }
-
-    return copy;
-  }
-  return data;
-}

+ 2 - 26
public/app/plugins/panel/table2/types.ts

@@ -1,31 +1,8 @@
-// Made to match the existing (untyped) settings in the angular table
-export interface Style {
-  alias?: string;
-  colorMode?: string;
-  colors?: any[];
-  decimals?: number;
-  pattern?: string;
-  thresholds?: any[];
-  type?: 'date' | 'number' | 'string' | 'hidden';
-  unit?: string;
-  dateFormat?: string;
-  sanitize?: boolean;
-  mappingType?: any;
-  valueMaps?: any;
-  rangeMaps?: any;
-
-  link?: any;
-  linkUrl?: any;
-  linkTooltip?: any;
-  linkTargetBlank?: boolean;
-
-  preserveFormat?: boolean;
-}
+import { ColumnStyle } from '@grafana/ui/src/components/DataTable/DataTable';
 
 export interface Options {
   showHeader: boolean;
-  styles: Style[];
-  pageSize: number;
+  styles: ColumnStyle[];
 }
 
 export const defaults: Options = {
@@ -48,5 +25,4 @@ export const defaults: Options = {
       thresholds: [],
     },
   ],
-  pageSize: 100,
 };