Commit 0d8384e0 by ryan

sortable class

parent f7c1842a
......@@ -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>
);
}
}
......@@ -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] };
}
......
// 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;
}
}
......@@ -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);
......
......@@ -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[];
......
.ReactVirtualized__Table {
}
// .ReactVirtualized__Table {
// }
.ReactVirtualized__Table__Grid {
}
// .ReactVirtualized__Table__Grid {
// }
.ReactVirtualized__Table__headerRow {
font-weight: 700;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment