Skip to content

Commit

Permalink
[Table Visualization] make table vis column sortable (#2502)
Browse files Browse the repository at this point in the history
* add sort state (asc | desc)  to column
* fix pagination issue

Partially resolve:
#2305

Signed-off-by: Anan Zhuang <[email protected]>
  • Loading branch information
ananzh authored Oct 12, 2022
1 parent 66b6848 commit 7e969bc
Show file tree
Hide file tree
Showing 4 changed files with 85 additions and 71 deletions.
127 changes: 67 additions & 60 deletions src/plugins/vis_type_table/public/components/table_vis_component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
*/

import React, { useState, useCallback, useMemo, useRef, useEffect } from 'react';
import { isEqual } from 'lodash';
import { EuiDataGridProps, EuiDataGrid } from '@elastic/eui';
import { isEqual, orderBy } from 'lodash';
import { EuiDataGridProps, EuiDataGrid, EuiDataGridSorting } from '@elastic/eui';

import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { Table } from '../table_vis_response_handler';
import { TableVisConfig, ColumnWidth } from '../types';
import { TableVisConfig, ColumnWidth, SortColumn } from '../types';
import { getDataGridColumns } from './table_vis_grid_columns';
import { usePagination } from '../utils';
import { convertToFormattedData } from '../utils/convert_to_formatted_data';

interface TableVisComponentProps {
table: Table;
Expand All @@ -20,77 +21,82 @@ interface TableVisComponentProps {
}

export const TableVisComponent = ({ table, visConfig, handlers }: TableVisComponentProps) => {
const { rows, columns } = table;
const { formattedRows: rows, formattedColumns: columns } = convertToFormattedData(
table,
visConfig
);

const pagination = usePagination(visConfig, rows.length);

// toDo: it is a sample renderCellValue to render a data grid component
// will check on it and it might be replaced
const renderCellValue = useMemo(() => {
return (({ rowIndex, columnId }) => {
let adjustedRowIndex = rowIndex;
// store current state
const currentColState = useRef<{
columnsWidth: ColumnWidth[];
}>({
columnsWidth: handlers.uiState.get('vis.columnsWidth'),
});

// If we are doing the pagination (instead of leaving that to the grid)
// then the row index must be adjusted as `data` has already been pruned to the page size
adjustedRowIndex = rowIndex - pagination!.pageIndex * pagination!.pageSize;
const sortedRows = useMemo(() => {
const sort = handlers.uiState.get('vis.sortColumn');
return sort && sort.colIndex !== null && sort.direction
? orderBy(rows, columns[sort.colIndex]?.id, sort.direction)
: rows;
}, [columns, rows, handlers.uiState]);

return rows.hasOwnProperty(adjustedRowIndex)
? rows[adjustedRowIndex][columnId] || null
: null;
const renderCellValue = useMemo(() => {
return (({ rowIndex, columnId }) => {
return sortedRows.hasOwnProperty(rowIndex) ? sortedRows[rowIndex][columnId] || null : null;
}) as EuiDataGridProps['renderCellValue'];
}, [rows, pagination]);
}, [sortedRows]);

const dataGridColumns = getDataGridColumns(
sortedRows,
columns,
table,
handlers,
currentColState.current.columnsWidth
);

// resize column
const [columnsWidth, setColumnsWidth] = useState<ColumnWidth[]>(
handlers.uiState.get('vis.columnsWidth') || []
const sortedColumns = useMemo(() => {
const sort = handlers.uiState.get('vis.sortColumn');
return sort && sort.colIndex !== null && sort.direction
? [{ id: dataGridColumns[sort.colIndex]?.id, direction: sort.direction }]
: [];
}, [handlers.uiState, dataGridColumns]);

const onSort = useCallback(
(sortingCols: EuiDataGridSorting['columns']) => {
const nextSortValue = sortingCols[sortingCols.length - 1];
const nextSort = {
colIndex: dataGridColumns.findIndex((col) => col.id === nextSortValue?.id),
direction: nextSortValue.direction,
};
handlers.uiState.set('vis.sortColumn', nextSort);
handlers.uiState?.emit('reload');
return nextSort;
},
[dataGridColumns, handlers.uiState]
);
const curColumnsWidth = useRef<{
columnsWidth: ColumnWidth[];
}>({
columnsWidth: handlers.uiState?.get('vis.columnsWidth'),
});

const onColumnResize: EuiDataGridProps['onColumnResize'] = useCallback(
({ columnId, width }) => {
setColumnsWidth((prevState) => {
const nextColIndex = columns.findIndex((c) => c.id === columnId);
const prevColIndex = prevState.findIndex((c) => c.colIndex === nextColIndex);
const nextState = [...prevState];
const updatedColWidth = { colIndex: nextColIndex, width };

// if updated column index is not found, then add it to nextState
// else reset it in nextState
if (prevColIndex < 0) nextState.push(updatedColWidth);
else nextState[prevColIndex] = updatedColWidth;

// update uiState
handlers.uiState?.set('vis.columnsWidth', nextState);
return nextState;
});
const prevState: ColumnWidth[] = currentColState.current.columnsWidth;
const nextColIndex = columns.findIndex((col) => col.id === columnId);
const prevColIndex = prevState.findIndex((col) => col.colIndex === nextColIndex);
const nextState = [...prevState];
const updatedColWidth = { colIndex: nextColIndex, width };

// if updated column index is not found, then add it to nextState
// else reset it in nextState
if (prevColIndex < 0) nextState.push(updatedColWidth);
else nextState[prevColIndex] = updatedColWidth;

// update uiState
currentColState.current.columnsWidth = nextState;
handlers.uiState.set('vis.columnsWidth', nextState);
},
[columns, setColumnsWidth, handlers.uiState]
[columns, currentColState, handlers.uiState]
);

useEffect(() => {
const updateTable = () => {
const updatedVisState = handlers.uiState?.getChanges()?.vis;
if (!isEqual(updatedVisState?.columnsWidth, curColumnsWidth.current.columnsWidth)) {
curColumnsWidth.current.columnsWidth = updatedVisState?.columnsWidth;
setColumnsWidth(updatedVisState?.columnsWidth || []);
}
};

if (handlers.uiState) {
handlers.uiState.on('change', updateTable);
}

return () => {
handlers.uiState?.off('change', updateTable);
};
}, [handlers.uiState]);

const dataGridColumns = getDataGridColumns(table, visConfig, handlers, columnsWidth);

return (
<EuiDataGrid
aria-label="tableVis"
Expand All @@ -101,6 +107,7 @@ export const TableVisComponent = ({ table, visConfig, handlers }: TableVisCompon
}}
rowCount={rows.length}
renderCellValue={renderCellValue}
sorting={{ columns: sortedColumns, onSort }}
onColumnResize={onColumnResize}
pagination={pagination}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,19 @@ import React from 'react';
import { i18n } from '@osd/i18n';
import { EuiDataGridColumn, EuiDataGridColumnCellActionProps } from '@elastic/eui';
import { IInterpreterRenderHandlers } from 'src/plugins/expressions';
import { OpenSearchDashboardsDatatableRow } from 'src/plugins/expressions';
import { Table } from '../table_vis_response_handler';
import { TableVisConfig, ColumnWidth } from '../types';
import { convertToFormattedData } from '../utils';
import { ColumnWidth, FormattedColumn } from '../types';

export const getDataGridColumns = (
rows: OpenSearchDashboardsDatatableRow[],
cols: FormattedColumn[],
table: Table,
visConfig: TableVisConfig,
handlers: IInterpreterRenderHandlers,
columnsWidth: ColumnWidth[]
) => {
const { formattedRows, formattedColumns } = convertToFormattedData(table, visConfig);

const filterBucket = (rowIndex: number, columnIndex: number, negate: boolean) => {
const foramttedColumnId = formattedColumns[columnIndex].id;
const foramttedColumnId = cols[columnIndex].id;
const rawColumnIndex = table.columns.findIndex((col) => col.id === foramttedColumnId);
handlers.event({
name: 'filterBucket',
Expand All @@ -29,7 +28,7 @@ export const getDataGridColumns = (
{
table: {
columns: table.columns,
rows: formattedRows,
rows,
},
row: rowIndex,
column: rawColumnIndex,
Expand All @@ -40,12 +39,12 @@ export const getDataGridColumns = (
});
};

return formattedColumns.map((col, colIndex) => {
return cols.map((col, colIndex) => {
const cellActions = col.filterable
? [
({ rowIndex, columnId, Component, closePopover }: EuiDataGridColumnCellActionProps) => {
const filterValue = formattedRows[rowIndex][columnId];
const filterContent = col.formatter?.convert(formattedRows[rowIndex][columnId]);
const filterValue = rows[rowIndex][columnId];
const filterContent = col.formatter?.convert(filterValue);

const filterForValueText = i18n.translate(
'visTypeTable.tableVisFilter.filterForValue',
Expand Down Expand Up @@ -80,7 +79,7 @@ export const getDataGridColumns = (
);
},
({ rowIndex, columnId, Component, closePopover }: EuiDataGridColumnCellActionProps) => {
const filterValue = formattedRows[rowIndex][columnId];
const filterValue = rows[rowIndex][columnId];
const filterContent = col.formatter?.convert(filterValue);

const filterOutValueText = i18n.translate(
Expand Down
3 changes: 3 additions & 0 deletions src/plugins/vis_type_table/public/table_vis_fn.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ export const createTableVisFn = (): TableVisExpressionFunctionDefinition => ({
visType: 'table',
visConfig,
},
params: {
listenOnChange: true,
},
};
},
});
5 changes: 5 additions & 0 deletions src/plugins/vis_type_table/public/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,8 @@ export interface ColumnWidth {
colIndex: number;
width: number;
}

export interface SortColumn {
colIndex: number;
direction: 'asc' | 'desc';
}

0 comments on commit 7e969bc

Please sign in to comment.