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

[data grid] Improve vertical scrolling performance #11924

Merged
merged 7 commits into from
Feb 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 6 additions & 0 deletions docs/pages/x/api/data-grid/data-grid-premium.json
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,12 @@
"description": "Styles applied to the empty cell element.",
"isGlobal": false
},
{
"key": "cellOffsetLeft",
"className": "MuiDataGridPremium-cellOffsetLeft",
"description": "",
"isGlobal": false
},
{
"key": "cellSkeleton",
"className": "MuiDataGridPremium-cellSkeleton",
Expand Down
6 changes: 6 additions & 0 deletions docs/pages/x/api/data-grid/data-grid-pro.json
Original file line number Diff line number Diff line change
Expand Up @@ -721,6 +721,12 @@
"description": "Styles applied to the empty cell element.",
"isGlobal": false
},
{
"key": "cellOffsetLeft",
"className": "MuiDataGridPro-cellOffsetLeft",
"description": "",
"isGlobal": false
},
{
"key": "cellSkeleton",
"className": "MuiDataGridPro-cellSkeleton",
Expand Down
6 changes: 6 additions & 0 deletions docs/pages/x/api/data-grid/data-grid.json
Original file line number Diff line number Diff line change
Expand Up @@ -606,6 +606,12 @@
"description": "Styles applied to the empty cell element.",
"isGlobal": false
},
{
"key": "cellOffsetLeft",
"className": "MuiDataGrid-cellOffsetLeft",
"description": "",
"isGlobal": false
},
{
"key": "cellSkeleton",
"className": "MuiDataGrid-cellSkeleton",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -763,6 +763,7 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the empty cell element"
},
"cellOffsetLeft": { "description": "" },
"cellSkeleton": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the skeleton cell element"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,7 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the empty cell element"
},
"cellOffsetLeft": { "description": "" },
"cellSkeleton": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the skeleton cell element"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,7 @@
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the empty cell element"
},
"cellOffsetLeft": { "description": "" },
"cellSkeleton": {
"description": "Styles applied to {{nodeName}}.",
"nodeName": "the skeleton cell element"
Expand Down
44 changes: 18 additions & 26 deletions packages/x-data-grid-pro/src/tests/rows.DataGridPro.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { spy } from 'sinon';
import { expect } from 'chai';
import {
$,
$$,
grid,
getCell,
getRow,
Expand Down Expand Up @@ -450,7 +451,7 @@ describe('<DataGridPro /> - Rows', () => {
virtualScroller.scrollTop = 10e6; // scroll to the bottom
act(() => virtualScroller.dispatchEvent(new Event('scroll')));

const lastCell = $('[role="row"]:last-child [role="gridcell"]:first-child')!;
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,
Expand Down Expand Up @@ -486,11 +487,13 @@ describe('<DataGridPro /> - Rows', () => {
/>,
);
const firstRow = getRow(0);
expect(firstRow.children).to.have.length(Math.floor(width / columnWidth) + columnBuffer);
expect($$(firstRow, '[role="gridcell"]')).to.have.length(
Math.floor(width / columnWidth) + columnBuffer,
);
const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!;
virtualScroller.scrollLeft = 301;
act(() => virtualScroller.dispatchEvent(new Event('scroll')));
expect(firstRow.children).to.have.length(
expect($$(firstRow, '[role="gridcell"]')).to.have.length(
columnBuffer + 1 + Math.floor(width / columnWidth) + columnBuffer,
);
});
Expand All @@ -517,15 +520,14 @@ describe('<DataGridPro /> - Rows', () => {
render(
<TestCaseVirtualization nbRows={1} columnBuffer={0} columnThreshold={columnThreshold} />,
);
const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!;
const renderingZone = document.querySelector('.MuiDataGrid-virtualScrollerRenderZone')!;
let firstRow = renderingZone.querySelector('[role="row"]:first-child')!;
let firstColumn = firstRow.firstChild!;
const virtualScroller = grid('virtualScroller')!;
const renderingZone = grid('virtualScrollerRenderZone')!;
const firstRow = $(renderingZone, '[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')));
firstRow = renderingZone.querySelector('[role="row"]:first-child')!;
firstColumn = firstRow.firstChild!;
firstColumn = $(renderingZone, '[role="row"] > [role="gridcell"]')!;
expect(firstColumn).to.have.attr('data-colindex', '3');
});

Expand All @@ -546,16 +548,14 @@ describe('<DataGridPro /> - Rows', () => {
act(() => virtualScroller.dispatchEvent(new Event('scroll')));

const dimensions = apiRef.current.state.dimensions;
const lastCell = document.querySelector(
'[role="row"]:last-child [role="gridcell"]:first-child',
)!;
const lastCell = $$('[role="row"]:last-child [role="gridcell"]')[0];
expect(lastCell).to.have.text('31');
expect(virtualScroller.scrollHeight).to.equal(
dimensions.headerHeight + nbRows * rowHeight + dimensions.scrollbarSize,
);
});

it('should not virtualized the last page if smaller than viewport', () => {
it('should not virtualize the last page if smaller than viewport', () => {
render(
<TestCaseVirtualization
pagination
Expand All @@ -564,19 +564,15 @@ describe('<DataGridPro /> - Rows', () => {
height={500}
/>,
);
const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!;
const virtualScroller = grid('virtualScroller')!;
virtualScroller.scrollTop = 10e6; // scroll to the bottom
virtualScroller.dispatchEvent(new Event('scroll'));

const lastCell = document.querySelector(
'[role="row"]:last-child [role="gridcell"]:first-child',
)!;
const lastCell = $$('[role="row"]:last-child [role="gridcell"]')[0];
expect(lastCell).to.have.text('99');
expect(virtualScroller.scrollTop).to.equal(0);
expect(virtualScroller.scrollHeight).to.equal(virtualScroller.clientHeight);
expect(
document.querySelector('.MuiDataGrid-virtualScrollerRenderZone')!.children,
).to.have.length(4);
expect(grid('virtualScrollerRenderZone')!.children).to.have.length(4);
});

it('should paginate small dataset in auto page-size #1492', () => {
Expand All @@ -585,18 +581,14 @@ describe('<DataGridPro /> - Rows', () => {
);
const virtualScroller = document.querySelector('.MuiDataGrid-virtualScroller')!;

const lastCell = document.querySelector(
'[role="row"]:last-child [role="gridcell"]:first-child',
)!;
const lastCell = $$('[role="row"]:last-child [role="gridcell"]')[0];
expect(lastCell).to.have.text('6');
const rows = document.querySelectorAll('.MuiDataGrid-row[role="row"]')!;
expect(rows.length).to.equal(7);

expect(virtualScroller.scrollTop).to.equal(0);
expect(virtualScroller.scrollHeight).to.equal(virtualScroller.clientHeight);
expect(
document.querySelector('.MuiDataGrid-virtualScrollerRenderZone')!.children,
).to.have.length(7);
expect(grid('virtualScrollerRenderZone')!.children).to.have.length(7);
});
});

Expand Down
3 changes: 1 addition & 2 deletions packages/x-data-grid/src/components/GridRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,6 @@ const useUtilityClasses = (ownerState: OwnerState) => {
isLastVisible && 'row--lastVisible',
rowHeight === 'auto' && 'row--dynamicHeight',
],
pinnedLeft: ['pinnedLeft'],
pinnedRight: ['pinnedRight'],
};

return composeClasses(slots, getDataGridUtilityClass, classes);
Expand Down Expand Up @@ -517,6 +515,7 @@ const GridRow = React.forwardRef<HTMLDivElement, GridRowProps>(function GridRow(
{...other}
>
{leftCells}
<div className={gridClasses.cellOffsetLeft} role="presentation" />
{cells}
{emptyCellWidth > 0 && <EmptyCell width={emptyCellWidth} />}
{rightCells.length > 0 && <div role="presentation" style={{ flex: '1' }} />}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ const Style = styled('div')({
position: 'sticky',
right: 0,
},
[`&:not(.${classes.header}):not(.${classes.pinnedRight})`]: {
transform: 'translate3d(var(--DataGrid-offsetLeft), 0, 0)',
},
});

function GridScrollbarFillerCell({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -572,8 +572,10 @@ export const GridRootStyles = styled('div', {
},
},
},
[`& .${c.cell}:not(.${c['cell--pinnedLeft']}):not(.${c['cell--pinnedRight']})`]: {
transform: 'translate3d(var(--DataGrid-offsetLeft), 0, 0)',
[`& .${c.cellOffsetLeft}`]: {
flex: '0 0 auto',
display: 'inline-block',
width: 'var(--DataGrid-offsetLeft)',
},
[`& .${c.columnHeaderDraggableContainer}`]: {
display: 'flex',
Expand Down
6 changes: 6 additions & 0 deletions packages/x-data-grid/src/constants/gridClasses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,11 @@ export interface GridClasses {
* Styles applied to the skeleton cell element.
*/
cellSkeleton: string;
/**
* @ignore - do not document.
* Styles applied to the left offset cell element.
*/
cellOffsetLeft: string;
/**
* Styles applied to the selection checkbox element.
*/
Expand Down Expand Up @@ -634,6 +639,7 @@ export const gridClasses = generateUtilityClasses<GridClassKey>('MuiDataGrid', [
'cellCheckbox',
'cellEmpty',
'cellSkeleton',
'cellOffsetLeft',
'checkboxInput',
'columnHeader--alignCenter',
'columnHeader--alignLeft',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ export const useGridVirtualScroller = () => {
const gridRootRef = apiRef.current.rootElementRef;
const mainRef = apiRef.current.mainElementRef;
const scrollerRef = apiRef.current.virtualScrollerRef;
const renderZoneRef = React.useRef<HTMLDivElement>(null);
const scrollbarVerticalRef = React.useRef<HTMLDivElement>(null);
const scrollbarHorizontalRef = React.useRef<HTMLDivElement>(null);
const contentHeight = dimensions.contentSize.height;
Expand Down Expand Up @@ -343,6 +342,16 @@ export const useGridVirtualScroller = () => {

for (let i = 0; i < renderedRows.length; i += 1) {
const { id, model } = renderedRows[i];

const rowIndexInPage = (currentPage?.range?.firstRowIndex || 0) + firstRowToRender + i;
let index = rowIndexOffset + rowIndexInPage;
if (isRowWithFocusedCellNotInRange && cellFocus?.id === id) {
index = indexOfRowWithFocusedCell;
isRowWithFocusedCellRendered = true;
} else if (isRowWithFocusedCellRendered) {
index -= 1;
}

const isRowNotVisible = isRowWithFocusedCellNotInRange && cellFocus!.id === id;

const baseRowHeight = !apiRef.current.rowHasAutoHeight(id)
Expand All @@ -358,7 +367,7 @@ export const useGridVirtualScroller = () => {

let isFirstVisible = false;
if (params.position === undefined) {
isFirstVisible = i === 0;
isFirstVisible = rowIndexInPage === 0;
}

let isLastVisible = false;
Expand Down Expand Up @@ -394,14 +403,6 @@ export const useGridVirtualScroller = () => {
tabbableCell = cellParams.cellMode === 'view' ? cellTabIndex.field : null;
}

let index = rowIndexOffset + (currentPage?.range?.firstRowIndex || 0) + firstRowToRender + i;
if (isRowWithFocusedCellNotInRange && cellFocus?.id === id) {
index = indexOfRowWithFocusedCell;
isRowWithFocusedCellRendered = true;
} else if (isRowWithFocusedCellRendered) {
index -= 1;
}

rows.push(
<rootProps.slots.row
key={id}
Expand Down Expand Up @@ -532,7 +533,7 @@ export const useGridVirtualScroller = () => {
style: contentSize,
role: 'presentation',
}),
getRenderZoneProps: () => ({ ref: renderZoneRef, role: 'rowgroup' }),
getRenderZoneProps: () => ({ role: 'rowgroup' }),
getScrollbarVerticalProps: () => ({ ref: scrollbarVerticalRef, role: 'presentation' }),
getScrollbarHorizontalProps: () => ({ ref: scrollbarHorizontalRef, role: 'presentation' }),
};
Expand Down
Loading