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
Show file tree
Hide file tree
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
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 }]
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"
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';
}