diff --git a/src/table/__tests__/columns-width.test.tsx b/src/table/__tests__/columns-width.test.tsx index 9ec3a24158..d5ffe86bc9 100644 --- a/src/table/__tests__/columns-width.test.tsx +++ b/src/table/__tests__/columns-width.test.tsx @@ -296,6 +296,70 @@ test('prints a warning when resizable columns have non-numeric width', () => { ); }); +describe('measurement on re-render', () => { + const stableColumns: TableProps.ColumnDefinition[] = [ + { id: 'id', header: 'id', cell: item => item.id, width: 150 }, + { id: 'text', header: 'text', cell: item => item.text, width: 200 }, + ]; + + test('does not measure column widths when re-rendering with unchanged columns', () => { + const getBoundingClientRectSpy = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect'); + const { rerender } = renderTable( + + ); + + // Ignore measurements from the initial render; only re-renders should be measurement-free. + getBoundingClientRectSpy.mockClear(); + + // Re-render with new items but the same column definitions (e.g. typing in the filter box). + rerender( +
+ ); + + expect(getBoundingClientRectSpy).not.toHaveBeenCalled(); + + getBoundingClientRectSpy.mockRestore(); + }); + + test('does measure column widths when the columns change', () => { + const getBoundingClientRectSpy = jest.spyOn(HTMLElement.prototype, 'getBoundingClientRect'); + const { rerender } = renderTable( +
+ ); + + getBoundingClientRectSpy.mockClear(); + + // Re-render with an added column: widths must be recomputed, so measurement is expected. + rerender( +
'-' }]} + items={defaultItems} + resizableColumns={true} + stickyColumns={{ first: 1 }} + /> + ); + + expect(getBoundingClientRectSpy).toHaveBeenCalled(); + + getBoundingClientRectSpy.mockRestore(); + }); +}); + describe('with stickyHeader=true', () => { const originalFn = window.CSS.supports; beforeEach(() => { diff --git a/src/table/internal.tsx b/src/table/internal.tsx index 800214b57e..d58a59f1f9 100644 --- a/src/table/internal.tsx +++ b/src/table/internal.tsx @@ -1,6 +1,6 @@ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. // SPDX-License-Identifier: Apache-2.0 -import React, { useCallback, useImperativeHandle, useRef } from 'react'; +import React, { useCallback, useImperativeHandle, useMemo, useRef } from 'react'; import clsx from 'clsx'; import { useContainerQuery } from '@cloudscape-design/component-toolkit'; @@ -299,11 +299,10 @@ const InternalTable = React.forwardRef( const { moveFocusDown, moveFocusUp, moveFocus } = useSelectionFocusMove(selectionType, allItems.length); const { onRowClickHandler, onRowContextMenuHandler } = useRowEvents({ onRowClick, onRowContextMenu }); - const visibleColumnDefinitions = getVisibleColumnDefinitions({ - columnDefinitions, - columnDisplay, - visibleColumns, - }); + const visibleColumnDefinitions = useMemo( + () => getVisibleColumnDefinitions({ columnDefinitions, columnDisplay, visibleColumns }), + [columnDefinitions, columnDisplay, visibleColumns] + ); const visibleColumnIds = visibleColumnDefinitions.map((col, idx) => getColumnKey(col, idx).toString()); @@ -374,17 +373,20 @@ const InternalTable = React.forwardRef( headerIdRef.current = id; }, []); - const visibleColumnWidthsWithSelection: ColumnWidthDefinition[] = []; - const visibleColumnIdsWithSelection: PropertyKey[] = []; - if (hasSelection) { - visibleColumnWidthsWithSelection.push({ id: selectionColumnId, width: SELECTION_COLUMN_WIDTH }); - visibleColumnIdsWithSelection.push(selectionColumnId); - } - for (let columnIndex = 0; columnIndex < visibleColumnDefinitions.length; columnIndex++) { - const columnId = getColumnKey(visibleColumnDefinitions[columnIndex], columnIndex); - visibleColumnWidthsWithSelection.push({ ...visibleColumnDefinitions[columnIndex], id: columnId }); - visibleColumnIdsWithSelection.push(columnId); - } + const { visibleColumnWidthsWithSelection, visibleColumnIdsWithSelection } = useMemo(() => { + const widths: ColumnWidthDefinition[] = []; + const ids: PropertyKey[] = []; + if (hasSelection) { + widths.push({ id: selectionColumnId, width: SELECTION_COLUMN_WIDTH }); + ids.push(selectionColumnId); + } + for (let columnIndex = 0; columnIndex < visibleColumnDefinitions.length; columnIndex++) { + const columnId = getColumnKey(visibleColumnDefinitions[columnIndex], columnIndex); + widths.push({ ...visibleColumnDefinitions[columnIndex], id: columnId }); + ids.push(columnId); + } + return { visibleColumnWidthsWithSelection: widths, visibleColumnIdsWithSelection: ids }; + }, [hasSelection, visibleColumnDefinitions]); const stickyState = useStickyColumns({ visibleColumns: visibleColumnIdsWithSelection,