diff --git a/packages/dx-react-grid/src/components/table-layout/virtual-table-layout.test.tsx b/packages/dx-react-grid/src/components/table-layout/virtual-table-layout.test.tsx index 99bb3ffdd4..7092a19688 100644 --- a/packages/dx-react-grid/src/components/table-layout/virtual-table-layout.test.tsx +++ b/packages/dx-react-grid/src/components/table-layout/virtual-table-layout.test.tsx @@ -537,4 +537,88 @@ describe('VirtualTableLayout', () => { }); }); }); + + describe('row heights', () => { + const getBoundingClientRect = () => ({ + height: 70, + }); + + const newProps = { + ...defaultProps, + tableComponent: ({ forwardedRef, ...props }) => { + (forwardedRef as any).current = { getBoundingClientRect }; + return ; + }, + rowComponent: ({ forwardedRef }) => { + (forwardedRef as any)({ getBoundingClientRect }); + return null; + }, + }; + + it('should specify correct row height at startup', () => { + expect.hasAssertions(); + + const rows = [ + { key: 1 }, + { key: 2, height: 10 }, + ]; + + getCollapsedGrids + .mockImplementationOnce((args) => { + const { getRowHeight } = args; + expect(getRowHeight(rows[0])) + .toEqual(newProps.estimatedRowHeight); + expect(getRowHeight(rows[1])) + .toEqual(10); + + return jest.requireActual('@devexpress/dx-grid-core').getCollapsedGrids(args); + }); + + mount(( + + )); + }); + + it('should store row height when rendered', () => { + const rows = [ + { key: 1 }, + { key: 2, height: 10 }, + ]; + + mount(( + + )); + + const { getRowHeight } = getCollapsedGrids.mock.calls[0][0]; + expect(getRowHeight(rows[0])) + .toEqual(70); + expect(getRowHeight(rows[1])) + .toEqual(70); + }); + + it('should clear row height when rows updated', () => { + const rows = [ + { key: 11 }, + { key: 12 }, + ]; + + const tree = mount(( + + )); + tree.setProps({ bodyRows: [rows[0]] }); + + const { getRowHeight } = getCollapsedGrids.mock.calls[0][0]; + expect(getRowHeight(rows[1])) + .toEqual(newProps.estimatedRowHeight); + }); + }); }); diff --git a/packages/dx-react-grid/src/components/table-layout/virtual-table-layout.tsx b/packages/dx-react-grid/src/components/table-layout/virtual-table-layout.tsx index 06b5261581..10f52b4f43 100644 --- a/packages/dx-react-grid/src/components/table-layout/virtual-table-layout.tsx +++ b/packages/dx-react-grid/src/components/table-layout/virtual-table-layout.tsx @@ -35,6 +35,7 @@ export class VirtualTableLayout extends React.PureComponent(), viewportTop: 0, skipItems: [0, 0], containerHeight: 600, @@ -63,13 +64,19 @@ export class VirtualTableLayout extends React.PureComponent { + const rowHeight = prevRowHeight.get(row.key); + if (rowHeight !== undefined) { + acc.set(row.key, rowHeight); + } + return acc; + }, + new Map(), + ); + return { rowHeights }; + } + getRowHeight = (row) => { + const { rowHeights } = this.state; + const { estimatedRowHeight } = this.props; if (row) { - const realHeight = this.rowRefs.get(row.key)?.getBoundingClientRect().height; - return row.height || realHeight || this.props.estimatedRowHeight; + const storedHeight = rowHeights.get(row.key); + if (storedHeight !== undefined) return storedHeight; + if (row.height) return row.height; + } + return estimatedRowHeight; + } + + storeRowHeights() { + const rowsWithChangedHeights = Array.from(this.rowRefs.entries()) + .map(([row, ref]) => [row, ref]) + .filter(([, node]) => !!node) + .map(([row, node]) => [row, node.getBoundingClientRect().height]) + .filter(([row, height]) => row.type !== TABLE_STUB_TYPE && height !== this.getRowHeight(row)); + + if (rowsWithChangedHeights.length) { + const { rowHeights } = this.state; + rowsWithChangedHeights + .forEach(([row, height]) => rowHeights.set(row.key, height)); + + this.setState({ + rowHeights, + }); } - return this.props.estimatedRowHeight; } onScroll = (e) => { diff --git a/packages/dx-react-grid/src/types/layout/table-layout.types.ts b/packages/dx-react-grid/src/types/layout/table-layout.types.ts index 58ced17d95..bbe7f87230 100644 --- a/packages/dx-react-grid/src/types/layout/table-layout.types.ts +++ b/packages/dx-react-grid/src/types/layout/table-layout.types.ts @@ -53,6 +53,7 @@ export interface VirtualTableLayoutProps extends TableLayoutProps { } /** @internal */ export type VirtualTableLayoutState = { + rowHeights: Map, viewportTop: number, skipItems: [number, number], containerHeight: number,