diff --git a/src/plugins/vis_type_table/public/components/table_vis_component.tsx b/src/plugins/vis_type_table/public/components/table_vis_component.tsx index 1a597fa15ddb..75bdc47cf075 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_component.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_component.tsx @@ -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,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( + handlers.uiState.get('vis.sortColumn') || {} + ); + + const [columnsWidth, setColumnsWidth] = useState( + 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(() => { @@ -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( - 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); } }; @@ -89,8 +139,6 @@ export const TableVisComponent = ({ table, visConfig, handlers }: TableVisCompon }; }, [handlers.uiState]); - const dataGridColumns = getDataGridColumns(table, visConfig, handlers, columnsWidth); - return ( diff --git a/src/plugins/vis_type_table/public/components/table_vis_grid_columns.tsx b/src/plugins/vis_type_table/public/components/table_vis_grid_columns.tsx index b1a9443fe059..3f8d7bcd112e 100644 --- a/src/plugins/vis_type_table/public/components/table_vis_grid_columns.tsx +++ b/src/plugins/vis_type_table/public/components/table_vis_grid_columns.tsx @@ -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( diff --git a/src/plugins/vis_type_table/public/types.ts b/src/plugins/vis_type_table/public/types.ts index 2a8cd5f9108e..f93f338893ca 100644 --- a/src/plugins/vis_type_table/public/types.ts +++ b/src/plugins/vis_type_table/public/types.ts @@ -69,3 +69,8 @@ export interface ColumnWidth { colIndex: number; width: number; } + +export interface SortColumn { + colIndex: number; + direction: 'asc' | 'desc'; +}