Commit 3f7b058b by Peter Holmberg Committed by GitHub

Panel inspect: Horizontal scrolling in Data table (#22245)

* First try at horizontal scrolling

* move width logic to table

* Update packages/grafana-ui/src/components/Table/Table.tsx

Co-Authored-By: Dominik Prokop <dominik.prokop@grafana.com>

* wrap table with memo

* fix typo

* re add field

* WIP: Table scrolling troubles

* Annother approach

* Think it's working

* Removed unnessary change

* Table: Added custom scrollbar for horizontal scrolling

* Removed console log and fixed test

Co-authored-by: Dominik Prokop <dominik.prokop@grafana.com>
Co-authored-by: Torkel Ödegaard <torkel@grafana.com>
parent 066d5cf4
import React, { useMemo } from 'react';
import React, { FC, memo, useMemo } from 'react';
import { DataFrame, Field } from '@grafana/data';
import { useSortBy, useTable, useBlockLayout, Cell } from 'react-table';
import { FixedSizeList } from 'react-window';
......@@ -10,22 +10,25 @@ import { getTableStyles } from './styles';
import { TableCell } from './TableCell';
import { Icon } from '../Icon/Icon';
import { getTextAlign } from './utils';
import { CustomScrollbar } from '../CustomScrollbar/CustomScrollbar';
export interface Props {
data: DataFrame;
width: number;
height: number;
/** Minimal column width specified in pixels */
columnMinWidth?: number;
onCellClick?: TableFilterActionCallback;
}
export const Table = ({ data, height, onCellClick, width }: Props) => {
export const Table: FC<Props> = memo(({ data, height, onCellClick, width, columnMinWidth }) => {
const theme = useTheme();
const [ref, headerRowMeasurements] = useMeasure();
const tableStyles = getTableStyles(theme);
const { getTableProps, headerGroups, rows, prepareRow } = useTable(
{
columns: useMemo(() => getColumns(data, width), [data]),
columns: useMemo(() => getColumns(data, width, columnMinWidth ?? 150), [data, width, columnMinWidth]),
data: useMemo(() => getTableRows(data), [data]),
},
useSortBy,
......@@ -53,28 +56,39 @@ export const Table = ({ data, height, onCellClick, width }: Props) => {
[prepareRow, rows]
);
let totalWidth = 0;
for (const headerGroup of headerGroups) {
for (const header of headerGroup.headers) {
totalWidth += header.width as number;
}
}
return (
<div {...getTableProps()} className={tableStyles.table}>
<div>
{headerGroups.map((headerGroup: any) => (
<div className={tableStyles.thead} {...headerGroup.getHeaderGroupProps()} ref={ref}>
{headerGroup.headers.map((column: any) =>
renderHeaderCell(column, tableStyles.headerCell, data.fields[column.index])
)}
</div>
))}
</div>
<FixedSizeList
height={height - headerRowMeasurements.height}
itemCount={rows.length}
itemSize={tableStyles.rowHeight}
width={width}
>
{RenderRow}
</FixedSizeList>
<CustomScrollbar>
<div>
{headerGroups.map((headerGroup: any) => (
<div className={tableStyles.thead} {...headerGroup.getHeaderGroupProps()} ref={ref}>
{headerGroup.headers.map((column: any) =>
renderHeaderCell(column, tableStyles.headerCell, data.fields[column.index])
)}
</div>
))}
</div>
<FixedSizeList
height={height - headerRowMeasurements.height}
itemCount={rows.length}
itemSize={tableStyles.rowHeight}
width={totalWidth ?? width}
style={{ overflow: 'hidden auto' }}
>
{RenderRow}
</FixedSizeList>
</CustomScrollbar>
</div>
);
};
});
function renderHeaderCell(column: any, className: string, field: Field) {
const headerProps = column.getHeaderProps(column.getSortByToggleProps());
......
......@@ -31,8 +31,10 @@ export const getTableStyles = stylesFactory(
cellHeightInner: bodyFontSize * lineHeight,
rowHeight: cellHeight + 2,
table: css`
height: 100%;
width: 100%;
overflow: auto;
border-spacing: 0;
display: flex;
`,
thead: css`
label: thead;
......
......@@ -40,12 +40,13 @@ describe('Table utils', () => {
});
it('Should distribute width and use field config width', () => {
const columns = getColumns(getData(), 1000);
const columns = getColumns(getData(), 1000, 120);
expect(columns[0].width).toBe(450);
expect(columns[1].width).toBe(100);
});
});
describe('getTextAlign', () => {
it('Should use textAlign from custom', () => {
const data = getData();
......
......@@ -42,7 +42,7 @@ export function getTextAlign(field: Field): TextAlignProperty {
return 'left';
}
export function getColumns(data: DataFrame, availableWidth: number): Column[] {
export function getColumns(data: DataFrame, availableWidth: number, columnMinWidth: number): Column[] {
const columns: Column[] = [];
let fieldCountWithoutWidth = data.fields.length;
......@@ -67,7 +67,7 @@ export function getColumns(data: DataFrame, availableWidth: number): Column[] {
const sharedWidth = availableWidth / fieldCountWithoutWidth;
for (const column of columns) {
if (!column.width) {
column.width = sharedWidth;
column.width = Math.max(sharedWidth, columnMinWidth);
}
}
......
......@@ -219,6 +219,7 @@ export class PanelInspector extends PureComponent<Props, State> {
if (width === 0) {
return null;
}
return (
<div style={{ width, height }}>
<Table width={width} height={height} data={processed[selected]} />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment