Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DataGrid] Performance: avoid layout invalidation #12238

Closed
wants to merge 58 commits into from
Closed
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
ad9e8bf
perf: remove cellContent
romgrk Feb 9, 2024
f7c1fee
perf: remove emotion wrapper
romgrk Feb 9, 2024
c5e6102
fix: resizing
romgrk Feb 9, 2024
012266d
fix: resize styles
romgrk Feb 9, 2024
fbb071e
lint
romgrk Feb 9, 2024
64cba14
docs: update
romgrk Feb 9, 2024
7dabf06
fix: alignment
romgrk Feb 9, 2024
dd65a9a
test: fix reorder
romgrk Feb 9, 2024
bb5c5f6
fix: rating style
romgrk Feb 9, 2024
7f6ee6a
fix: styles
romgrk Feb 10, 2024
6331a57
fix: style invalidation
romgrk Feb 10, 2024
c7a7dd8
lint
romgrk Feb 10, 2024
3eff377
lint
romgrk Feb 10, 2024
c5dc461
fix: pinned border
romgrk Feb 10, 2024
da2c2f1
fix: empty columns bug
romgrk Feb 10, 2024
f1a819f
fix: empty rows bug
romgrk Feb 10, 2024
a821c66
fix: layout ownerState test failing
romgrk Feb 10, 2024
380995c
test: fix offset top
romgrk Feb 10, 2024
2b3c933
docs: api
romgrk Feb 10, 2024
1a03542
lint
romgrk Feb 10, 2024
490d429
fix: styles
romgrk Feb 10, 2024
959f038
docs: api
romgrk Feb 10, 2024
aed570f
lint
romgrk Feb 10, 2024
169c302
fix: style
romgrk Feb 10, 2024
c90621c
Merge branches 'perf-dom-changes' and 'perf-style-invalidation' into my
romgrk Feb 10, 2024
6f60b7f
refactor: remove render zone, use position:absolute
romgrk Feb 10, 2024
9510b61
draft: append-only scroll
romgrk Feb 10, 2024
2b41c33
perf: remove offsets.top, keep offsetLeft
romgrk Feb 11, 2024
69e22fe
draft: benchmark
romgrk Feb 11, 2024
f338d47
lint
romgrk Feb 11, 2024
4a2a643
Merge branch 'perf-style-invalidation' into perf-scrolling-complex
romgrk Feb 11, 2024
e529580
perf: avoid colSpan computations when not needed
romgrk Feb 11, 2024
d2e3313
refactor: focus cell handling
romgrk Feb 12, 2024
9c8333e
feat: virtualization cleanup
romgrk Feb 12, 2024
551fbda
lint
romgrk Feb 12, 2024
db15000
lint
romgrk Feb 13, 2024
f367e0a
Merge branch 'perf-dom-changes' into perf-scrolling-complex
romgrk Feb 13, 2024
c4b35ab
Merge branch 'next' into perf-scrolling-complex
romgrk Feb 23, 2024
167322e
lint
romgrk Feb 23, 2024
c71ffcc
fix: missed offsets
romgrk Feb 23, 2024
f82c480
Merge branch 'next' into perf-scrolling-complex
romgrk Feb 28, 2024
a580629
refactor: offsets & layout
romgrk Feb 28, 2024
af0d18b
lint
romgrk Feb 28, 2024
ece8b7e
lint
romgrk Feb 28, 2024
a1dfa4e
lint
romgrk Feb 28, 2024
4461cba
lint
romgrk Feb 28, 2024
af63bff
lint
romgrk Feb 28, 2024
92ed561
style
romgrk Feb 28, 2024
e89ca7f
fix: tests
romgrk Feb 28, 2024
b4b9c11
fix: render context update
romgrk Feb 28, 2024
23a59f1
fix
romgrk Feb 28, 2024
5ed191a
fix: autosize
romgrk Feb 28, 2024
0aa8cee
fix: tests
romgrk Feb 28, 2024
0609363
Merge branch 'perf-scrolling-complex' into perf-dom-layout
romgrk Feb 29, 2024
bba1c93
Merge branch 'next' into perf-dom-layout
romgrk Feb 29, 2024
bccac75
fix
romgrk Feb 29, 2024
c65339c
draft: event-based reactivity
romgrk Feb 29, 2024
05a4a8f
fix: top => transform/translate
romgrk Feb 29, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions docs/pages/x/api/data-grid/data-grid-premium.json
Original file line number Diff line number Diff line change
Expand Up @@ -1875,12 +1875,6 @@
"description": "Styles applied to the virtualization content when its height is bigger than the virtualization container.",
"isGlobal": false
},
{
"key": "virtualScrollerRenderZone",
"className": "MuiDataGridPremium-virtualScrollerRenderZone",
"description": "Styles applied to the virtualization render zone.",
"isGlobal": false
},
{
"key": "withBorderColor",
"className": "MuiDataGridPremium-withBorderColor",
Expand Down
6 changes: 0 additions & 6 deletions docs/pages/x/api/data-grid/data-grid-pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -1796,12 +1796,6 @@
"description": "Styles applied to the virtualization content when its height is bigger than the virtualization container.",
"isGlobal": false
},
{
"key": "virtualScrollerRenderZone",
"className": "MuiDataGridPro-virtualScrollerRenderZone",
"description": "Styles applied to the virtualization render zone.",
"isGlobal": false
},
{
"key": "withBorderColor",
"className": "MuiDataGridPro-withBorderColor",
Expand Down
6 changes: 0 additions & 6 deletions docs/pages/x/api/data-grid/data-grid.json
Original file line number Diff line number Diff line change
Expand Up @@ -1657,12 +1657,6 @@
"description": "Styles applied to the virtualization content when its height is bigger than the virtualization container.",
"isGlobal": false
},
{
"key": "virtualScrollerRenderZone",
"className": "MuiDataGrid-virtualScrollerRenderZone",
"description": "Styles applied to the virtualization render zone.",
"isGlobal": false
},
{
"key": "withBorderColor",
"className": "MuiDataGrid-withBorderColor",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1207,10 +1207,6 @@
"nodeName": "the virtualization content",
"conditions": "its height is bigger than the virtualization container"
},
"virtualScrollerRenderZone": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the virtualization render zone"
},
"withBorderColor": {
"description": "Styles applied to {{nodeName}}, {{conditions}}. Sets border color only.",
"nodeName": "cells",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1149,10 +1149,6 @@
"nodeName": "the virtualization content",
"conditions": "its height is bigger than the virtualization container"
},
"virtualScrollerRenderZone": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the virtualization render zone"
},
"withBorderColor": {
"description": "Styles applied to {{nodeName}}, {{conditions}}. Sets border color only.",
"nodeName": "cells",
Expand Down
4 changes: 0 additions & 4 deletions docs/translations/api-docs/data-grid/data-grid/data-grid.json
Original file line number Diff line number Diff line change
Expand Up @@ -1015,10 +1015,6 @@
"nodeName": "the virtualization content",
"conditions": "its height is bigger than the virtualization container"
},
"virtualScrollerRenderZone": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the virtualization render zone"
},
"withBorderColor": {
"description": "Styles applied to {{nodeName}}, {{conditions}}. Sets border color only.",
"nodeName": "cells",
Expand Down
15 changes: 10 additions & 5 deletions packages/x-data-grid-pro/src/components/GridDetailPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,27 +12,32 @@ const DetailPanel = styled('div', {
name: 'MuiDataGrid',
slot: 'DetailPanel',
overridesResolver: (props, styles) => styles.detailPanel,
})<{ ownerState: OwnerState }>(({ theme }) => ({
})<{ ownerState: OwnerState }>({
position: 'relative',
width:
'calc(var(--DataGrid-rowWidth) - var(--DataGrid-hasScrollY) * var(--DataGrid-scrollbarSize))',
backgroundColor: (theme.vars || theme).palette.background.default,
backgroundColor: 'var(--DataGrid-containerBackground)',
overflow: 'auto',
}));
});

interface GridDetailPanelProps
extends Pick<React.HTMLAttributes<HTMLDivElement>, 'className' | 'children'> {
/**
* The row ID that this panel belongs to.
*/
rowId: GridRowId;
/**
* The top position.
*/
top: number;
/**
* The panel height.
*/
height: number | 'auto';
}

function GridDetailPanel(props: GridDetailPanelProps) {
const { rowId, height, className, children } = props;
const { rowId, height, top, className, children } = props;
const apiRef = useGridPrivateApiContext();
const ref = React.useRef<HTMLDivElement | null>(null);
const rootProps = useGridRootProps();
Expand Down Expand Up @@ -65,7 +70,7 @@ function GridDetailPanel(props: GridDetailPanelProps) {
ref={ref}
ownerState={ownerState}
role="presentation"
style={{ height }}
style={{ top, height }}
className={className}
>
{children}
Expand Down
12 changes: 10 additions & 2 deletions packages/x-data-grid-pro/src/components/GridDetailPanels.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import * as React from 'react';
import { unstable_composeClasses as composeClasses } from '@mui/utils';
import { getDataGridUtilityClass, useGridSelector, GridRowId } from '@mui/x-data-grid';
import {
getDataGridUtilityClass,
gridRowsMetaSelector,
useGridSelector,
GridRowId,
} from '@mui/x-data-grid';
import { GridDetailPanelsProps, EMPTY_DETAIL_PANELS } from '@mui/x-data-grid/internals';
import { useGridPrivateApiContext } from '../hooks/utils/useGridPrivateApiContext';
import { useGridRootProps } from '../hooks/utils/useGridRootProps';
Expand Down Expand Up @@ -31,6 +36,7 @@ function GridDetailPanelsImpl({ virtualScroller }: GridDetailPanelsProps) {
const classes = useUtilityClasses();
const { setPanels } = virtualScroller;

const rowsMeta = useGridSelector(apiRef, gridRowsMetaSelector);
const expandedRowIds = useGridSelector(apiRef, gridDetailPanelExpandedRowIdsSelector);
const detailPanelsContent = useGridSelector(
apiRef,
Expand All @@ -55,19 +61,21 @@ function GridDetailPanelsImpl({ virtualScroller }: GridDetailPanelsProps) {

const hasAutoHeight = apiRef.current.detailPanelHasAutoHeight(rowId);
const height = hasAutoHeight ? 'auto' : detailPanelsHeights[rowId];
const top = rowsMeta.positions[rowIndex] + apiRef.current.unstable_getRowHeight(rowId);

return (
<GridDetailPanel
key={`panel-${rowId}`}
rowId={rowId}
top={top}
height={height}
className={classes.detailPanel}
>
{content}
</GridDetailPanel>
);
},
[apiRef, classes.detailPanel, detailPanelsHeights, detailPanelsContent],
[apiRef, classes.detailPanel, rowsMeta, detailPanelsHeights, detailPanelsContent],
);

React.useEffect(() => {
Expand Down
15 changes: 14 additions & 1 deletion packages/x-data-grid-pro/src/components/GridPinnedRows.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { getDataGridUtilityClass, gridClasses, useGridSelector } from '@mui/x-da
import {
GridPinnedRowsProps,
gridPinnedRowsSelector,
gridRenderContextSelector,
useGridPrivateApiContext,
} from '@mui/x-data-grid/internals';

Expand All @@ -19,10 +20,22 @@ export function GridPinnedRows({ position, virtualScroller, ...other }: GridPinn
const classes = useUtilityClasses();
const apiRef = useGridPrivateApiContext();

const renderContext = useGridSelector(apiRef, gridRenderContextSelector);
const pinnedRowsData = useGridSelector(apiRef, gridPinnedRowsSelector);
const rows = pinnedRowsData[position];

const pinnedRows = virtualScroller.getRows({
position,
rows: pinnedRowsData[position],
rows,
renderContext: React.useMemo(
() => ({
firstRowIndex: 0,
lastRowIndex: rows.length,
firstColumnIndex: renderContext.firstColumnIndex,
lastColumnIndex: renderContext.lastColumnIndex,
}),
[rows, renderContext.firstColumnIndex, renderContext.lastColumnIndex],
),
});

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,20 +365,20 @@ describe('<DataGridPro /> - Column pinning', () => {
it('should change the side when called on a pinned column', () => {
render(<TestCase />);

const renderZone = $(`.${gridClasses.virtualScrollerRenderZone}`)!;
const content = $(`.${gridClasses.virtualScrollerContent}`)!;

expect($(renderZone, '[data-field="currencyPair"]')!.className).not.to.include('pinned');
expect($(content, '[data-field="currencyPair"]')!.className).not.to.include('pinned');

act(() => apiRef.current.pinColumn('currencyPair', GridPinnedColumnPosition.LEFT));
expect(
$(renderZone, `.${gridClasses['cell--pinnedLeft']}[data-field="currencyPair"]`),
$(content, `.${gridClasses['cell--pinnedLeft']}[data-field="currencyPair"]`),
).not.to.equal(null);
expect($(renderZone, '[data-field="currencyPair"]')!.className).to.include('pinned');
expect($(content, '[data-field="currencyPair"]')!.className).to.include('pinned');

act(() => apiRef.current.pinColumn('currencyPair', GridPinnedColumnPosition.RIGHT));
expect($$(renderZone, `.${gridClasses['cell--pinnedLeft']}`).length).to.equal(0);
expect($$(content, `.${gridClasses['cell--pinnedLeft']}`).length).to.equal(0);
expect(
$(renderZone, `.${gridClasses['cell--pinnedRight']}[data-field="currencyPair"]`),
$(content, `.${gridClasses['cell--pinnedRight']}[data-field="currencyPair"]`),
).not.to.equal(null);
});

Expand All @@ -398,8 +398,8 @@ describe('<DataGridPro /> - Column pinning', () => {
expect($$(`.${gridClasses['cell--pinnedLeft']}`).length).not.to.equal(0);
act(() => apiRef.current.unpinColumn('currencyPair'));
expect($$(`.${gridClasses['cell--pinnedLeft']}`).length).to.equal(0);
const renderZone = $(`.${gridClasses.virtualScrollerRenderZone}`)!;
expect(renderZone.querySelector('[data-field="currencyPair"]')).not.to.equal(null);
const content = $(`.${gridClasses.virtualScrollerContent}`)!;
expect(content.querySelector('[data-field="currencyPair"]')).not.to.equal(null);
});
});

Expand Down
24 changes: 11 additions & 13 deletions packages/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -447,17 +447,15 @@ describe('<DataGridPro /> - Rows', () => {
);

const virtualScroller = grid('virtualScroller')!;
const renderingZone = grid('virtualScrollerRenderZone')!;
const content = grid('virtualScrollerContent')!.querySelectorAll('[role="row"]');
virtualScroller.scrollTop = 10e6; // scroll to the bottom
act(() => virtualScroller.dispatchEvent(new Event('scroll')));

const lastCell = $$('[role="row"]:last-child [role="gridcell"]')[0];
expect(lastCell).to.have.text('995');
expect(renderingZone.children.length).to.equal(
Math.floor((height - 1) / rowHeight) + rowBuffer,
); // Subtracting 1 is needed because of the column header borders
expect(content.length).to.equal(Math.floor((height - 1) / rowHeight) + rowBuffer); // Subtracting 1 is needed because of the column header borders
const scrollbarSize = apiRef.current.state.dimensions.scrollbarSize;
const distanceToFirstRow = (nbRows - renderingZone.children.length) * rowHeight;
const distanceToFirstRow = (nbRows - content.length) * rowHeight;
expect(gridOffsetTop()).to.equal(distanceToFirstRow);
expect(virtualScroller.scrollHeight - scrollbarSize).to.equal(nbRows * rowHeight);
});
Expand Down Expand Up @@ -503,12 +501,12 @@ describe('<DataGridPro /> - Rows', () => {
<TestCaseVirtualization rowHeight={rowHeight} rowBuffer={0} rowThreshold={rowThreshold} />,
);
const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!;
const renderingZone = document.querySelector('.MuiDataGrid-virtualScrollerRenderZone')!;
let firstRow = renderingZone.firstChild;
const content = document.querySelector('.MuiDataGrid-virtualScrollerContent')!;
let firstRow = content.firstChild;
expect(firstRow).to.have.attr('data-rowindex', '0');
virtualScroller.scrollTop = rowThreshold * rowHeight;
act(() => virtualScroller.dispatchEvent(new Event('scroll')));
firstRow = renderingZone.firstChild;
firstRow = content.firstChild;
expect(firstRow).to.have.attr('data-rowindex', '3');
});

Expand All @@ -519,13 +517,13 @@ describe('<DataGridPro /> - Rows', () => {
<TestCaseVirtualization nbRows={1} columnBuffer={0} columnThreshold={columnThreshold} />,
);
const virtualScroller = grid('virtualScroller')!;
const renderingZone = grid('virtualScrollerRenderZone')!;
const firstRow = $(renderingZone, '[role="row"]:first-child')!;
const content = grid('virtualScrollerContent')!;
const firstRow = $(content, '[role="row"]:first-child')!;
let firstColumn = $$(firstRow, '[role="gridcell"]')[0];
expect(firstColumn).to.have.attr('data-colindex', '0');
virtualScroller.scrollLeft = columnThreshold * columnWidth;
act(() => virtualScroller.dispatchEvent(new Event('scroll')));
firstColumn = $(renderingZone, '[role="row"] > [role="gridcell"]')!;
firstColumn = $(content, '[role="row"] > [role="gridcell"]')!;
expect(firstColumn).to.have.attr('data-colindex', '3');
});

Expand Down Expand Up @@ -570,7 +568,7 @@ describe('<DataGridPro /> - Rows', () => {
expect(lastCell).to.have.text('99');
expect(virtualScroller.scrollTop).to.equal(0);
expect(virtualScroller.scrollHeight).to.equal(virtualScroller.clientHeight);
expect(grid('virtualScrollerRenderZone')!.children).to.have.length(4);
expect(grid('virtualScrollerContent')!.children).to.have.length(4);
});

it('should paginate small dataset in auto page-size #1492', () => {
Expand All @@ -586,7 +584,7 @@ describe('<DataGridPro /> - Rows', () => {

expect(virtualScroller.scrollTop).to.equal(0);
expect(virtualScroller.scrollHeight).to.equal(virtualScroller.clientHeight);
expect(grid('virtualScrollerRenderZone')!.children).to.have.length(7);
expect(grid('virtualScrollerContent')!.children).to.have.length(7);
});
});

Expand Down
6 changes: 4 additions & 2 deletions packages/x-data-grid-pro/src/utils/domUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,17 @@ export function findGridCells(api: GridPrivateApiPro, field: string) {
const container = api.virtualScrollerRef!.current!;
return Array.from(
container.querySelectorAll(
`:scope > div > div > div > [data-field="${field}"][role="gridcell"]`,
`:scope > div > div > [data-field="${field}"][role="gridcell"],
:scope > div > div > div > [data-field="${field}"][role="gridcell"]`,
),
);
}

function queryRows(api: GridPrivateApiPro) {
return api.virtualScrollerRef.current!.querySelectorAll(
// Use > to ignore rows from nested data grids (e.g. in detail panel)
`:scope > div > div > .${gridClasses.row}`,
`:scope > div > .${gridClasses.row},
:scope > div > div > .${gridClasses.row}`,
);
}

Expand Down
Loading
Loading