From 3e0fdae7db36a7bf970d2a36fecf9f3e5149c45b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 25 Nov 2024 19:10:04 +0000 Subject: [PATCH] [DataGrid] Support column virtualization with dynamic row height (@cherniavskii) (#15567) Co-authored-by: Andrew Cherniavskii --- .../VirtualizeColumnsWithAutoRowHeight.js | 49 +++++++++++++++ .../VirtualizeColumnsWithAutoRowHeight.tsx | 59 +++++++++++++++++++ ...ualizeColumnsWithAutoRowHeight.tsx.preview | 5 ++ docs/data/data-grid/row-height/row-height.md | 12 +++- .../virtualization/virtualization.md | 7 ++- .../x/api/data-grid/data-grid-premium.json | 3 +- docs/pages/x/api/data-grid/data-grid-pro.json | 3 +- docs/pages/x/api/data-grid/data-grid.json | 3 +- .../data-grid-premium/data-grid-premium.json | 3 + .../data-grid-pro/data-grid-pro.json | 3 + .../data-grid/data-grid/data-grid.json | 3 + .../src/DataGridPremium/DataGridPremium.tsx | 8 +++ .../src/DataGridPro/DataGridPro.tsx | 8 +++ .../x-data-grid/src/DataGrid/DataGrid.tsx | 8 +++ .../src/components/cell/GridCell.tsx | 1 + .../constants/dataGridPropsDefaultValues.ts | 1 + .../virtualization/useGridVirtualScroller.tsx | 13 ++-- .../src/models/props/DataGridProps.ts | 8 +++ 18 files changed, 188 insertions(+), 9 deletions(-) create mode 100644 docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.js create mode 100644 docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx create mode 100644 docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx.preview diff --git a/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.js b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.js new file mode 100644 index 0000000000000..9d01cdcaf506a --- /dev/null +++ b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.js @@ -0,0 +1,49 @@ +import * as React from 'react'; +import { DataGrid } from '@mui/x-data-grid'; + +function useData(rowLength, columnLength) { + const [data, setData] = React.useState({ columns: [], rows: [] }); + + React.useEffect(() => { + const rows = []; + + for (let i = 0; i < rowLength; i += 1) { + const row = { + id: i, + }; + + for (let j = 1; j <= columnLength; j += 1) { + row[`price${j}M`] = `${i.toString()}, ${j} `; + } + + rows.push(row); + } + + const columns = []; + + for (let j = 1; j <= columnLength; j += 1) { + columns.push({ field: `price${j}M`, headerName: `${j}M`, width: 55 }); + } + + setData({ + rows, + columns, + }); + }, [rowLength, columnLength]); + + return data; +} + +export default function VirtualizeColumnsWithAutoRowHeight() { + const data = useData(100, 100); + + return ( +
+ 'auto'} + virtualizeColumnsWithAutoRowHeight + /> +
+ ); +} diff --git a/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx new file mode 100644 index 0000000000000..1a76b22ee5c97 --- /dev/null +++ b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx @@ -0,0 +1,59 @@ +import * as React from 'react'; +import { DataGrid, GridColDef, GridRowId } from '@mui/x-data-grid'; + +export interface DataRowModel { + id: GridRowId; + [price: string]: number | string; +} + +export interface GridData { + columns: GridColDef[]; + rows: DataRowModel[]; +} + +function useData(rowLength: number, columnLength: number) { + const [data, setData] = React.useState({ columns: [], rows: [] }); + + React.useEffect(() => { + const rows: DataRowModel[] = []; + + for (let i = 0; i < rowLength; i += 1) { + const row: DataRowModel = { + id: i, + }; + + for (let j = 1; j <= columnLength; j += 1) { + row[`price${j}M`] = `${i.toString()}, ${j} `; + } + + rows.push(row); + } + + const columns: GridColDef[] = []; + + for (let j = 1; j <= columnLength; j += 1) { + columns.push({ field: `price${j}M`, headerName: `${j}M`, width: 55 }); + } + + setData({ + rows, + columns, + }); + }, [rowLength, columnLength]); + + return data; +} + +export default function VirtualizeColumnsWithAutoRowHeight() { + const data = useData(100, 100); + + return ( +
+ 'auto'} + virtualizeColumnsWithAutoRowHeight + /> +
+ ); +} diff --git a/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx.preview b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx.preview new file mode 100644 index 0000000000000..1f3efbd9e944e --- /dev/null +++ b/docs/data/data-grid/row-height/VirtualizeColumnsWithAutoRowHeight.tsx.preview @@ -0,0 +1,5 @@ + 'auto'} + virtualizeColumnsWithAutoRowHeight +/> \ No newline at end of file diff --git a/docs/data/data-grid/row-height/row-height.md b/docs/data/data-grid/row-height/row-height.md index 0d5790977d894..fa5a34a72a4c5 100644 --- a/docs/data/data-grid/row-height/row-height.md +++ b/docs/data/data-grid/row-height/row-height.md @@ -54,7 +54,6 @@ This side effect happens because a row height estimation is used while a row is You can configure the estimated value used by passing a function to the `getEstimatedRowHeight` prop. If not provided, the default row height of `52px` is used as estimation. It's recommended to pass this prop if the content deviates too much from the default value. -Note that, due to the implementation adopted, the virtualization of the columns is also disabled to force all columns to be rendered at the same time. ```tsx 'auto'} getEstimatedRowHeight={() => 200} /> @@ -78,6 +77,17 @@ Add padding to the cells to increase the space between the content and the cell ::: +### Column virtualization + +By default, the virtualization of the columns is disabled to force all columns to be rendered at the same time and calculate the row height correctly. +However, this can lead to poor performance when rendering a lot of columns. + +If you need column virtualization, you can set the `virtualizeColumnsWithAutoRowHeight` prop to `true`. +With this approach, the Data Grid measures the row height based on the visible columns. +However, the row height might change during horizontal scrolling. + +{{"demo": "VirtualizeColumnsWithAutoRowHeight.js", "bg": "inline" }} + ## Row density Give your users the option to change the default row density to match their preferences—compact, standard, or comfortable. diff --git a/docs/data/data-grid/virtualization/virtualization.md b/docs/data/data-grid/virtualization/virtualization.md index 46efb8f1de521..3dc949818c958 100644 --- a/docs/data/data-grid/virtualization/virtualization.md +++ b/docs/data/data-grid/virtualization/virtualization.md @@ -31,7 +31,12 @@ By default, columns coming under 150 pixels region are rendered outside of the v {{"demo": "ColumnVirtualizationGrid.js", "bg": "inline"}} -You can disable column virtualization by calling `apiRef.current.unstable_setColumnVirtualization(false)`, or by setting the column buffer to the number of total columns. +You can disable column virtualization by calling `apiRef.current.unstable_setColumnVirtualization(false)`, or by setting the [`columnBufferPx`](/x/api/data-grid/data-grid/#data-grid-prop-columnBufferPx) to a high value. + +:::info +Column virtualization is disabled when dynamic row height is enabled. +See [dynamic row height and column virtualization](/x/react-data-grid/row-height/#column-virtualization) to learn more. +::: ## Disable virtualization diff --git a/docs/pages/x/api/data-grid/data-grid-premium.json b/docs/pages/x/api/data-grid/data-grid-premium.json index 7497389556f5d..62dbe51227fbf 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -656,7 +656,8 @@ } }, "unstable_listView": { "type": { "name": "bool" } }, - "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" } + "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" }, + "virtualizeColumnsWithAutoRowHeight": { "type": { "name": "bool" }, "default": "false" } }, "name": "DataGridPremium", "imports": [ diff --git a/docs/pages/x/api/data-grid/data-grid-pro.json b/docs/pages/x/api/data-grid/data-grid-pro.json index 0c9713826173d..e9568e460d4bd 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -587,7 +587,8 @@ } }, "unstable_listView": { "type": { "name": "bool" } }, - "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" } + "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" }, + "virtualizeColumnsWithAutoRowHeight": { "type": { "name": "bool" }, "default": "false" } }, "name": "DataGridPro", "imports": [ diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json index 7effab95eb675..5a0cb7771daf3 100644 --- a/docs/pages/x/api/data-grid/data-grid.json +++ b/docs/pages/x/api/data-grid/data-grid.json @@ -485,7 +485,8 @@ }, "additionalInfo": { "sx": true } }, - "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" } + "unstable_rowSpanning": { "type": { "name": "bool" }, "default": "false" }, + "virtualizeColumnsWithAutoRowHeight": { "type": { "name": "bool" }, "default": "false" } }, "name": "DataGrid", "imports": [ diff --git a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json index 87146ea00c4fb..15904b669cfb3 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json @@ -669,6 +669,9 @@ }, "unstable_rowSpanning": { "description": "If true, the Data Grid will auto span the cells over the rows having the same value." + }, + "virtualizeColumnsWithAutoRowHeight": { + "description": "If true, the Data Grid enables column virtualization when getRowHeight is set to () => 'auto'. By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. For datasets with a large number of columns, this can cause performance issues. The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally." } }, "classDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json index 1beda7ae96a8e..bc84b36fabbf0 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json @@ -607,6 +607,9 @@ }, "unstable_rowSpanning": { "description": "If true, the Data Grid will auto span the cells over the rows having the same value." + }, + "virtualizeColumnsWithAutoRowHeight": { + "description": "If true, the Data Grid enables column virtualization when getRowHeight is set to () => 'auto'. By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. For datasets with a large number of columns, this can cause performance issues. The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally." } }, "classDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid/data-grid.json index ecd06e565a60a..70cfd2fa8a474 100644 --- a/docs/translations/api-docs/data-grid/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid/data-grid.json @@ -487,6 +487,9 @@ }, "unstable_rowSpanning": { "description": "If true, the Data Grid will auto span the cells over the rows having the same value." + }, + "virtualizeColumnsWithAutoRowHeight": { + "description": "If true, the Data Grid enables column virtualization when getRowHeight is set to () => 'auto'. By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. For datasets with a large number of columns, this can cause performance issues. The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally." } }, "classDescriptions": { diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index 0f05ca94e442d..0566094f468a3 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -1127,6 +1127,14 @@ DataGridPremiumRaw.propTypes = { * @default false */ unstable_rowSpanning: PropTypes.bool, + /** + * If `true`, the Data Grid enables column virtualization when `getRowHeight` is set to `() => 'auto'`. + * By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. + * For datasets with a large number of columns, this can cause performance issues. + * The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally. + * @default false + */ + virtualizeColumnsWithAutoRowHeight: PropTypes.bool, } as any; interface DataGridPremiumComponent { diff --git a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index cf10a788ce68c..206d7489f3ddc 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -1026,4 +1026,12 @@ DataGridProRaw.propTypes = { * @default false */ unstable_rowSpanning: PropTypes.bool, + /** + * If `true`, the Data Grid enables column virtualization when `getRowHeight` is set to `() => 'auto'`. + * By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. + * For datasets with a large number of columns, this can cause performance issues. + * The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally. + * @default false + */ + virtualizeColumnsWithAutoRowHeight: PropTypes.bool, } as any; diff --git a/packages/x-data-grid/src/DataGrid/DataGrid.tsx b/packages/x-data-grid/src/DataGrid/DataGrid.tsx index ccda733ed0deb..2c33f6ec8e70c 100644 --- a/packages/x-data-grid/src/DataGrid/DataGrid.tsx +++ b/packages/x-data-grid/src/DataGrid/DataGrid.tsx @@ -821,4 +821,12 @@ DataGridRaw.propTypes = { * @default false */ unstable_rowSpanning: PropTypes.bool, + /** + * If `true`, the Data Grid enables column virtualization when `getRowHeight` is set to `() => 'auto'`. + * By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. + * For datasets with a large number of columns, this can cause performance issues. + * The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally. + * @default false + */ + virtualizeColumnsWithAutoRowHeight: PropTypes.bool, } as any; diff --git a/packages/x-data-grid/src/components/cell/GridCell.tsx b/packages/x-data-grid/src/components/cell/GridCell.tsx index 3760b113a261f..70614fecce353 100644 --- a/packages/x-data-grid/src/components/cell/GridCell.tsx +++ b/packages/x-data-grid/src/components/cell/GridCell.tsx @@ -343,6 +343,7 @@ const GridCell = React.forwardRef(function GridCe padding: 0, opacity: 0, width: 0, + height: 0, border: 0, }; } diff --git a/packages/x-data-grid/src/constants/dataGridPropsDefaultValues.ts b/packages/x-data-grid/src/constants/dataGridPropsDefaultValues.ts index 689aa164e7a09..4c69790cebb67 100644 --- a/packages/x-data-grid/src/constants/dataGridPropsDefaultValues.ts +++ b/packages/x-data-grid/src/constants/dataGridPropsDefaultValues.ts @@ -59,4 +59,5 @@ export const DATA_GRID_PROPS_DEFAULT_VALUES: DataGridPropsWithDefaultValues = { sortingOrder: ['asc' as const, 'desc' as const, null], throttleRowsMs: 0, unstable_rowSpanning: false, + virtualizeColumnsWithAutoRowHeight: false, }; diff --git a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx index bc635380def6e..679b05f8bbbbb 100644 --- a/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx +++ b/packages/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx @@ -33,6 +33,7 @@ import type { GridRowEntry, GridRowId, } from '../../../models'; +import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { selectedIdsLookupSelector } from '../rowSelection/gridRowSelectionSelector'; import { gridRowsMetaSelector } from '../rows/gridRowsMetaSelector'; import { getFirstNonSpannedColumnToRender } from '../columns/gridColumnsUtils'; @@ -685,6 +686,7 @@ type RenderContextInputs = { visibleColumns: ReturnType; hiddenCellsOriginMap: ReturnType; listView: boolean; + virtualizeColumnsWithAutoRowHeight: DataGridProcessedProps['virtualizeColumnsWithAutoRowHeight']; }; function inputsSelector( @@ -722,6 +724,7 @@ function inputsSelector( visibleColumns, hiddenCellsOriginMap, listView: rootProps.unstable_listView ?? false, + virtualizeColumnsWithAutoRowHeight: rootProps.virtualizeColumnsWithAutoRowHeight, }; } @@ -785,12 +788,14 @@ function computeRenderContext( lastSize: inputs.lastRowHeight, }); - for (let i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) { - const row = inputs.rows[i]; - hasRowWithAutoHeight = inputs.apiRef.current.rowHasAutoHeight(row.id); + if (!inputs.virtualizeColumnsWithAutoRowHeight) { + for (let i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) { + const row = inputs.rows[i]; + hasRowWithAutoHeight = inputs.apiRef.current.rowHasAutoHeight(row.id); + } } - if (!hasRowWithAutoHeight) { + if (!hasRowWithAutoHeight || inputs.virtualizeColumnsWithAutoRowHeight) { firstColumnIndex = binarySearch(realLeft, inputs.columnPositions, { atStart: true, lastPosition: inputs.columnsTotalWidth, diff --git a/packages/x-data-grid/src/models/props/DataGridProps.ts b/packages/x-data-grid/src/models/props/DataGridProps.ts index b35fa21f220ec..abf591bedcd50 100644 --- a/packages/x-data-grid/src/models/props/DataGridProps.ts +++ b/packages/x-data-grid/src/models/props/DataGridProps.ts @@ -396,6 +396,14 @@ export interface DataGridPropsWithDefaultValues 'auto'`. + * By default, column virtualization is disabled when dynamic row height is enabled to measure the row height correctly. + * For datasets with a large number of columns, this can cause performance issues. + * The downside of enabling this prop is that the row height will be estimated based the cells that are currently rendered, which can cause row height change when scrolling horizontally. + * @default false + */ + virtualizeColumnsWithAutoRowHeight: boolean; } /**