From 497ce81867b115b3fd3b8648bf51b533873f1f62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Torkel=20=C3=96degaard?= Date: Wed, 28 Dec 2022 19:37:17 +0100 Subject: [PATCH] Table: Refactoring state handling for expanded rows (#60791) * Table: Refactoring state handling for expanded rows * Simplify row expander * remove console.log * review fixes * Simplify hook name * fixed test --- .betterer.results | 27 +-- .../src/components/Table/FooterRow.tsx | 8 +- .../src/components/Table/HeaderRow.tsx | 7 +- .../src/components/Table/RowExpander.tsx | 38 ++-- .../src/components/Table/Table.test.tsx | 5 +- .../grafana-ui/src/components/Table/Table.tsx | 170 ++---------------- .../grafana-ui/src/components/Table/hooks.ts | 53 ++++++ .../src/components/Table/reducer.ts | 87 +++++++++ .../grafana-ui/src/components/Table/types.ts | 32 +++- .../src/components/Table/utils.test.tsx | 8 +- .../grafana-ui/src/components/Table/utils.tsx | 39 ++-- 11 files changed, 244 insertions(+), 230 deletions(-) create mode 100644 packages/grafana-ui/src/components/Table/hooks.ts create mode 100644 packages/grafana-ui/src/components/Table/reducer.ts diff --git a/.betterer.results b/.betterer.results index f054e0016e0..ffce50e2068 100644 --- a/.betterer.results +++ b/.betterer.results @@ -1419,18 +1419,11 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "0"] ], "packages/grafana-ui/src/components/Table/Table.tsx:5381": [ - [0, 0, 0, "Do not use any type assertions.", "0"], - [0, 0, 0, "Unexpected any. Specify a different type.", "1"], + [0, 0, 0, "Unexpected any. Specify a different type.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"], - [0, 0, 0, "Unexpected any. Specify a different type.", "3"], - [0, 0, 0, "Do not use any type assertions.", "4"], - [0, 0, 0, "Do not use any type assertions.", "5"], - [0, 0, 0, "Do not use any type assertions.", "6"], - [0, 0, 0, "Do not use any type assertions.", "7"], - [0, 0, 0, "Do not use any type assertions.", "8"], - [0, 0, 0, "Do not use any type assertions.", "9"], - [0, 0, 0, "Do not use any type assertions.", "10"], - [0, 0, 0, "Do not use any type assertions.", "11"] + [0, 0, 0, "Do not use any type assertions.", "3"], + [0, 0, 0, "Do not use any type assertions.", "4"] ], "packages/grafana-ui/src/components/Table/TableCell.tsx:5381": [ [0, 0, 0, "Do not use any type assertions.", "0"], @@ -1443,6 +1436,18 @@ exports[`better eslint`] = { "packages/grafana-ui/src/components/Table/TableCellInspectModal.tsx:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], + "packages/grafana-ui/src/components/Table/hooks.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"], + [0, 0, 0, "Do not use any type assertions.", "2"], + [0, 0, 0, "Do not use any type assertions.", "3"], + [0, 0, 0, "Do not use any type assertions.", "4"] + ], + "packages/grafana-ui/src/components/Table/reducer.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Unexpected any. Specify a different type.", "1"], + [0, 0, 0, "Do not use any type assertions.", "2"] + ], "packages/grafana-ui/src/components/Table/types.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"] diff --git a/packages/grafana-ui/src/components/Table/FooterRow.tsx b/packages/grafana-ui/src/components/Table/FooterRow.tsx index 3811205ec6c..6acbd766dc5 100644 --- a/packages/grafana-ui/src/components/Table/FooterRow.tsx +++ b/packages/grafana-ui/src/components/Table/FooterRow.tsx @@ -3,10 +3,8 @@ import { ColumnInstance, HeaderGroup } from 'react-table'; import { selectors } from '@grafana/e2e-selectors'; -import { useStyles2 } from '../../themes'; - import { EmptyCell, FooterCell } from './FooterCell'; -import { getTableStyles, TableStyles } from './styles'; +import { TableStyles } from './styles'; import { FooterItem } from './types'; export interface FooterRowProps { @@ -14,12 +12,12 @@ export interface FooterRowProps { footerGroups: HeaderGroup[]; footerValues: FooterItem[]; isPaginationVisible: boolean; + tableStyles: TableStyles; } export const FooterRow = (props: FooterRowProps) => { - const { totalColumnsWidth, footerGroups, isPaginationVisible } = props; + const { totalColumnsWidth, footerGroups, isPaginationVisible, tableStyles } = props; const e2eSelectorsTable = selectors.components.Panels.Visualization.Table; - const tableStyles = useStyles2(getTableStyles); return (
{ - const { headerGroups, showTypeIcons } = props; + const { headerGroups, showTypeIcons, tableStyles } = props; const e2eSelectorsTable = selectors.components.Panels.Visualization.Table; - const tableStyles = useStyles2(getTableStyles); return (
diff --git a/packages/grafana-ui/src/components/Table/RowExpander.tsx b/packages/grafana-ui/src/components/Table/RowExpander.tsx index 1e4f5a52cc0..535ac572fc7 100644 --- a/packages/grafana-ui/src/components/Table/RowExpander.tsx +++ b/packages/grafana-ui/src/components/Table/RowExpander.tsx @@ -1,41 +1,23 @@ -import React, { FC } from 'react'; -import { Row } from 'react-table'; +import React from 'react'; -import { useStyles2 } from '../../themes'; import { Icon } from '../Icon/Icon'; -import { getTableStyles } from './styles'; +import { TableStyles } from './styles'; +import { GrafanaTableRow } from './types'; export interface Props { - row: Row; - expandedIndexes: Set; - setExpandedIndexes: (indexes: Set) => void; + row: GrafanaTableRow; + tableStyles: TableStyles; } -export const RowExpander: FC = ({ row, expandedIndexes, setExpandedIndexes }) => { - const tableStyles = useStyles2(getTableStyles); - const isExpanded = expandedIndexes.has(row.index); - // Use Cell to render an expander for each row. - // We can use the getToggleRowExpandedProps prop-getter - // to build the expander. +export function RowExpander({ row, tableStyles }: Props) { return ( -
{ - const newExpandedIndexes = new Set(expandedIndexes); - if (isExpanded) { - newExpandedIndexes.delete(row.index); - } else { - newExpandedIndexes.add(row.index); - } - setExpandedIndexes(newExpandedIndexes); - }} - > +
); -}; +} diff --git a/packages/grafana-ui/src/components/Table/Table.test.tsx b/packages/grafana-ui/src/components/Table/Table.test.tsx index 95ca40bf7a5..5a76193e9cd 100644 --- a/packages/grafana-ui/src/components/Table/Table.test.tsx +++ b/packages/grafana-ui/src/components/Table/Table.test.tsx @@ -4,7 +4,8 @@ import React from 'react'; import { applyFieldOverrides, createTheme, DataFrame, FieldType, toDataFrame } from '@grafana/data'; -import { Props, Table } from './Table'; +import { Table } from './Table'; +import { Props } from './types'; function getDefaultDataFrame(): DataFrame { const dataFrame = toDataFrame({ @@ -555,7 +556,7 @@ describe('Table', () => { { time: '2021-01-01 02:00:00', temperature: '12', link: '12' }, ]); - within(rows[1]).getByLabelText('Open trace').click(); + within(rows[1]).getByLabelText('Expand row').click(); const rowsAfterClick = within(getTable()).getAllByRole('row'); expect(within(rowsAfterClick[1]).getByRole('table')).toBeInTheDocument(); expect(within(rowsAfterClick[1]).getByText(/number0/)).toBeInTheDocument(); diff --git a/packages/grafana-ui/src/components/Table/Table.tsx b/packages/grafana-ui/src/components/Table/Table.tsx index aafd34af3d4..ee0e8da53df 100644 --- a/packages/grafana-ui/src/components/Table/Table.tsx +++ b/packages/grafana-ui/src/components/Table/Table.tsx @@ -1,18 +1,17 @@ import React, { CSSProperties, memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { Cell, - TableState, useAbsoluteLayout, + useExpanded, useFilters, usePagination, useResizeColumns, useSortBy, useTable, } from 'react-table'; -import usePrevious from 'react-use/lib/usePrevious'; import { VariableSizeList } from 'react-window'; -import { DataFrame, getFieldDisplayName, Field, ReducerID } from '@grafana/data'; +import { Field, ReducerID } from '@grafana/data'; import { useStyles2, useTheme2 } from '../../themes'; import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar'; @@ -21,16 +20,10 @@ import { Pagination } from '../Pagination/Pagination'; import { FooterRow } from './FooterRow'; import { HeaderRow } from './HeaderRow'; import { TableCell } from './TableCell'; +import { useFixScrollbarContainer, useResetVariableListSizeCache } from './hooks'; +import { getInitialState, useTableStateReducer } from './reducer'; import { getTableStyles } from './styles'; -import { - TableColumnResizeActionCallback, - TableFilterActionCallback, - FooterItem, - TableSortByActionCallback, - TableSortByFieldState, - TableFooterCalc, - GrafanaTableColumn, -} from './types'; +import { FooterItem, GrafanaTableState, Props } from './types'; import { getColumns, sortCaseInsensitive, @@ -42,91 +35,6 @@ import { const COLUMN_MIN_WIDTH = 150; -export interface Props { - ariaLabel?: string; - data: DataFrame; - width: number; - height: number; - /** Minimal column width specified in pixels */ - columnMinWidth?: number; - noHeader?: boolean; - showTypeIcons?: boolean; - resizable?: boolean; - initialSortBy?: TableSortByFieldState[]; - onColumnResize?: TableColumnResizeActionCallback; - onSortByChange?: TableSortByActionCallback; - onCellFilterAdded?: TableFilterActionCallback; - footerOptions?: TableFooterCalc; - footerValues?: FooterItem[]; - enablePagination?: boolean; - /** @alpha */ - subData?: DataFrame[]; -} - -function useTableStateReducer({ onColumnResize, onSortByChange, data }: Props) { - return useCallback( - (newState: TableState, action: { type: string }) => { - switch (action.type) { - case 'columnDoneResizing': - if (onColumnResize) { - const info = (newState.columnResizing.headerIdWidths as any)[0]; - const columnIdString = info[0]; - const fieldIndex = parseInt(columnIdString, 10); - const width = Math.round(newState.columnResizing.columnWidths[columnIdString] as number); - - const field = data.fields[fieldIndex]; - if (!field) { - return newState; - } - - const fieldDisplayName = getFieldDisplayName(field, data); - onColumnResize(fieldDisplayName, width); - } - case 'toggleSortBy': - if (onSortByChange) { - const sortByFields: TableSortByFieldState[] = []; - - for (const sortItem of newState.sortBy) { - const field = data.fields[parseInt(sortItem.id, 10)]; - if (!field) { - continue; - } - - sortByFields.push({ - displayName: getFieldDisplayName(field, data), - desc: sortItem.desc, - }); - } - - onSortByChange(sortByFields); - } - break; - } - - return newState; - }, - [data, onColumnResize, onSortByChange] - ); -} - -function getInitialState(initialSortBy: Props['initialSortBy'], columns: GrafanaTableColumn[]): Partial { - const state: Partial = {}; - - if (initialSortBy) { - state.sortBy = []; - - for (const sortBy of initialSortBy) { - for (const col of columns) { - if (col.Header === sortBy.displayName) { - state.sortBy.push({ id: col.id!, desc: sortBy.desc }); - } - } - } - } - - return state; -} - export const Table = memo((props: Props) => { const { ariaLabel, @@ -152,8 +60,6 @@ export const Table = memo((props: Props) => { const theme = useTheme2(); const headerHeight = noHeader ? 0 : tableStyles.rowHeight; const [footerItems, setFooterItems] = useState(footerValues); - const [expandedIndexes, setExpandedIndexes] = useState>(new Set()); - const prevExpandedIndexes = usePrevious(expandedIndexes); const footerHeight = useMemo(() => { const EXTENDED_ROW_HEIGHT = headerHeight; @@ -197,18 +103,8 @@ export const Table = memo((props: Props) => { // React-table column definitions const memoizedColumns = useMemo( - () => - getColumns( - data, - width, - columnMinWidth, - expandedIndexes, - setExpandedIndexes, - !!subData?.length, - footerItems, - isCountRowsSet - ), - [data, width, columnMinWidth, footerItems, subData, expandedIndexes, isCountRowsSet] + () => getColumns(data, width, columnMinWidth, !!subData?.length, footerItems, isCountRowsSet), + [data, width, columnMinWidth, footerItems, subData, isCountRowsSet] ); // Internal react table state reducer @@ -242,7 +138,9 @@ export const Table = memo((props: Props) => { gotoPage, setPageSize, pageOptions, - } = useTable(options, useFilters, useSortBy, usePagination, useAbsoluteLayout, useResizeColumns); + } = useTable(options, useFilters, useSortBy, useAbsoluteLayout, useResizeColumns, useExpanded, usePagination); + + const extendedState = state as GrafanaTableState; /* Footer value calculation is being moved in the Table component and the footerValues prop will be deprecated. @@ -299,45 +197,12 @@ export const Table = memo((props: Props) => { setPageSize(pageSize); }, [pageSize, setPageSize]); - useEffect(() => { - // react-table caches the height of cells so we need to reset them when expanding/collapsing rows - // We need to take the minimum of the current expanded indexes and the previous expandedIndexes array to account - // for collapsed rows, since they disappear from expandedIndexes but still keep their expanded height - listRef.current?.resetAfterIndex( - Math.min(...Array.from(expandedIndexes), ...(prevExpandedIndexes ? Array.from(prevExpandedIndexes) : [])) - ); - }, [expandedIndexes, prevExpandedIndexes]); - - useEffect(() => { - // To have the custom vertical scrollbar always visible (https://github.com/grafana/grafana/issues/52136), - // we need to bring the element from the VariableSizeList scope to the outer Table container scope, - // because the VariableSizeList scope has overflow. By moving scrollbar to container scope we will have - // it always visible since the entire width is in view. - - // Select the scrollbar element from the VariableSizeList scope - const listVerticalScrollbarHTML = (variableSizeListScrollbarRef.current as HTMLDivElement)?.querySelector( - '.track-vertical' - ); - - // Select Table custom scrollbars - const tableScrollbarView = (tableDivRef.current as HTMLDivElement)?.firstChild; - - //If they exists, move the scrollbar element to the Table container scope - if (tableScrollbarView && listVerticalScrollbarHTML) { - listVerticalScrollbarHTML?.remove(); - (tableScrollbarView as HTMLDivElement).querySelector(':scope > .track-vertical')?.remove(); - - (tableScrollbarView as HTMLDivElement).append(listVerticalScrollbarHTML as Node); - } - }); - - useEffect(() => { - setExpandedIndexes(new Set()); - }, [data, subData]); + useResetVariableListSizeCache(extendedState, listRef, data); + useFixScrollbarContainer(variableSizeListScrollbarRef, tableDivRef); const renderSubTable = React.useCallback( (rowIndex: number) => { - if (expandedIndexes.has(rowIndex)) { + if (state.expanded[rowIndex]) { const rowSubData = subData?.find((frame) => frame.meta?.custom?.parentRowIndex === rowIndex); if (rowSubData) { const noHeader = !!rowSubData.meta?.custom?.noHeader; @@ -362,7 +227,7 @@ export const Table = memo((props: Props) => { } return null; }, - [expandedIndexes, subData, tableStyles.rowHeight, theme.colors, width] + [state.expanded, subData, tableStyles.rowHeight, theme.colors, width] ); const RenderRow = React.useCallback( @@ -430,7 +295,7 @@ export const Table = memo((props: Props) => { } const getItemSize = (index: number): number => { - if (expandedIndexes.has(index)) { + if (state.expanded[index]) { const rowSubData = subData?.find((frame) => frame.meta?.custom?.parentRowIndex === index); if (rowSubData) { const noHeader = !!rowSubData.meta?.custom?.noHeader; @@ -452,7 +317,9 @@ export const Table = memo((props: Props) => {
- {!noHeader && } + {!noHeader && ( + + )} {itemCount > 0 ? (
@@ -479,6 +346,7 @@ export const Table = memo((props: Props) => { footerValues={footerItems} footerGroups={footerGroups} totalColumnsWidth={totalColumnsWidth} + tableStyles={tableStyles} /> )}
diff --git a/packages/grafana-ui/src/components/Table/hooks.ts b/packages/grafana-ui/src/components/Table/hooks.ts new file mode 100644 index 00000000000..4c096f3a6fc --- /dev/null +++ b/packages/grafana-ui/src/components/Table/hooks.ts @@ -0,0 +1,53 @@ +import React, { useEffect } from 'react'; +import { VariableSizeList } from 'react-window'; + +import { DataFrame } from '@grafana/data'; + +import { GrafanaTableState } from './types'; + +/** + To have the custom vertical scrollbar always visible (https://github.com/grafana/grafana/issues/52136), + we need to bring the element from the VariableSizeList scope to the outer Table container scope, + because the VariableSizeList scope has overflow. By moving scrollbar to container scope we will have + it always visible since the entire width is in view. + Select the scrollbar element from the VariableSizeList scope + */ +export function useFixScrollbarContainer( + variableSizeListScrollbarRef: React.RefObject, + tableDivRef: React.RefObject +) { + useEffect(() => { + const listVerticalScrollbarHTML = (variableSizeListScrollbarRef.current as HTMLDivElement)?.querySelector( + '.track-vertical' + ); + + // Select Table custom scrollbars + const tableScrollbarView = (tableDivRef.current as HTMLDivElement)?.firstChild; + + //If they exists, move the scrollbar element to the Table container scope + if (tableScrollbarView && listVerticalScrollbarHTML) { + listVerticalScrollbarHTML?.remove(); + (tableScrollbarView as HTMLDivElement).querySelector(':scope > .track-vertical')?.remove(); + + (tableScrollbarView as HTMLDivElement).append(listVerticalScrollbarHTML as Node); + } + }); +} + +/** + react-table caches the height of cells so we need to reset them when expanding/collapsing rows + We need to take the minimum of the current expanded indexes and the previous expandedIndexes array to account + for collapsed rows, since they disappear from expandedIndexes but still keep their expanded height + */ +export function useResetVariableListSizeCache( + extendedState: GrafanaTableState, + listRef: React.RefObject, + data: DataFrame +) { + useEffect(() => { + if (extendedState.lastExpandedIndex !== undefined) { + listRef.current?.resetAfterIndex(Math.max(extendedState.lastExpandedIndex - 1, 0)); + return; + } + }, [extendedState.lastExpandedIndex, extendedState.toggleRowExpandedCounter, listRef, data]); +} diff --git a/packages/grafana-ui/src/components/Table/reducer.ts b/packages/grafana-ui/src/components/Table/reducer.ts new file mode 100644 index 00000000000..f8da8dc659e --- /dev/null +++ b/packages/grafana-ui/src/components/Table/reducer.ts @@ -0,0 +1,87 @@ +import { useCallback } from 'react'; + +import { getFieldDisplayName } from '@grafana/data'; + +import { TableSortByFieldState, GrafanaTableColumn, GrafanaTableState, Props } from './types'; + +export interface ActionType { + type: string; + id: string | undefined; +} + +export function useTableStateReducer({ onColumnResize, onSortByChange, data }: Props) { + return useCallback( + (newState: GrafanaTableState, action: ActionType) => { + switch (action.type) { + case 'columnDoneResizing': + if (onColumnResize) { + const info = (newState.columnResizing.headerIdWidths as any)[0]; + const columnIdString = info[0]; + const fieldIndex = parseInt(columnIdString, 10); + const width = Math.round(newState.columnResizing.columnWidths[columnIdString] as number); + + const field = data.fields[fieldIndex]; + if (!field) { + return newState; + } + + const fieldDisplayName = getFieldDisplayName(field, data); + onColumnResize(fieldDisplayName, width); + } + case 'toggleSortBy': + if (onSortByChange) { + const sortByFields: TableSortByFieldState[] = []; + + for (const sortItem of newState.sortBy) { + const field = data.fields[parseInt(sortItem.id, 10)]; + if (!field) { + continue; + } + + sortByFields.push({ + displayName: getFieldDisplayName(field, data), + desc: sortItem.desc, + }); + } + + onSortByChange(sortByFields); + } + case 'toggleRowExpanded': { + if (action.id) { + return { + ...newState, + lastExpandedIndex: parseInt(action.id, 10), + toggleRowExpandedCounter: newState.toggleRowExpandedCounter + 1, + }; + } + } + } + + return newState; + }, + [data, onColumnResize, onSortByChange] + ); +} + +export function getInitialState( + initialSortBy: Props['initialSortBy'], + columns: GrafanaTableColumn[] +): Partial { + const state: Partial = { + toggleRowExpandedCounter: 0, + }; + + if (initialSortBy) { + state.sortBy = []; + + for (const sortBy of initialSortBy) { + for (const col of columns) { + if (col.Header === sortBy.displayName) { + state.sortBy.push({ id: col.id!, desc: sortBy.desc }); + } + } + } + } + + return state; +} diff --git a/packages/grafana-ui/src/components/Table/types.ts b/packages/grafana-ui/src/components/Table/types.ts index 6942ec7594b..d737a019d77 100644 --- a/packages/grafana-ui/src/components/Table/types.ts +++ b/packages/grafana-ui/src/components/Table/types.ts @@ -1,8 +1,8 @@ import { Property } from 'csstype'; import { FC } from 'react'; -import { CellProps, Column, Row } from 'react-table'; +import { CellProps, Column, Row, TableState, UseExpandedRowProps } from 'react-table'; -import { Field, KeyValue, SelectableValue } from '@grafana/data'; +import { DataFrame, Field, KeyValue, SelectableValue } from '@grafana/data'; import { TableStyles } from './styles'; @@ -52,3 +52,31 @@ export interface TableFooterCalc { enablePagination?: boolean; countRows?: boolean; } + +export interface GrafanaTableState extends TableState { + lastExpandedIndex?: number; + toggleRowExpandedCounter: number; +} + +export interface GrafanaTableRow extends Row, UseExpandedRowProps<{}> {} + +export interface Props { + ariaLabel?: string; + data: DataFrame; + width: number; + height: number; + /** Minimal column width specified in pixels */ + columnMinWidth?: number; + noHeader?: boolean; + showTypeIcons?: boolean; + resizable?: boolean; + initialSortBy?: TableSortByFieldState[]; + onColumnResize?: TableColumnResizeActionCallback; + onSortByChange?: TableSortByActionCallback; + onCellFilterAdded?: TableFilterActionCallback; + footerOptions?: TableFooterCalc; + footerValues?: FooterItem[]; + enablePagination?: boolean; + /** @alpha */ + subData?: DataFrame[]; +} diff --git a/packages/grafana-ui/src/components/Table/utils.test.tsx b/packages/grafana-ui/src/components/Table/utils.test.tsx index 293c3511cfa..c28bf5d9ac9 100644 --- a/packages/grafana-ui/src/components/Table/utils.test.tsx +++ b/packages/grafana-ui/src/components/Table/utils.test.tsx @@ -46,21 +46,21 @@ function getData() { describe('Table utils', () => { describe('getColumns', () => { it('Should build columns from DataFrame', () => { - const columns = getColumns(getData(), 1000, 120, new Set(), () => null, false); + const columns = getColumns(getData(), 1000, 120, false); expect(columns[0].Header).toBe('Time'); expect(columns[1].Header).toBe('Value'); }); it('Should distribute width and use field config width', () => { - const columns = getColumns(getData(), 1000, 120, new Set(), () => null, false); + const columns = getColumns(getData(), 1000, 120, false); expect(columns[0].width).toBe(450); expect(columns[1].width).toBe(100); }); it('Should distribute width and use field config width with expander enabled', () => { - const columns = getColumns(getData(), 1000, 120, new Set(), () => null, true); + const columns = getColumns(getData(), 1000, 120, true); expect(columns[0].width).toBe(50); // expander column expect(columns[1].width).toBe(425); @@ -68,7 +68,7 @@ describe('Table utils', () => { }); it('Should set field on columns', () => { - const columns = getColumns(getData(), 1000, 120, new Set(), () => null, false); + const columns = getColumns(getData(), 1000, 120, false); expect(columns[0].field.name).toBe('Time'); expect(columns[1].field.name).toBe('Value'); diff --git a/packages/grafana-ui/src/components/Table/utils.tsx b/packages/grafana-ui/src/components/Table/utils.tsx index fd5f1b46c88..89fb0f11dfa 100644 --- a/packages/grafana-ui/src/components/Table/utils.tsx +++ b/packages/grafana-ui/src/components/Table/utils.tsx @@ -1,7 +1,6 @@ import { Property } from 'csstype'; import { clone } from 'lodash'; import memoizeOne from 'memoize-one'; -import React from 'react'; import { Row } from 'react-table'; import { @@ -65,35 +64,29 @@ export function getColumns( data: DataFrame, availableWidth: number, columnMinWidth: number, - expandedIndexes: Set, - setExpandedIndexes: (indexes: Set) => void, expander: boolean, footerValues?: FooterItem[], isCountRowsSet?: boolean ): GrafanaTableColumn[] { - const columns: GrafanaTableColumn[] = expander - ? [ - { - // Make an expander cell - Header: () => null, // No header - id: 'expander', // It needs an ID - Cell: ({ row }) => { - return ; - }, - width: EXPANDER_WIDTH, - minWidth: EXPANDER_WIDTH, - filter: (rows: Row[], id: string, filterValues?: SelectableValue[]) => { - return []; - }, - justifyContent: 'left', - field: data.fields[0], - sortType: 'basic', - }, - ] - : []; + const columns: GrafanaTableColumn[] = []; let fieldCountWithoutWidth = 0; if (expander) { + columns.push({ + // Make an expander cell + Header: () => null, // No header + id: 'expander', // It needs an ID + Cell: RowExpander, + width: EXPANDER_WIDTH, + minWidth: EXPANDER_WIDTH, + filter: (rows: Row[], id: string, filterValues?: SelectableValue[]) => { + return []; + }, + justifyContent: 'left', + field: data.fields[0], + sortType: 'basic', + }); + availableWidth -= EXPANDER_WIDTH; }