Skip to content

Commit

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

Partially resolve:
#2305

Signed-off-by: Anan Zhuang <ananzh@amazon.com>
  • Loading branch information
ananzh committed Oct 4, 2022
1 parent 66b6848 commit 8c72e36
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 48 deletions.
123 changes: 86 additions & 37 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,10 +21,38 @@ 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);

// set sort and resize column state
const [sortColumn, setSortColumn] = useState<SortColumn>(
handlers.uiState.get('vis.sortColumn') || {}
);

const [columnsWidth, setColumnsWidth] = useState<ColumnWidth[]>(
handlers.uiState.get('vis.columnsWidth') || []
);

// store current state
const curColumnsState = useRef<{
sortColumn: SortColumn;
columnsWidth: ColumnWidth[];
}>({
sortColumn: handlers.uiState.get('vis.sortColumn'),
columnsWidth: handlers.uiState?.get('vis.columnsWidth'),
});

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]);

// toDo: it is a sample renderCellValue to render a data grid component
// will check on it and it might be replaced
const renderCellValue = useMemo(() => {
Expand All @@ -34,49 +63,70 @@ export const TableVisComponent = ({ table, visConfig, handlers }: TableVisCompon
// then the row index must be adjusted as `data` has already been pruned to the page size
adjustedRowIndex = rowIndex - pagination!.pageIndex * pagination!.pageSize;

return rows.hasOwnProperty(adjustedRowIndex)
? rows[adjustedRowIndex][columnId] || null
return sortedRows.hasOwnProperty(adjustedRowIndex)
? sortedRows[adjustedRowIndex][columnId] || null
: null;
}) as EuiDataGridProps['renderCellValue'];
}, [rows, pagination]);

// resize column
const [columnsWidth, setColumnsWidth] = useState<ColumnWidth[]>(
handlers.uiState.get('vis.columnsWidth') || []
}, [sortedRows, pagination]);

const dataGridColumns = getDataGridColumns(sortedRows, columns, table, handlers, 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);
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 = 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
handlers.uiState?.set('vis.columnsWidth', nextState);
},
[columns, setColumnsWidth, handlers.uiState]
[columns, columnsWidth, 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 (
updatedVisState?.sortColumn &&
!isEqual(updatedVisState.sortColumn, curColumnsState.current.sortColumn)
) {
curColumnsState.current.sortColumn = updatedVisState.sortColumn;
setSortColumn(updatedVisState.sortColumn);
}

if (
updatedVisState?.columnsWidth &&
!isEqual(updatedVisState.columnsWidth, curColumnsState.current.columnsWidth)
) {
curColumnsState.current.columnsWidth = updatedVisState.columnsWidth;
setColumnsWidth(updatedVisState.columnsWidth);
}
};

Expand All @@ -89,8 +139,6 @@ export const TableVisComponent = ({ table, visConfig, handlers }: TableVisCompon
};
}, [handlers.uiState]);

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

return (
<EuiDataGrid
aria-label="tableVis"
Expand All @@ -101,6 +149,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
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 8c72e36

Please sign in to comment.