From 73ab088804abedc99d8d104017a3f88e47e878db Mon Sep 17 00:00:00 2001 From: Paul Marbach Date: Fri, 11 Jul 2025 08:50:25 -0400 Subject: [PATCH] TableNG: Scrollbar width handling (#107994) * TableNG: Scrollbar width handling * slight cleanup * fix types * fixie --------- Co-authored-by: Leon Sorokin --- .../src/components/Table/TableNG/TableNG.tsx | 11 ++++++++--- .../src/components/Table/TableNG/hooks.ts | 18 ++++++++++++++++-- 2 files changed, 24 insertions(+), 5 deletions(-) diff --git a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx index 129eb256c97..dbe5dd3f1a0 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx +++ b/packages/grafana-ui/src/components/Table/TableNG/TableNG.tsx @@ -1,11 +1,12 @@ import 'react-data-grid/lib/styles.css'; import { css, cx } from '@emotion/css'; import { Property } from 'csstype'; -import { Key, ReactNode, useCallback, useLayoutEffect, useMemo, useState } from 'react'; +import { Key, ReactNode, useCallback, useLayoutEffect, useMemo, useRef, useState } from 'react'; import { Cell, CellRendererProps, DataGrid, + DataGridHandle, DataGridProps, RenderCellProps, RenderRowProps, @@ -39,6 +40,7 @@ import { useHeaderHeight, usePaginatedRows, useRowHeight, + useScrollbarWidth, useSortedRows, useTypographyCtx, } from './hooks'; @@ -151,9 +153,11 @@ export function TableNG(props: TableNGProps) { // vt scrollbar accounting for column auto-sizing const visibleFields = useMemo(() => getVisibleFields(data.fields), [data.fields]); + const gridRef = useRef(null); + const scrollbarWidth = useScrollbarWidth(gridRef, height, sortedRows); const availableWidth = useMemo( - () => (hasNestedFrames ? width - COLUMN.EXPANDER_WIDTH : width), - [width, hasNestedFrames] + () => (hasNestedFrames ? width - COLUMN.EXPANDER_WIDTH : width) - scrollbarWidth, + [width, hasNestedFrames, scrollbarWidth] ); const typographyCtx = useTypographyCtx(); const widths = useMemo(() => computeColWidths(visibleFields, availableWidth), [visibleFields, availableWidth]); @@ -549,6 +553,7 @@ export function TableNG(props: TableNGProps) { <> {...commonDataGridProps} + ref={gridRef} className={styles.grid} columns={structureRevColumns} rows={paginatedRows} diff --git a/packages/grafana-ui/src/components/Table/TableNG/hooks.ts b/packages/grafana-ui/src/components/Table/TableNG/hooks.ts index 4ef2c8f185e..c4bcabe0828 100644 --- a/packages/grafana-ui/src/components/Table/TableNG/hooks.ts +++ b/packages/grafana-ui/src/components/Table/TableNG/hooks.ts @@ -1,5 +1,5 @@ -import { useState, useMemo, useEffect, useCallback, useRef, useLayoutEffect } from 'react'; -import { Column, DataGridProps, SortColumn } from 'react-data-grid'; +import { useState, useMemo, useEffect, useCallback, useRef, useLayoutEffect, RefObject } from 'react'; +import { Column, DataGridHandle, DataGridProps, SortColumn } from 'react-data-grid'; import { varPreLine } from 'uwrap'; import { Field, fieldReducers, FieldType, formattedValueToString, LinkModel, reduceField } from '@grafana/data'; @@ -608,3 +608,17 @@ export function useSingleLink(field: Field, rowIdx: number): LinkModel | undefin const shouldShowLink = linksCount === 1 && actionsCount === 0; return useMemo(() => (shouldShowLink ? (getCellLinks(field, rowIdx) ?? []) : [])[0], [field, shouldShowLink, rowIdx]); } + +export function useScrollbarWidth(ref: RefObject, height: number, renderedRows: TableRow[]) { + const [scrollbarWidth, setScrollbarWidth] = useState(0); + + useLayoutEffect(() => { + const el = ref.current?.element; + + if (el) { + setScrollbarWidth(el.offsetWidth - el.clientWidth); + } + }, [ref, height, renderedRows]); + + return scrollbarWidth; +}