Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Table Visualization] make table vis column sortable #2502

Merged
merged 1 commit into from
Oct 12, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
[Table Visualization] make table vis column sortable
* add sort state (asc | desc)  to column

Partially resolve:
#2305

Signed-off-by: Anan Zhuang <ananzh@amazon.com>
ananzh committed Oct 12, 2022
commit 65181fb0894665ac254c09a439878d99fca6d4e9
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
@@ -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;
@@ -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 }]
ananzh marked this conversation as resolved.
Show resolved Hide resolved
: [];
}, [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"
@@ -101,6 +107,7 @@ export const TableVisComponent = ({ table, visConfig, handlers }: TableVisCompon
}}
rowCount={rows.length}
renderCellValue={renderCellValue}
sorting={{ columns: sortedColumns, onSort }}
onColumnResize={onColumnResize}
pagination={pagination}
/>
Original file line number Diff line number Diff line change
@@ -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',
@@ -29,7 +28,7 @@ export const getDataGridColumns = (
{
table: {
columns: table.columns,
rows: formattedRows,
rows,
},
row: rowIndex,
column: rawColumnIndex,
@@ -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',
@@ -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(
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
@@ -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
@@ -69,3 +69,8 @@ export interface ColumnWidth {
colIndex: number;
width: number;
}

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