From 0083b2e346bb81ef4749a36c3ca1ca939812f034 Mon Sep 17 00:00:00 2001 From: Kristina Date: Thu, 13 Jul 2023 06:44:42 -0500 Subject: [PATCH] Explore: Support display of multiple tables (#70935) * Explore: Display multiple tables * Fix tests * Add multi table test * Use refID and not weird custom key * Remove tempo changes * Consolidate/clean up some table frame logic * clean up some code, have meta text in panel --- .../explore/Table/TableContainer.test.tsx | 103 ++++++++++-------- .../features/explore/Table/TableContainer.tsx | 81 ++++++++------ 2 files changed, 105 insertions(+), 79 deletions(-) diff --git a/public/app/features/explore/Table/TableContainer.test.tsx b/public/app/features/explore/Table/TableContainer.test.tsx index 4fecc5cf6a2..c8db16caca3 100644 --- a/public/app/features/explore/Table/TableContainer.test.tsx +++ b/public/app/features/explore/Table/TableContainer.test.tsx @@ -5,8 +5,8 @@ import { DataFrame, FieldType, getDefaultTimeRange, InternalTimeZones, toDataFra import { TableContainer } from './TableContainer'; -function getTable(): HTMLElement { - return screen.getAllByRole('table')[0]; +function getTables(): HTMLElement[] { + return screen.getAllByRole('table'); } function getRowsData(rows: HTMLElement[]): Object[] { @@ -58,48 +58,63 @@ const defaultProps = { }; describe('TableContainer', () => { - it('should render component', () => { - render(); - expect(getTable()).toBeInTheDocument(); - const rows = within(getTable()).getAllByRole('row'); - expect(rows).toHaveLength(5); - expect(getRowsData(rows)).toEqual([ - { time: '2021-01-01 00:00:00', text: 'test_string_1' }, - { time: '2021-01-01 03:00:00', text: 'test_string_2' }, - { time: '2021-01-01 01:00:00', text: 'test_string_3' }, - { time: '2021-01-01 02:00:00', text: 'test_string_4' }, - ]); + describe('With one main frame', () => { + it('should render component', () => { + render(); + const tables = getTables(); + expect(tables.length).toBe(1); + expect(tables[0]).toBeInTheDocument(); + const rows = within(tables[0]).getAllByRole('row'); + expect(rows).toHaveLength(5); + expect(getRowsData(rows)).toEqual([ + { time: '2021-01-01 00:00:00', text: 'test_string_1' }, + { time: '2021-01-01 03:00:00', text: 'test_string_2' }, + { time: '2021-01-01 01:00:00', text: 'test_string_3' }, + { time: '2021-01-01 02:00:00', text: 'test_string_4' }, + ]); + }); + + it('should render 0 series returned on no items', () => { + const emptyFrames: DataFrame[] = [ + { + name: 'TableResultName', + fields: [], + length: 0, + }, + ]; + render(); + expect(screen.getByText('0 series returned')).toBeInTheDocument(); + }); + + it('should update time when timezone changes', () => { + const { rerender } = render(); + const rowsBeforeChange = within(getTables()[0]).getAllByRole('row'); + expect(getRowsData(rowsBeforeChange)).toEqual([ + { time: '2021-01-01 00:00:00', text: 'test_string_1' }, + { time: '2021-01-01 03:00:00', text: 'test_string_2' }, + { time: '2021-01-01 01:00:00', text: 'test_string_3' }, + { time: '2021-01-01 02:00:00', text: 'test_string_4' }, + ]); + + rerender(); + const rowsAfterChange = within(getTables()[0]).getAllByRole('row'); + expect(getRowsData(rowsAfterChange)).toEqual([ + { time: '2020-12-31 19:00:00', text: 'test_string_1' }, + { time: '2020-12-31 22:00:00', text: 'test_string_2' }, + { time: '2020-12-31 20:00:00', text: 'test_string_3' }, + { time: '2020-12-31 21:00:00', text: 'test_string_4' }, + ]); + }); }); - - it('should render 0 series returned on no items', () => { - const emptyFrames: DataFrame[] = [ - { - name: 'TableResultName', - fields: [], - length: 0, - }, - ]; - render(); - expect(screen.getByText('0 series returned')).toBeInTheDocument(); - }); - - it('should update time when timezone changes', () => { - const { rerender } = render(); - const rowsBeforeChange = within(getTable()).getAllByRole('row'); - expect(getRowsData(rowsBeforeChange)).toEqual([ - { time: '2021-01-01 00:00:00', text: 'test_string_1' }, - { time: '2021-01-01 03:00:00', text: 'test_string_2' }, - { time: '2021-01-01 01:00:00', text: 'test_string_3' }, - { time: '2021-01-01 02:00:00', text: 'test_string_4' }, - ]); - - rerender(); - const rowsAfterChange = within(getTable()).getAllByRole('row'); - expect(getRowsData(rowsAfterChange)).toEqual([ - { time: '2020-12-31 19:00:00', text: 'test_string_1' }, - { time: '2020-12-31 22:00:00', text: 'test_string_2' }, - { time: '2020-12-31 20:00:00', text: 'test_string_3' }, - { time: '2020-12-31 21:00:00', text: 'test_string_4' }, - ]); + describe('With multiple main frames', () => { + it('should render multiple tables for multiple frames', () => { + const dataFrames = [dataFrame, dataFrame]; + const multiDefaultProps = { ...defaultProps, tableResult: dataFrames }; + render(); + const tables = getTables(); + expect(tables.length).toBe(2); + expect(tables[0]).toBeInTheDocument(); + expect(tables[1]).toBeInTheDocument(); + }); }); }); diff --git a/public/app/features/explore/Table/TableContainer.tsx b/public/app/features/explore/Table/TableContainer.tsx index 531189fc40b..9a534b4159c 100644 --- a/public/app/features/explore/Table/TableContainer.tsx +++ b/public/app/features/explore/Table/TableContainer.tsx @@ -34,25 +34,21 @@ const connector = connect(mapStateToProps, {}); type Props = TableContainerProps & ConnectedProps; export class TableContainer extends PureComponent { - getMainFrame(frames: DataFrame[] | null) { - return frames?.find((df) => df.meta?.custom?.parentRowIndex === undefined) || frames?.[0]; + getMainFrames(frames: DataFrame[] | null) { + return frames?.filter((df) => df.meta?.custom?.parentRowIndex === undefined) || [frames?.[0]]; } - getTableHeight() { - const { tableResult } = this.props; - const mainFrame = this.getMainFrame(tableResult); - - if (!mainFrame || mainFrame.length === 0) { + getTableHeight(rowCount: number, isSingleTable = true) { + if (rowCount === 0) { return 200; } - - // tries to estimate table height - return Math.min(600, Math.max(mainFrame.length * 36, 300) + 40 + 46); + // tries to estimate table height, with a min of 300 and a max of 600 + // if there are multiple tables, there is no min + return Math.min(600, Math.max(rowCount * 36, isSingleTable ? 300 : 0) + 40 + 46); } render() { const { loading, onCellFilterAdded, tableResult, width, splitOpenFn, range, ariaLabel, timeZone } = this.props; - const height = this.getTableHeight(); let dataFrames = tableResult; @@ -85,33 +81,48 @@ export class TableContainer extends PureComponent { } } - const mainFrame = this.getMainFrame(dataFrames); - const subFrames = dataFrames?.filter((df) => df.meta?.custom?.parentRowIndex !== undefined); + // move dataframes to be grouped by table, with optional sub-tables for a row + const tableData: Array<{ main: DataFrame; sub?: DataFrame[] }> = []; + const mainFrames = this.getMainFrames(dataFrames).filter( + (frame: DataFrame | undefined): frame is DataFrame => !!frame && frame.length !== 0 + ); + + mainFrames?.forEach((frame) => { + const subFrames = + dataFrames?.filter((df) => frame.refId === df.refId && df.meta?.custom?.parentRowIndex !== undefined) || + undefined; + tableData.push({ main: frame, sub: subFrames }); + }); return ( - - {(innerWidth, innerHeight) => ( - <> - {mainFrame?.length ? ( - - ) : ( - - )} - + <> + {tableData.length === 0 && ( + + {() => } + )} - + {tableData.length > 0 && + tableData.map((data, i) => ( + 1 ? `Table - ${data.main.name || data.main.refId || i}` : 'Table'} + width={width} + height={this.getTableHeight(data.main.length, tableData.length === 1)} + loadingState={loading ? LoadingState.Loading : undefined} + > + {(innerWidth, innerHeight) => ( +
+ )} + + ))} + ); } }