diff --git a/packages/grafana-ui/src/components/Table/Table.story.tsx b/packages/grafana-ui/src/components/Table/Table.story.tsx index 1aa618d73d7..240c7def7f3 100644 --- a/packages/grafana-ui/src/components/Table/Table.story.tsx +++ b/packages/grafana-ui/src/components/Table/Table.story.tsx @@ -18,11 +18,28 @@ const replaceVariables = (value: string, scopedVars?: ScopedVars) => { return value; }; -storiesOf('UI/Table', module) +export function makeDummyTable(columnCount: number, rowCount: number): TableData { + const A = 'A'.charCodeAt(0); + return { + columns: Array.from(new Array(columnCount), (x, i) => { + return { + text: String.fromCharCode(A + i), + }; + }), + rows: Array.from(new Array(rowCount), (x, rowId) => { + const suffix = (rowId + 1).toString(); + return Array.from(new Array(columnCount), (x, colId) => String.fromCharCode(A + colId) + suffix); + }), + type: 'table', + columnMap: {}, + }; +} + +storiesOf('Alpha/Table', module) .add('basic', () => { const showHeader = boolean('Show Header', true); - const fixedRowCount = number('Fixed Rows', 1); - const fixedColumnCount = number('Fixed Columns', 1); + const fixedRowCount = number('Fixed Rows', 1, { min: 0, max: 50, step: 1, range: false }); + const fixedColumnCount = number('Fixed Columns', 1, { min: 0, max: 50, step: 1, range: false }); return withFullSizeStory(Table, { styles: [], @@ -33,41 +50,28 @@ storiesOf('UI/Table', module) showHeader, }); }) - .add('Test Configuration', () => { + .add('variable size', () => { + const columnCount = number('Column Count', 10, { min: 2, max: 50, step: 1, range: false }); + const rowCount = number('Row Count', 20, { min: 0, max: 100, step: 1, range: false }); + + const showHeader = boolean('Show Header', true); + const fixedRowCount = number('Fixed Rows', 1, { min: 0, max: 50, step: 1, range: false }); + const fixedColumnCount = number('Fixed Columns', 1, { min: 0, max: 50, step: 1, range: false }); + + return withFullSizeStory(Table, { + styles: [], + data: makeDummyTable(columnCount, rowCount), + replaceVariables, + fixedRowCount, + fixedColumnCount, + showHeader, + }); + }) + .add('Old tests configuration', () => { return withFullSizeStory(Table, { styles: migratedTestStyles, data: migratedTestTable, replaceVariables, showHeader: true, }); - }) - .add('Lots of cells', () => { - const data = { - columns: [], - rows: [], - type: 'table', - columnMap: {}, - } as TableData; - for (let i = 0; i < 20; i++) { - data.columns.push({ - text: 'Column ' + i, - }); - } - for (let r = 0; r < 500; r++) { - const row = []; - for (let i = 0; i < 20; i++) { - row.push(r + i); - } - data.rows.push(row); - } - console.log('DATA:', data); - - return withFullSizeStory(Table, { - styles: simpleTable, - data, - replaceVariables, - showHeader: true, - fixedColumnCount: 1, - fixedRowCount: 1, - }); }); diff --git a/packages/grafana-ui/src/components/Table/Table.tsx b/packages/grafana-ui/src/components/Table/Table.tsx index 2a75c33224e..509eaf5d676 100644 --- a/packages/grafana-ui/src/components/Table/Table.tsx +++ b/packages/grafana-ui/src/components/Table/Table.tsx @@ -43,6 +43,7 @@ interface State { export class Table extends Component { columns: ColumnInfo[]; measurer: CellMeasurerCache; + scrollToTop = false; static defaultProps = { showHeader: true, @@ -65,12 +66,13 @@ export class Table extends Component { } componentDidUpdate(prevProps: Props, prevState: State) { - const { data, styles } = this.props; + const { data, styles, showHeader } = this.props; const { sortBy, sortDirection } = this.state; const dataChanged = data !== prevProps.data; + const configsChanged = showHeader !== prevProps.showHeader; // Reset the size cache - if (dataChanged) { + if (dataChanged || configsChanged) { this.measurer.clearAll(); } @@ -81,8 +83,8 @@ export class Table extends Component { // Update the data when data or sort changes if (dataChanged || sortBy !== prevState.sortBy || sortDirection !== prevState.sortDirection) { - const sorted = data ? sortTableData(data, sortBy, sortDirection === 'DESC') : data; - this.setState({ data: sorted }); + this.scrollToTop = true; + this.setState({ data: sortTableData(data, sortBy, sortDirection === 'DESC') }); } } @@ -137,7 +139,7 @@ export class Table extends Component { if (realRowIndex < 0) { this.doSort(columnIndex); } else { - const row = data!.rows[realRowIndex]; + const row = data.rows[realRowIndex]; const value = row[columnIndex]; console.log('CLICK', rowIndex, columnIndex, value); } @@ -147,18 +149,19 @@ export class Table extends Component { const { data, sortBy, sortDirection } = this.state; const { columnIndex, rowIndex, style } = cell.props; - const col = data!.columns[columnIndex]; + let col = data.columns[columnIndex]; const sorting = sortBy === columnIndex; + if (!col) { + // NOT SURE Why this happens sometimes + col = { + text: '??' + columnIndex + '???', + }; + } return (
this.handleCellClick(rowIndex, columnIndex)}> {col.text} - {sorting && ( - - {sortDirection} - - - )} + {sorting && }
); }; @@ -167,15 +170,16 @@ export class Table extends Component { const { rowIndex, columnIndex, key, parent } = props; const { showHeader } = this.props; const { data } = this.state; - if (!data) { - return
??
; + const column = this.columns[columnIndex]; + if (!column) { + return
XXX
; // NOT SURE HOW/WHY THIS HAPPENS! } const realRowIndex = rowIndex - (showHeader ? 1 : 0); const isHeader = realRowIndex < 0; const row = isHeader ? (data.columns as any[]) : data.rows[realRowIndex]; const value = row[columnIndex]; - const builder = isHeader ? this.headerBuilder : this.columns[columnIndex].builder; + const builder = isHeader ? this.headerBuilder : column.builder; return ( @@ -185,9 +189,18 @@ export class Table extends Component { }; render() { - const { data, showHeader, width, height, fixedColumnCount, fixedRowCount } = this.props; - if (!data) { - return
NO Data
; + const { data, showHeader, width, height } = this.props; + + const columnCount = data.columns.length; + const rowCount = data.rows.length + (showHeader ? 1 : 0); + + const fixedColumnCount = Math.min(this.props.fixedColumnCount, columnCount); + const fixedRowCount = Math.min(this.props.fixedRowCount, rowCount); + + // Usually called after a sort or the data changes + const scrollToRow = this.scrollToTop ? 1 : -1; + if (this.scrollToTop) { + this.scrollToTop = false; } return ( @@ -195,8 +208,12 @@ export class Table extends Component { { ...this.state /** Force MultiGrid to update when data changes */ } - columnCount={data.columns.length} - rowCount={data.rows.length + (showHeader ? 1 : 0)} + { + ...this.props /** Force MultiGrid to update when data changes */ + } + scrollToRow={scrollToRow} + columnCount={columnCount} + rowCount={rowCount} overscanColumnCount={2} overscanRowCount={2} columnWidth={this.measurer.columnWidth} diff --git a/packages/grafana-ui/src/components/Table/TableCellBuilder.tsx b/packages/grafana-ui/src/components/Table/TableCellBuilder.tsx index d8816b08f2e..2f143c81e3b 100644 --- a/packages/grafana-ui/src/components/Table/TableCellBuilder.tsx +++ b/packages/grafana-ui/src/components/Table/TableCellBuilder.tsx @@ -25,7 +25,7 @@ export const simpleCellBuilder: TableCellBuilder = (cell: TableCellBuilderOption const { style } = props; return ( -
+
{value}
); diff --git a/packages/grafana-ui/src/components/Table/_Table.scss b/packages/grafana-ui/src/components/Table/_Table.scss index 22170ecf913..05569f84e2e 100644 --- a/packages/grafana-ui/src/components/Table/_Table.scss +++ b/packages/grafana-ui/src/components/Table/_Table.scss @@ -41,7 +41,6 @@ } .ReactVirtualized__Table__sortableHeaderIconContainer { - display: flex; align-items: center; } .ReactVirtualized__Table__sortableHeaderIcon { @@ -73,3 +72,7 @@ border-right: 2px solid $body-bg; border-bottom: 2px solid $body-bg; } + +.gf-table-fixed-column { + border-right: 1px solid #ccc; +}