diff --git a/docs/data/data-grid/column-dimensions/ColumnAutosizing.js b/docs/data/data-grid/column-dimensions/ColumnAutosizing.js
new file mode 100644
index 0000000000000..1743b754eccc1
--- /dev/null
+++ b/docs/data/data-grid/column-dimensions/ColumnAutosizing.js
@@ -0,0 +1,129 @@
+import * as React from 'react';
+import Button from '@mui/material/Button';
+import Checkbox from '@mui/material/Checkbox';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import Rating from '@mui/material/Rating';
+import Stack from '@mui/material/Stack';
+import TextField from '@mui/material/TextField';
+import { useGridApiRef } from '@mui/x-data-grid';
+import { DataGridPro, DEFAULT_GRID_AUTOSIZE_OPTIONS } from '@mui/x-data-grid-pro';
+import { randomRating, randomTraderName } from '@mui/x-data-grid-generator';
+
+function renderRating(params) {
+ return ;
+}
+
+function useData(length) {
+ return React.useMemo(() => {
+ const names = [
+ 'Nike',
+ 'Adidas',
+ 'Puma',
+ 'Reebok',
+ 'Fila',
+ 'Lululemon Athletica Clothing',
+ 'Varley',
+ ];
+
+ const rows = Array.from({ length }).map((_, id) => ({
+ id,
+ brand: names[id % names.length],
+ rep: randomTraderName(),
+ rating: randomRating(),
+ }));
+
+ const columns = [
+ { field: 'id', headerName: 'Brand ID' },
+ { field: 'brand', headerName: 'Brand name' },
+ { field: 'rep', headerName: 'Representative' },
+ { field: 'rating', headerName: 'Rating', renderCell: renderRating },
+ ];
+
+ return { rows, columns };
+ }, [length]);
+}
+
+export default function ColumnAutosizing() {
+ const apiRef = useGridApiRef();
+ const data = useData(100);
+
+ const [includeHeaders, setIncludeHeaders] = React.useState(
+ DEFAULT_GRID_AUTOSIZE_OPTIONS.includeHeaders,
+ );
+ const [includeOutliers, setExcludeOutliers] = React.useState(
+ DEFAULT_GRID_AUTOSIZE_OPTIONS.includeOutliers,
+ );
+ const [outliersFactor, setOutliersFactor] = React.useState(
+ String(DEFAULT_GRID_AUTOSIZE_OPTIONS.outliersFactor),
+ );
+ const [expand, setExpand] = React.useState(DEFAULT_GRID_AUTOSIZE_OPTIONS.expand);
+
+ const autosizeOptions = {
+ includeHeaders,
+ includeOutliers,
+ outliersFactor: Number.isNaN(parseFloat(outliersFactor))
+ ? 1
+ : parseFloat(outliersFactor),
+ expand,
+ };
+
+ return (
+
+
+
+ setIncludeHeaders(ev.target.checked)}
+ />
+ }
+ label="Include headers"
+ />
+ setExcludeOutliers(event.target.checked)}
+ />
+ }
+ label="Include outliers"
+ />
+ setOutliersFactor(ev.target.value)}
+ sx={{ width: '12ch' }}
+ />
+ setExpand(ev.target.checked)}
+ />
+ }
+ label="Expand"
+ />
+
+
+
+
+
+ );
+}
diff --git a/docs/data/data-grid/column-dimensions/ColumnAutosizing.tsx b/docs/data/data-grid/column-dimensions/ColumnAutosizing.tsx
new file mode 100644
index 0000000000000..066846d3406c2
--- /dev/null
+++ b/docs/data/data-grid/column-dimensions/ColumnAutosizing.tsx
@@ -0,0 +1,132 @@
+import * as React from 'react';
+import Button from '@mui/material/Button';
+import Checkbox from '@mui/material/Checkbox';
+import FormControlLabel from '@mui/material/FormControlLabel';
+import Rating from '@mui/material/Rating';
+import Stack from '@mui/material/Stack';
+import TextField from '@mui/material/TextField';
+import { useGridApiRef } from '@mui/x-data-grid';
+import {
+ DataGridPro,
+ GridApiPro,
+ DEFAULT_GRID_AUTOSIZE_OPTIONS,
+} from '@mui/x-data-grid-pro';
+import { randomRating, randomTraderName } from '@mui/x-data-grid-generator';
+
+function renderRating(params: any) {
+ return ;
+}
+
+function useData(length: number) {
+ return React.useMemo(() => {
+ const names = [
+ 'Nike',
+ 'Adidas',
+ 'Puma',
+ 'Reebok',
+ 'Fila',
+ 'Lululemon Athletica Clothing',
+ 'Varley',
+ ];
+ const rows = Array.from({ length }).map((_, id) => ({
+ id,
+ brand: names[id % names.length],
+ rep: randomTraderName(),
+ rating: randomRating(),
+ }));
+
+ const columns = [
+ { field: 'id', headerName: 'Brand ID' },
+ { field: 'brand', headerName: 'Brand name' },
+ { field: 'rep', headerName: 'Representative' },
+ { field: 'rating', headerName: 'Rating', renderCell: renderRating },
+ ];
+
+ return { rows, columns };
+ }, [length]);
+}
+
+export default function ColumnAutosizing() {
+ const apiRef = useGridApiRef();
+ const data = useData(100);
+
+ const [includeHeaders, setIncludeHeaders] = React.useState(
+ DEFAULT_GRID_AUTOSIZE_OPTIONS.includeHeaders,
+ );
+ const [includeOutliers, setExcludeOutliers] = React.useState(
+ DEFAULT_GRID_AUTOSIZE_OPTIONS.includeOutliers,
+ );
+ const [outliersFactor, setOutliersFactor] = React.useState(
+ String(DEFAULT_GRID_AUTOSIZE_OPTIONS.outliersFactor),
+ );
+ const [expand, setExpand] = React.useState(DEFAULT_GRID_AUTOSIZE_OPTIONS.expand);
+
+ const autosizeOptions = {
+ includeHeaders,
+ includeOutliers,
+ outliersFactor: Number.isNaN(parseFloat(outliersFactor))
+ ? 1
+ : parseFloat(outliersFactor),
+ expand,
+ };
+
+ return (
+
+
+
+ setIncludeHeaders(ev.target.checked)}
+ />
+ }
+ label="Include headers"
+ />
+ setExcludeOutliers(event.target.checked)}
+ />
+ }
+ label="Include outliers"
+ />
+ setOutliersFactor(ev.target.value)}
+ sx={{ width: '12ch' }}
+ />
+ setExpand(ev.target.checked)}
+ />
+ }
+ label="Expand"
+ />
+
+
+
+
+
+ );
+}
diff --git a/docs/data/data-grid/column-dimensions/column-dimensions.md b/docs/data/data-grid/column-dimensions/column-dimensions.md
index bded63f3da53f..3a60bf30cbe9d 100644
--- a/docs/data/data-grid/column-dimensions/column-dimensions.md
+++ b/docs/data/data-grid/column-dimensions/column-dimensions.md
@@ -58,6 +58,45 @@ To capture changes in the width of a column there are two callbacks that are cal
- `onColumnResize`: Called while a column is being resized.
- `onColumnWidthChange`: Called after the width of a column is changed, but not during resizing.
+## Autosizing [](/x/introduction/licensing/#pro-plan 'Pro plan')
+
+`DataGridPro` allows to autosize the columns' dimensions based on their content. Autosizing is enabled by default. To turn it off, pass the `disableAutosize` prop to the datagrid.
+
+Autosizing can be used by one of the following methods:
+
+- Adding the `autosizeOnMount` prop,
+- Double-clicking a column header separator on the grid,
+- Calling the `apiRef.current.autosizeColumns(options)` API method.
+
+You can pass options directly to the API method when you call it. To configure autosize for the other two methods, provide the options in the `autosizeOptions` prop.
+
+Note that for the separator double-click method, the `autosizeOptions.columns` will be replaced by the respective column user double-clicked on.
+
+In all the cases, the `colDef.minWidth` and `colDef.maxWidth` options will be respected.
+
+```tsx
+
+```
+
+{{"demo": "ColumnAutosizing.js", "disableAd": true, "bg": "inline"}}
+
+:::warning
+Autosizing has no effect if dynamic row height is enabled.
+:::
+
+:::warning
+The data grid can only autosize based on the currently rendered cells.
+
+DOM access is required to accurately calculate dimensions, so unmounted cells (when [virtualization](/x/react-data-grid/virtualization/) is on) cannot be sized. If you need a bigger row sample, [open an issue](https://github.com/mui/mui-x/issues) to discuss it further.
+:::
+
## API
- [DataGrid](/x/api/data-grid/data-grid/)
diff --git a/docs/data/data-grid/getting-started/getting-started.md b/docs/data/data-grid/getting-started/getting-started.md
index 09b092da8d60f..1503de859a2df 100644
--- a/docs/data/data-grid/getting-started/getting-started.md
+++ b/docs/data/data-grid/getting-started/getting-started.md
@@ -178,6 +178,7 @@ The enterprise components come in two plans: Pro and Premium.
| [Column groups](/x/react-data-grid/column-groups/) | ✅ | ✅ | ✅ |
| [Column spanning](/x/react-data-grid/column-spanning/) | ✅ | ✅ | ✅ |
| [Column resizing](/x/react-data-grid/column-dimensions/#resizing) | ❌ | ✅ | ✅ |
+| [Column autosizing](/x/react-data-grid/column-dimensions/#autosizing) | ❌ | ✅ | ✅ |
| [Column reorder](/x/react-data-grid/column-ordering/) | ❌ | ✅ | ✅ |
| [Column pinning](/x/react-data-grid/column-pinning/) | ❌ | ✅ | ✅ |
| **Row** | | | |
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 bdc26c3d9d287..214405c70d17f 100644
--- a/docs/pages/x/api/data-grid/data-grid-premium.json
+++ b/docs/pages/x/api/data-grid/data-grid-premium.json
@@ -22,6 +22,13 @@
"aria-labelledby": { "type": { "name": "string" } },
"autoHeight": { "type": { "name": "bool" } },
"autoPageSize": { "type": { "name": "bool" } },
+ "autosizeOnMount": { "type": { "name": "bool" } },
+ "autosizeOptions": {
+ "type": {
+ "name": "shape",
+ "description": "{ columns?: Array<string>, expand?: bool, includeHeaders?: bool, includeOutliers?: bool, outliersFactor?: number }"
+ }
+ },
"cellModesModel": { "type": { "name": "object" } },
"checkboxSelection": { "type": { "name": "bool" } },
"checkboxSelectionVisibleOnly": {
@@ -56,6 +63,7 @@
"type": { "name": "arrayOf", "description": "Array<number
| string>" }
},
"disableAggregation": { "type": { "name": "bool" } },
+ "disableAutosize": { "type": { "name": "bool" } },
"disableChildrenFiltering": { "type": { "name": "bool" } },
"disableChildrenSorting": { "type": { "name": "bool" } },
"disableClipboardPaste": { "type": { "name": "bool" } },
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 726b11be83007..ae3753d91c47f 100644
--- a/docs/pages/x/api/data-grid/data-grid-pro.json
+++ b/docs/pages/x/api/data-grid/data-grid-pro.json
@@ -13,6 +13,13 @@
"aria-labelledby": { "type": { "name": "string" } },
"autoHeight": { "type": { "name": "bool" } },
"autoPageSize": { "type": { "name": "bool" } },
+ "autosizeOnMount": { "type": { "name": "bool" } },
+ "autosizeOptions": {
+ "type": {
+ "name": "shape",
+ "description": "{ columns?: Array<string>, expand?: bool, includeHeaders?: bool, includeOutliers?: bool, outliersFactor?: number }"
+ }
+ },
"cellModesModel": { "type": { "name": "object" } },
"checkboxSelection": { "type": { "name": "bool" } },
"checkboxSelectionVisibleOnly": {
@@ -46,6 +53,7 @@
"detailPanelExpandedRowIds": {
"type": { "name": "arrayOf", "description": "Array<number
| string>" }
},
+ "disableAutosize": { "type": { "name": "bool" } },
"disableChildrenFiltering": { "type": { "name": "bool" } },
"disableChildrenSorting": { "type": { "name": "bool" } },
"disableColumnFilter": { "type": { "name": "bool" } },
diff --git a/docs/pages/x/api/data-grid/grid-api.md b/docs/pages/x/api/data-grid/grid-api.md
index fd398b891abd1..d7c967eb00f73 100644
--- a/docs/pages/x/api/data-grid/grid-api.md
+++ b/docs/pages/x/api/data-grid/grid-api.md
@@ -27,6 +27,7 @@ import { GridApi } from '@mui/x-data-grid';
| :----------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| addRowGroupingCriteria [](/x/introduction/licensing/#premium-plan) | (groupingCriteriaField: string, groupingIndex?: number) => void | Adds the field to the row grouping model. |
| applySorting | () => void | Applies the current sort model to the rows. |
+| autosizeColumns [](/x/introduction/licensing/#pro-plan) | (options?: GridAutosizeOptions) => Promise<void> | Auto-size the columns of the grid based on the cells' content and the space available. |
| deleteFilterItem | (item: GridFilterItem) => void | Deletes a [GridFilterItem](/x/api/data-grid/grid-filter-item/). |
| exportDataAsCsv | (options?: GridCsvExportOptions) => void | Downloads and exports a CSV of the grid's data. |
| exportDataAsExcel [](/x/introduction/licensing/#premium-plan) | (options?: GridExcelExportOptions) => Promise<void> | Downloads and exports an Excel file of the grid's data. |
@@ -137,7 +138,9 @@ import { GridApi } from '@mui/x-data-grid';
| unstable_replaceRows | (firstRowToReplace: number, newRows: GridRowModel[]) => void | Replace a set of rows with new rows. |
| unstable_selectCellRange [](/x/introduction/licensing/#premium-plan) | (start: GridCellCoordinates, end: GridCellCoordinates, keepOtherSelected?: boolean) => void | Selects all cells that are inside the range given by `start` and `end` coordinates. |
| unstable_setCellSelectionModel [](/x/introduction/licensing/#premium-plan) | (newModel: GridCellSelectionModel) => void | Updates the selected cells to be those passed to the `newModel` argument.
Any cell already selected will be unselected. |
+| unstable_setColumnVirtualization | (enabled: boolean) => void | Enable/disable column virtualization. |
| unstable_setPinnedRows [](/x/introduction/licensing/#pro-plan) | (pinnedRows?: GridPinnedRowsProp) => void | Changes the pinned rows. |
+| unstable_setVirtualization | (enabled: boolean) => void | Enable/disable virtualization. |
| updateColumns | (cols: GridColDef[]) => void | Updates the definition of multiple columns at the same time. |
| updateRows | (updates: GridRowModelUpdate[]) => void | Allows to update, insert and delete rows. |
| upsertFilterItem | (item: GridFilterItem) => void | Updates or inserts a [GridFilterItem](/x/api/data-grid/grid-filter-item/). |
diff --git a/docs/pages/x/api/data-grid/grid-column-resize-api.json b/docs/pages/x/api/data-grid/grid-column-resize-api.json
new file mode 100644
index 0000000000000..50378d4b2d1a4
--- /dev/null
+++ b/docs/pages/x/api/data-grid/grid-column-resize-api.json
@@ -0,0 +1,11 @@
+{
+ "name": "GridColumnResizeApi",
+ "description": "The Resize API interface that is available in the grid `apiRef`.",
+ "properties": [
+ {
+ "name": "autosizeColumns",
+ "description": "Auto-size the columns of the grid based on the cells' content and the space available.",
+ "type": "(options?: GridAutosizeOptions) => Promise"
+ }
+ ]
+}
diff --git a/docs/pages/x/api/data-grid/grid-virtualization-api.json b/docs/pages/x/api/data-grid/grid-virtualization-api.json
new file mode 100644
index 0000000000000..d295357e4397a
--- /dev/null
+++ b/docs/pages/x/api/data-grid/grid-virtualization-api.json
@@ -0,0 +1,16 @@
+{
+ "name": "GridVirtualizationApi",
+ "description": "",
+ "properties": [
+ {
+ "name": "unstable_setColumnVirtualization",
+ "description": "Enable/disable column virtualization.",
+ "type": "(enabled: boolean) => void"
+ },
+ {
+ "name": "unstable_setVirtualization",
+ "description": "Enable/disable virtualization.",
+ "type": "(enabled: boolean) => void"
+ }
+ ]
+}
diff --git a/docs/pages/x/api/data-grid/selectors.json b/docs/pages/x/api/data-grid/selectors.json
index 418ad2366f6bc..ed03b0af5172a 100644
--- a/docs/pages/x/api/data-grid/selectors.json
+++ b/docs/pages/x/api/data-grid/selectors.json
@@ -424,6 +424,27 @@
"description": "",
"supportsApiRef": true
},
+ {
+ "name": "gridVirtualizationColumnEnabledSelector",
+ "returnType": "boolean",
+ "category": "Virtualization",
+ "description": "Get the enabled state for virtualization",
+ "supportsApiRef": true
+ },
+ {
+ "name": "gridVirtualizationEnabledSelector",
+ "returnType": "boolean",
+ "category": "Virtualization",
+ "description": "Get the enabled state for virtualization",
+ "supportsApiRef": true
+ },
+ {
+ "name": "gridVirtualizationSelector",
+ "returnType": "GridVirtualizationState",
+ "category": "Virtualization",
+ "description": "Get the columns state",
+ "supportsApiRef": false
+ },
{
"name": "gridVisibleColumnDefinitionsSelector",
"returnType": "GridStateColDef[]",
diff --git a/docs/scripts/api/buildInterfacesDocumentation.ts b/docs/scripts/api/buildInterfacesDocumentation.ts
index e9345308365e8..48b578dfda536 100644
--- a/docs/scripts/api/buildInterfacesDocumentation.ts
+++ b/docs/scripts/api/buildInterfacesDocumentation.ts
@@ -40,21 +40,22 @@ interface ParsedProperty {
}
const GRID_API_INTERFACES_WITH_DEDICATED_PAGES = [
- 'GridRowSelectionApi',
- 'GridRowMultiSelectionApi',
'GridCellSelectionApi',
- 'GridFilterApi',
- 'GridSortApi',
- 'GridPaginationApi',
- 'GridCsvExportApi',
- 'GridScrollApi',
- 'GridEditingApi',
- 'GridRowGroupingApi',
'GridColumnPinningApi',
+ 'GridColumnResizeApi',
+ 'GridCsvExportApi',
'GridDetailPanelApi',
- 'GridPrintExportApi',
- 'GridDisableVirtualizationApi',
+ 'GridEditingApi',
'GridExcelExportApi',
+ 'GridFilterApi',
+ 'GridPaginationApi',
+ 'GridPrintExportApi',
+ 'GridRowGroupingApi',
+ 'GridRowMultiSelectionApi',
+ 'GridRowSelectionApi',
+ 'GridScrollApi',
+ 'GridSortApi',
+ 'GridVirtualizationApi',
];
const OTHER_GRID_INTERFACES_WITH_DEDICATED_PAGES = [
diff --git a/docs/translations/api-docs/data-grid/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium.json
index 6777cb6c53e5a..56c0232c3a532 100644
--- a/docs/translations/api-docs/data-grid/data-grid-premium.json
+++ b/docs/translations/api-docs/data-grid/data-grid-premium.json
@@ -41,6 +41,16 @@
"deprecated": "",
"typeDescriptions": {}
},
+ "autosizeOnMount": {
+ "description": "If true
, columns are autosized after the datagrid is mounted.",
+ "deprecated": "",
+ "typeDescriptions": {}
+ },
+ "autosizeOptions": {
+ "description": "The options for autosize when user-initiated.",
+ "deprecated": "",
+ "typeDescriptions": {}
+ },
"cellModesModel": {
"description": "Controls the modes of the cells.",
"deprecated": "",
@@ -121,6 +131,11 @@
"deprecated": "",
"typeDescriptions": {}
},
+ "disableAutosize": {
+ "description": "If true
, column autosizing on header separator double-click is disabled.",
+ "deprecated": "",
+ "typeDescriptions": {}
+ },
"disableChildrenFiltering": {
"description": "If true
, the filtering will only be applied to the top level rows when grouping rows with the treeData
prop.",
"deprecated": "",
diff --git a/docs/translations/api-docs/data-grid/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro.json
index 141f12c38f150..00bf251a86214 100644
--- a/docs/translations/api-docs/data-grid/data-grid-pro.json
+++ b/docs/translations/api-docs/data-grid/data-grid-pro.json
@@ -26,6 +26,16 @@
"deprecated": "",
"typeDescriptions": {}
},
+ "autosizeOnMount": {
+ "description": "If true
, columns are autosized after the datagrid is mounted.",
+ "deprecated": "",
+ "typeDescriptions": {}
+ },
+ "autosizeOptions": {
+ "description": "The options for autosize when user-initiated.",
+ "deprecated": "",
+ "typeDescriptions": {}
+ },
"cellModesModel": {
"description": "Controls the modes of the cells.",
"deprecated": "",
@@ -101,6 +111,11 @@
"deprecated": "",
"typeDescriptions": {}
},
+ "disableAutosize": {
+ "description": "If true
, column autosizing on header separator double-click is disabled.",
+ "deprecated": "",
+ "typeDescriptions": {}
+ },
"disableChildrenFiltering": {
"description": "If true
, the filtering will only be applied to the top level rows when grouping rows with the treeData
prop.",
"deprecated": "",
diff --git a/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx
index 0357715bb15ce..95bb964b8d390 100644
--- a/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx
+++ b/packages/grid/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx
@@ -107,6 +107,21 @@ DataGridPremiumRaw.propTypes = {
* @default false
*/
autoPageSize: PropTypes.bool,
+ /**
+ * If `true`, columns are autosized after the datagrid is mounted.
+ * @default false
+ */
+ autosizeOnMount: PropTypes.bool,
+ /**
+ * The options for autosize when user-initiated.
+ */
+ autosizeOptions: PropTypes.shape({
+ columns: PropTypes.arrayOf(PropTypes.string),
+ expand: PropTypes.bool,
+ includeHeaders: PropTypes.bool,
+ includeOutliers: PropTypes.bool,
+ outliersFactor: PropTypes.number,
+ }),
/**
* Controls the modes of the cells.
*/
@@ -195,6 +210,11 @@ DataGridPremiumRaw.propTypes = {
* @default false
*/
disableAggregation: PropTypes.bool,
+ /**
+ * If `true`, column autosizing on header separator double-click is disabled.
+ * @default false
+ */
+ disableAutosize: PropTypes.bool,
/**
* If `true`, the filtering will only be applied to the top level rows when grouping rows with the `treeData` prop.
* @default false
diff --git a/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx b/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx
index 4da70394a3142..aaca7bff91acb 100644
--- a/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx
+++ b/packages/grid/x-data-grid-premium/src/DataGridPremium/useDataGridPremiumComponent.tsx
@@ -62,6 +62,8 @@ import {
useGridLazyLoaderPreProcessors,
headerFilteringStateInitializer,
useGridHeaderFiltering,
+ virtualizationStateInitializer,
+ useGridVirtualization,
} from '@mui/x-data-grid-pro/internals';
import { GridApiPremium, GridPrivateApiPremium } from '../models/gridApiPremium';
import { DataGridPremiumProcessedProps } from '../models/dataGridPremiumProps';
@@ -87,91 +89,90 @@ export const useDataGridPremiumComponent = (
inputApiRef: React.MutableRefObject | undefined,
props: DataGridPremiumProcessedProps,
) => {
- const privateApiRef = useGridInitialization(
- inputApiRef,
- props,
- );
+ const apiRef = useGridInitialization(inputApiRef, props);
/**
* Register all pre-processors called during state initialization here.
*/
- useGridRowSelectionPreProcessors(privateApiRef, props);
- useGridRowReorderPreProcessors(privateApiRef, props);
- useGridRowGroupingPreProcessors(privateApiRef, props);
- useGridTreeDataPreProcessors(privateApiRef, props);
- useGridLazyLoaderPreProcessors(privateApiRef, props);
- useGridRowPinningPreProcessors(privateApiRef);
- useGridAggregationPreProcessors(privateApiRef, props);
- useGridDetailPanelPreProcessors(privateApiRef, props);
+ useGridRowSelectionPreProcessors(apiRef, props);
+ useGridRowReorderPreProcessors(apiRef, props);
+ useGridRowGroupingPreProcessors(apiRef, props);
+ useGridTreeDataPreProcessors(apiRef, props);
+ useGridLazyLoaderPreProcessors(apiRef, props);
+ useGridRowPinningPreProcessors(apiRef);
+ useGridAggregationPreProcessors(apiRef, props);
+ useGridDetailPanelPreProcessors(apiRef, props);
// The column pinning `hydrateColumns` pre-processor must be after every other `hydrateColumns` pre-processors
// Because it changes the order of the columns.
- useGridColumnPinningPreProcessors(privateApiRef, props);
- useGridRowsPreProcessors(privateApiRef);
+ useGridColumnPinningPreProcessors(apiRef, props);
+ useGridRowsPreProcessors(apiRef);
/**
* Register all state initializers here.
*/
- useGridInitializeState(headerFilteringStateInitializer, privateApiRef, props);
- useGridInitializeState(rowGroupingStateInitializer, privateApiRef, props);
- useGridInitializeState(aggregationStateInitializer, privateApiRef, props);
- useGridInitializeState(rowSelectionStateInitializer, privateApiRef, props);
- useGridInitializeState(cellSelectionStateInitializer, privateApiRef, props);
- useGridInitializeState(detailPanelStateInitializer, privateApiRef, props);
- useGridInitializeState(columnPinningStateInitializer, privateApiRef, props);
- useGridInitializeState(columnsStateInitializer, privateApiRef, props);
- useGridInitializeState(rowPinningStateInitializer, privateApiRef, props);
- useGridInitializeState(rowsStateInitializer, privateApiRef, props);
- useGridInitializeState(editingStateInitializer, privateApiRef, props);
- useGridInitializeState(focusStateInitializer, privateApiRef, props);
- useGridInitializeState(sortingStateInitializer, privateApiRef, props);
- useGridInitializeState(preferencePanelStateInitializer, privateApiRef, props);
- useGridInitializeState(filterStateInitializer, privateApiRef, props);
- useGridInitializeState(densityStateInitializer, privateApiRef, props);
- useGridInitializeState(columnReorderStateInitializer, privateApiRef, props);
- useGridInitializeState(columnResizeStateInitializer, privateApiRef, props);
- useGridInitializeState(paginationStateInitializer, privateApiRef, props);
- useGridInitializeState(rowsMetaStateInitializer, privateApiRef, props);
- useGridInitializeState(columnMenuStateInitializer, privateApiRef, props);
- useGridInitializeState(columnGroupsStateInitializer, privateApiRef, props);
+ useGridInitializeState(headerFilteringStateInitializer, apiRef, props);
+ useGridInitializeState(rowGroupingStateInitializer, apiRef, props);
+ useGridInitializeState(aggregationStateInitializer, apiRef, props);
+ useGridInitializeState(rowSelectionStateInitializer, apiRef, props);
+ useGridInitializeState(cellSelectionStateInitializer, apiRef, props);
+ useGridInitializeState(detailPanelStateInitializer, apiRef, props);
+ useGridInitializeState(columnPinningStateInitializer, apiRef, props);
+ useGridInitializeState(columnsStateInitializer, apiRef, props);
+ useGridInitializeState(rowPinningStateInitializer, apiRef, props);
+ useGridInitializeState(rowsStateInitializer, apiRef, props);
+ useGridInitializeState(editingStateInitializer, apiRef, props);
+ useGridInitializeState(focusStateInitializer, apiRef, props);
+ useGridInitializeState(sortingStateInitializer, apiRef, props);
+ useGridInitializeState(preferencePanelStateInitializer, apiRef, props);
+ useGridInitializeState(filterStateInitializer, apiRef, props);
+ useGridInitializeState(densityStateInitializer, apiRef, props);
+ useGridInitializeState(columnReorderStateInitializer, apiRef, props);
+ useGridInitializeState(columnResizeStateInitializer, apiRef, props);
+ useGridInitializeState(paginationStateInitializer, apiRef, props);
+ useGridInitializeState(rowsMetaStateInitializer, apiRef, props);
+ useGridInitializeState(columnMenuStateInitializer, apiRef, props);
+ useGridInitializeState(columnGroupsStateInitializer, apiRef, props);
+ useGridInitializeState(virtualizationStateInitializer, apiRef, props);
- useGridRowGrouping(privateApiRef, props);
- useGridHeaderFiltering(privateApiRef, props);
- useGridTreeData(privateApiRef);
- useGridAggregation(privateApiRef, props);
- useGridKeyboardNavigation(privateApiRef, props);
- useGridRowSelection(privateApiRef, props);
- useGridCellSelection(privateApiRef, props);
- useGridColumnPinning(privateApiRef, props);
- useGridRowPinning(privateApiRef, props);
- useGridColumns(privateApiRef, props);
- useGridRows(privateApiRef, props);
- useGridParamsApi(privateApiRef, props);
- useGridDetailPanel(privateApiRef, props);
- useGridColumnSpanning(privateApiRef);
- useGridColumnGrouping(privateApiRef, props);
- useGridClipboardImport(privateApiRef, props);
- useGridEditing(privateApiRef, props);
- useGridFocus(privateApiRef, props);
- useGridPreferencesPanel(privateApiRef, props);
- useGridFilter(privateApiRef, props);
- useGridSorting(privateApiRef, props);
- useGridDensity(privateApiRef, props);
- useGridColumnReorder(privateApiRef, props);
- useGridColumnResize(privateApiRef, props);
- useGridPagination(privateApiRef, props);
- useGridRowsMeta(privateApiRef, props);
- useGridRowReorder(privateApiRef, props);
- useGridScroll(privateApiRef, props);
- useGridInfiniteLoader(privateApiRef, props);
- useGridLazyLoader(privateApiRef, props);
- useGridColumnMenu(privateApiRef);
- useGridCsvExport(privateApiRef, props);
- useGridPrintExport(privateApiRef, props);
- useGridExcelExport(privateApiRef, props);
- useGridClipboard(privateApiRef, props);
- useGridDimensions(privateApiRef, props);
- useGridEvents(privateApiRef, props);
- useGridStatePersistence(privateApiRef);
+ useGridRowGrouping(apiRef, props);
+ useGridHeaderFiltering(apiRef, props);
+ useGridTreeData(apiRef);
+ useGridAggregation(apiRef, props);
+ useGridKeyboardNavigation(apiRef, props);
+ useGridRowSelection(apiRef, props);
+ useGridCellSelection(apiRef, props);
+ useGridColumnPinning(apiRef, props);
+ useGridRowPinning(apiRef, props);
+ useGridColumns(apiRef, props);
+ useGridRows(apiRef, props);
+ useGridParamsApi(apiRef, props);
+ useGridDetailPanel(apiRef, props);
+ useGridColumnSpanning(apiRef);
+ useGridColumnGrouping(apiRef, props);
+ useGridClipboardImport(apiRef, props);
+ useGridEditing(apiRef, props);
+ useGridFocus(apiRef, props);
+ useGridPreferencesPanel(apiRef, props);
+ useGridFilter(apiRef, props);
+ useGridSorting(apiRef, props);
+ useGridDensity(apiRef, props);
+ useGridColumnReorder(apiRef, props);
+ useGridColumnResize(apiRef, props);
+ useGridPagination(apiRef, props);
+ useGridRowsMeta(apiRef, props);
+ useGridRowReorder(apiRef, props);
+ useGridScroll(apiRef, props);
+ useGridInfiniteLoader(apiRef, props);
+ useGridLazyLoader(apiRef, props);
+ useGridColumnMenu(apiRef);
+ useGridCsvExport(apiRef, props);
+ useGridPrintExport(apiRef, props);
+ useGridExcelExport(apiRef, props);
+ useGridClipboard(apiRef, props);
+ useGridDimensions(apiRef, props);
+ useGridEvents(apiRef, props);
+ useGridStatePersistence(apiRef);
+ useGridVirtualization(apiRef, props);
- return privateApiRef;
+ return apiRef;
};
diff --git a/packages/grid/x-data-grid-premium/src/models/gridApiPremium.ts b/packages/grid/x-data-grid-premium/src/models/gridApiPremium.ts
index 3577b6ca9c3bd..c28b36e4dd19c 100644
--- a/packages/grid/x-data-grid-premium/src/models/gridApiPremium.ts
+++ b/packages/grid/x-data-grid-premium/src/models/gridApiPremium.ts
@@ -2,6 +2,7 @@ import { GridPrivateOnlyApiCommon } from '@mui/x-data-grid/internals';
import {
GridApiCommon,
GridColumnPinningApi,
+ GridColumnResizeApi,
GridDetailPanelApi,
GridDetailPanelPrivateApi,
GridRowPinningApi,
@@ -21,6 +22,7 @@ export interface GridApiPremium
extends GridApiCommon,
GridRowProApi,
GridColumnPinningApi,
+ GridColumnResizeApi,
GridDetailPanelApi,
GridRowGroupingApi,
GridExcelExportApi,
@@ -29,8 +31,7 @@ export interface GridApiPremium
GridCellSelectionApi,
// APIs that are private in Community plan, but public in Pro and Premium plans
GridRowMultiSelectionApi,
- GridColumnReorderApi,
- GridRowProApi {}
+ GridColumnReorderApi {}
export interface GridPrivateApiPremium
extends GridApiPremium,
diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx
index 0231d8bb4d41c..a4e53c48bdeca 100644
--- a/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx
+++ b/packages/grid/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx
@@ -90,6 +90,21 @@ DataGridProRaw.propTypes = {
* @default false
*/
autoPageSize: PropTypes.bool,
+ /**
+ * If `true`, columns are autosized after the datagrid is mounted.
+ * @default false
+ */
+ autosizeOnMount: PropTypes.bool,
+ /**
+ * The options for autosize when user-initiated.
+ */
+ autosizeOptions: PropTypes.shape({
+ columns: PropTypes.arrayOf(PropTypes.string),
+ expand: PropTypes.bool,
+ includeHeaders: PropTypes.bool,
+ includeOutliers: PropTypes.bool,
+ outliersFactor: PropTypes.number,
+ }),
/**
* Controls the modes of the cells.
*/
@@ -173,6 +188,11 @@ DataGridProRaw.propTypes = {
detailPanelExpandedRowIds: PropTypes.arrayOf(
PropTypes.oneOfType([PropTypes.number, PropTypes.string]).isRequired,
),
+ /**
+ * If `true`, column autosizing on header separator double-click is disabled.
+ * @default false
+ */
+ disableAutosize: PropTypes.bool,
/**
* If `true`, the filtering will only be applied to the top level rows when grouping rows with the `treeData` prop.
* @default false
diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx b/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx
index e9077e1996b1d..23220818d45aa 100644
--- a/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx
+++ b/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProComponent.tsx
@@ -42,6 +42,8 @@ import {
columnGroupsStateInitializer,
headerFilteringStateInitializer,
useGridHeaderFiltering,
+ virtualizationStateInitializer,
+ useGridVirtualization,
} from '@mui/x-data-grid/internals';
import { GridApiPro, GridPrivateApiPro } from '../models/gridApiPro';
import { DataGridProProcessedProps } from '../models/dataGridProProps';
@@ -119,6 +121,7 @@ export const useDataGridProComponent = (
useGridInitializeState(rowsMetaStateInitializer, apiRef, props);
useGridInitializeState(columnMenuStateInitializer, apiRef, props);
useGridInitializeState(columnGroupsStateInitializer, apiRef, props);
+ useGridInitializeState(virtualizationStateInitializer, apiRef, props);
useGridHeaderFiltering(apiRef, props);
useGridTreeData(apiRef);
@@ -153,6 +156,7 @@ export const useDataGridProComponent = (
useGridDimensions(apiRef, props);
useGridEvents(apiRef, props);
useGridStatePersistence(apiRef);
+ useGridVirtualization(apiRef, props);
return apiRef;
};
diff --git a/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts b/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts
index 9c991e4f24d8d..eb8c0ab505db0 100644
--- a/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts
+++ b/packages/grid/x-data-grid-pro/src/DataGridPro/useDataGridProProps.ts
@@ -22,6 +22,8 @@ export const DATA_GRID_PRO_PROPS_DEFAULT_VALUES: DataGridProPropsWithDefaultValu
scrollEndThreshold: 80,
treeData: false,
defaultGroupingExpansionDepth: 0,
+ autosizeOnMount: false,
+ disableAutosize: false,
disableColumnPinning: false,
keepColumnPositionIfDraggedOutside: false,
disableChildrenFiltering: false,
diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/gridColumnResizeApi.ts b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/gridColumnResizeApi.ts
new file mode 100644
index 0000000000000..c19883f4368aa
--- /dev/null
+++ b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/gridColumnResizeApi.ts
@@ -0,0 +1,47 @@
+import { GridColDef } from '@mui/x-data-grid';
+
+export const DEFAULT_GRID_AUTOSIZE_OPTIONS = {
+ includeHeaders: true,
+ includeOutliers: false,
+ outliersFactor: 1.5,
+ expand: false,
+};
+
+export type GridAutosizeOptions = {
+ /**
+ * The columns to autosize. By default, applies to all columns.
+ */
+ columns?: GridColDef['field'][];
+ /**
+ * If true, include the header widths in the calculation.
+ * @default false
+ */
+ includeHeaders?: boolean;
+ /**
+ * If true, width outliers will be ignored.
+ * @default false
+ */
+ includeOutliers?: boolean;
+ /**
+ * The IQR factor range to detect outliers.
+ * @default 1.5
+ */
+ outliersFactor?: number;
+ /**
+ * If the total width is less than the available width, expand columns to fill it.
+ * @default false
+ */
+ expand?: boolean;
+};
+
+/**
+ * The Resize API interface that is available in the grid `apiRef`.
+ */
+export interface GridColumnResizeApi {
+ /**
+ * Auto-size the columns of the grid based on the cells' content and the space available.
+ * @param {GridAutosizeOptions} options The autosizing options
+ * @returns {Promise} A promise that resolves when autosizing is completed
+ */
+ autosizeColumns: (options?: GridAutosizeOptions) => Promise;
+}
diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/index.ts b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/index.ts
index f4105375e68ff..beb30a48691a6 100644
--- a/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/index.ts
+++ b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/index.ts
@@ -1,2 +1,3 @@
export * from './columnResizeSelector';
export * from './columnResizeState';
+export * from './gridColumnResizeApi';
diff --git a/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx
index 93161721c049d..fdf34490bd9ae 100644
--- a/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx
+++ b/packages/grid/x-data-grid-pro/src/hooks/features/columnResize/useGridColumnResize.tsx
@@ -11,14 +11,22 @@ import {
GridColumnResizeParams,
useGridApiEventHandler,
useGridApiOptionHandler,
+ useGridApiMethod,
useGridNativeEventListener,
useGridLogger,
+ useGridSelector,
+ gridVirtualizationColumnEnabledSelector,
} from '@mui/x-data-grid';
import {
clamp,
findParentElementFromClassName,
- GridStateInitializer,
+ gridColumnsStateSelector,
+ useOnMount,
+ useTimeout,
+ createControllablePromise,
+ ControllablePromise,
GridStateColDef,
+ GridStateInitializer,
} from '@mui/x-data-grid/internals';
import { useTheme, Direction } from '@mui/material/styles';
import {
@@ -26,9 +34,18 @@ import {
getFieldFromHeaderElem,
findHeaderElementFromField,
findGroupHeaderElementsFromField,
+ findGridHeader,
+ findGridCells,
} from '../../../utils/domUtils';
import { GridPrivateApiPro } from '../../../models/gridApiPro';
import { DataGridProProcessedProps } from '../../../models/dataGridProProps';
+import {
+ GridAutosizeOptions,
+ GridColumnResizeApi,
+ DEFAULT_GRID_AUTOSIZE_OPTIONS,
+} from './gridColumnResizeApi';
+
+type AutosizeOptionsRequired = Required;
type ResizeDirection = keyof typeof GridColumnHeaderSeparatorSides;
@@ -119,18 +136,139 @@ function getResizeDirection(element: HTMLElement, direction: Direction) {
return side;
}
+function preventClick(event: MouseEvent) {
+ event.preventDefault();
+ event.stopImmediatePropagation();
+}
+
+/**
+ * Checker that returns a promise that resolves when the column virtualization
+ * is disabled.
+ */
+function useColumnVirtualizationDisabled(apiRef: React.MutableRefObject) {
+ const promise = React.useRef();
+ const selector = () => gridVirtualizationColumnEnabledSelector(apiRef);
+ const value = useGridSelector(apiRef, selector);
+
+ React.useEffect(() => {
+ if (promise.current && value === false) {
+ promise.current.resolve();
+ promise.current = undefined;
+ }
+ });
+
+ const asyncCheck = () => {
+ if (!promise.current) {
+ if (selector() === false) {
+ return Promise.resolve();
+ }
+ promise.current = createControllablePromise();
+ }
+ return promise.current;
+ };
+
+ return asyncCheck;
+}
+
+/**
+ * Basic statistical outlier detection, checks if the value is `F * IQR` away from
+ * the Q1 and Q3 boundaries. IQR: interquartile range.
+ */
+function excludeOutliers(inputValues: number[], factor: number) {
+ if (inputValues.length < 4) {
+ return inputValues;
+ }
+
+ const values = inputValues.slice();
+ values.sort((a, b) => a - b);
+
+ const q1 = values[Math.floor(values.length * 0.25)];
+ const q3 = values[Math.floor(values.length * 0.75) - 1];
+ const iqr = q3 - q1;
+
+ // We make a small adjustment if `iqr < 5` for the cases where the IQR is
+ // very small (e.g. zero) due to very close by values in the input data.
+ // Otherwise, with an IQR of `0`, anything outside that would be considered
+ // an outlier, but it makes more sense visually to allow for this 5px variance
+ // rather than showing a cropped cell.
+ const deviation = iqr < 5 ? 5 : iqr * factor;
+
+ return values.filter((v) => v > q1 - deviation && v < q3 + deviation);
+}
+
+function extractColumnWidths(
+ apiRef: React.MutableRefObject,
+ options: AutosizeOptionsRequired,
+ columns: GridStateColDef[],
+) {
+ const widthByField = {} as Record;
+
+ columns.forEach((column) => {
+ const cells = findGridCells(apiRef.current, column.field);
+
+ const widths = cells.map((cell) => {
+ const id = cell.parentElement!.getAttribute('data-id') ?? '';
+ const hasAutoHeight = apiRef.current.rowHasAutoHeight(id);
+ if (hasAutoHeight) {
+ return (column as any).computedWidth ?? column.width;
+ }
+ const style = window.getComputedStyle(cell, null);
+ const paddingWidth = parseInt(style.paddingLeft, 10) + parseInt(style.paddingRight, 10);
+ const contentWidth = (cell.firstElementChild?.scrollWidth ?? -1) + 1;
+ return paddingWidth + contentWidth;
+ });
+
+ const filteredWidths = options.includeOutliers
+ ? widths
+ : excludeOutliers(widths, options.outliersFactor);
+
+ if (options.includeHeaders) {
+ const header = findGridHeader(apiRef.current, column.field);
+ if (header) {
+ const title = header.querySelector(`.${gridClasses.columnHeaderTitle}`);
+ const content = header.querySelector(`.${gridClasses.columnHeaderTitleContainerContent}`)!;
+ const element = title ?? content;
+
+ const style = window.getComputedStyle(header, null);
+ const paddingWidth = parseInt(style.paddingLeft, 10) + parseInt(style.paddingRight, 10);
+ const contentWidth = element.scrollWidth + 1;
+ const width = paddingWidth + contentWidth;
+
+ filteredWidths.push(width);
+ }
+ }
+
+ const hasColumnMin = column.minWidth !== -Infinity && column.minWidth !== undefined;
+ const hasColumnMax = column.maxWidth !== Infinity && column.maxWidth !== undefined;
+
+ const min = hasColumnMin ? column.minWidth! : 0;
+ const max = hasColumnMax ? column.maxWidth! : Infinity;
+ const maxContent = filteredWidths.length === 0 ? 0 : Math.max(...filteredWidths);
+
+ widthByField[column.field] = clamp(maxContent, min, max);
+ });
+
+ return widthByField;
+}
+
export const columnResizeStateInitializer: GridStateInitializer = (state) => ({
...state,
columnResize: { resizingColumnField: '' },
});
-
/**
* @requires useGridColumns (method, event)
* TODO: improve experience for last column
*/
export const useGridColumnResize = (
apiRef: React.MutableRefObject,
- props: Pick,
+ props: Pick<
+ DataGridProProcessedProps,
+ | 'autosizeOptions'
+ | 'autosizeOnMount'
+ | 'disableAutosize'
+ | 'onColumnResize'
+ | 'onColumnWidthChange'
+ >,
) => {
const logger = useGridLogger(apiRef, 'useGridColumnResize');
@@ -147,7 +285,7 @@ export const useGridColumnResize = (
const initialOffsetToSeparator = React.useRef();
const resizeDirection = React.useRef();
- const stopResizeEventTimeout = React.useRef();
+ const stopResizeEventTimeout = useTimeout();
const touchId = React.useRef();
const updateWidth = (newWidth: number) => {
@@ -201,8 +339,7 @@ export const useGridColumnResize = (
);
}
- clearTimeout(stopResizeEventTimeout.current);
- stopResizeEventTimeout.current = setTimeout(() => {
+ stopResizeEventTimeout.start(0, () => {
apiRef.current.publishEvent('columnResizeStop', null, nativeEvent);
});
};
@@ -234,66 +371,6 @@ export const useGridColumnResize = (
apiRef.current.publishEvent('columnResize', params, nativeEvent);
});
- const handleColumnResizeMouseDown: GridEventListener<'columnSeparatorMouseDown'> =
- useEventCallback(({ colDef }, event) => {
- // Only handle left clicks
- if (event.button !== 0) {
- return;
- }
-
- // Skip if the column isn't resizable
- if (!event.currentTarget.classList.contains(gridClasses['columnSeparator--resizable'])) {
- return;
- }
-
- // Avoid text selection
- event.preventDefault();
-
- logger.debug(`Start Resize on col ${colDef.field}`);
- apiRef.current.publishEvent('columnResizeStart', { field: colDef.field }, event);
-
- colDefRef.current = colDef as GridStateColDef;
- colElementRef.current =
- apiRef.current.columnHeadersContainerElementRef?.current!.querySelector(
- `[data-field="${colDef.field}"]`,
- )!;
-
- const headerFilterRowElement = apiRef.current.headerFiltersElementRef?.current;
-
- if (headerFilterRowElement) {
- headerFilterElementRef.current = headerFilterRowElement.querySelector(
- `[data-field="${colDef.field}"]`,
- ) as HTMLDivElement;
- }
-
- colGroupingElementRef.current = findGroupHeaderElementsFromField(
- apiRef.current.columnHeadersContainerElementRef?.current!,
- colDef.field,
- );
-
- colCellElementsRef.current = findGridCellElementsFromCol(
- colElementRef.current,
- apiRef.current,
- );
-
- const doc = ownerDocument(apiRef.current.rootElementRef!.current);
- doc.body.style.cursor = 'col-resize';
-
- resizeDirection.current = getResizeDirection(event.currentTarget, theme.direction);
-
- initialOffsetToSeparator.current = computeOffsetToSeparator(
- event.clientX,
- colElementRef.current!.getBoundingClientRect(),
- resizeDirection.current,
- );
-
- doc.addEventListener('mousemove', handleResizeMouseMove);
- doc.addEventListener('mouseup', handleResizeMouseUp);
-
- // Fixes https://github.com/mui/mui-x/issues/4777
- colElementRef.current.style.pointerEvents = 'none';
- });
-
const handleTouchEnd = useEventCallback((nativeEvent: any) => {
const finger = trackFinger(nativeEvent, touchId.current);
@@ -395,6 +472,11 @@ export const useGridColumnResize = (
doc.removeEventListener('mouseup', handleResizeMouseUp);
doc.removeEventListener('touchmove', handleTouchMove);
doc.removeEventListener('touchend', handleTouchEnd);
+ // The click event runs right after the mouseup event, we want to wait until it
+ // has been canceled before removing our handler.
+ setTimeout(() => {
+ doc.removeEventListener('click', preventClick, true);
+ }, 100);
if (colElementRef.current) {
colElementRef.current!.style.pointerEvents = 'unset';
}
@@ -426,12 +508,172 @@ export const useGridColumnResize = (
apiRef.current.forceUpdate();
}, [apiRef]);
- React.useEffect(() => {
- return () => {
- clearTimeout(stopResizeEventTimeout.current);
- stopListening();
- };
- }, [apiRef, handleTouchStart, stopListening]);
+ const handleColumnResizeMouseDown: GridEventListener<'columnSeparatorMouseDown'> =
+ useEventCallback(({ colDef }, event) => {
+ // Only handle left clicks
+ if (event.button !== 0) {
+ return;
+ }
+
+ // Skip if the column isn't resizable
+ if (!event.currentTarget.classList.contains(gridClasses['columnSeparator--resizable'])) {
+ return;
+ }
+
+ // Avoid text selection
+ event.preventDefault();
+
+ logger.debug(`Start Resize on col ${colDef.field}`);
+ apiRef.current.publishEvent('columnResizeStart', { field: colDef.field }, event);
+
+ colDefRef.current = colDef as GridStateColDef;
+ colElementRef.current =
+ apiRef.current.columnHeadersContainerElementRef?.current!.querySelector(
+ `[data-field="${colDef.field}"]`,
+ )!;
+
+ const headerFilterRowElement = apiRef.current.headerFiltersElementRef?.current;
+
+ if (headerFilterRowElement) {
+ headerFilterElementRef.current = headerFilterRowElement.querySelector(
+ `[data-field="${colDef.field}"]`,
+ ) as HTMLDivElement;
+ }
+
+ colGroupingElementRef.current = findGroupHeaderElementsFromField(
+ apiRef.current.columnHeadersContainerElementRef?.current!,
+ colDef.field,
+ );
+
+ colCellElementsRef.current = findGridCellElementsFromCol(
+ colElementRef.current,
+ apiRef.current,
+ );
+
+ const doc = ownerDocument(apiRef.current.rootElementRef!.current);
+ doc.body.style.cursor = 'col-resize';
+
+ resizeDirection.current = getResizeDirection(event.currentTarget, theme.direction);
+
+ initialOffsetToSeparator.current = computeOffsetToSeparator(
+ event.clientX,
+ colElementRef.current!.getBoundingClientRect(),
+ resizeDirection.current,
+ );
+
+ doc.addEventListener('mousemove', handleResizeMouseMove);
+ doc.addEventListener('mouseup', handleResizeMouseUp);
+
+ // Prevent the click event if we have resized the column.
+ // Fixes https://github.com/mui/mui-x/issues/4777
+ doc.addEventListener('click', preventClick, true);
+ });
+
+ const handleColumnSeparatorDoubleClick: GridEventListener<'columnSeparatorDoubleClick'> =
+ useEventCallback((params, event) => {
+ if (props.disableAutosize) {
+ return;
+ }
+
+ // Only handle left clicks
+ if (event.button !== 0) {
+ return;
+ }
+
+ const column = apiRef.current.state.columns.lookup[params.field];
+ if (column.resizable === false) {
+ return;
+ }
+
+ apiRef.current.autosizeColumns({
+ ...props.autosizeOptions,
+ columns: [column.field],
+ });
+ });
+
+ /**
+ * API METHODS
+ */
+
+ const columnVirtualizationDisabled = useColumnVirtualizationDisabled(apiRef);
+ const isAutosizingRef = React.useRef(false);
+ const autosizeColumns = React.useCallback(
+ async (userOptions) => {
+ const root = apiRef.current.rootElementRef?.current;
+ if (!root) {
+ return;
+ }
+ if (isAutosizingRef.current) {
+ return;
+ }
+ isAutosizingRef.current = true;
+
+ const state = gridColumnsStateSelector(apiRef.current.state);
+ const options = {
+ ...DEFAULT_GRID_AUTOSIZE_OPTIONS,
+ ...userOptions,
+ columns: userOptions?.columns ?? state.orderedFields,
+ };
+ options.columns = options.columns.filter((c) => state.columnVisibilityModel[c] !== false);
+
+ const columns = options.columns.map((c) => apiRef.current.state.columns.lookup[c]);
+
+ try {
+ apiRef.current.unstable_setColumnVirtualization(false);
+ await columnVirtualizationDisabled();
+
+ const widthByField = extractColumnWidths(apiRef, options, columns);
+
+ const newColumns = columns.map((column) => ({
+ ...column,
+ width: widthByField[column.field],
+ computedWidth: widthByField[column.field],
+ }));
+
+ if (options.expand) {
+ const visibleColumns = state.orderedFields
+ .map((field) => state.lookup[field])
+ .filter((c) => state.columnVisibilityModel[c.field] !== false);
+
+ const totalWidth = visibleColumns.reduce(
+ (total, column) =>
+ total + (widthByField[column.field] ?? column.computedWidth ?? column.width),
+ 0,
+ );
+ const availableWidth = apiRef.current.getRootDimensions()?.viewportInnerSize.width ?? 0;
+ const remainingWidth = availableWidth - totalWidth;
+
+ if (remainingWidth > 0) {
+ const widthPerColumn = remainingWidth / (newColumns.length || 1);
+ newColumns.forEach((column) => {
+ column.width += widthPerColumn;
+ column.computedWidth += widthPerColumn;
+ });
+ }
+ }
+
+ apiRef.current.updateColumns(newColumns);
+ } finally {
+ apiRef.current.unstable_setColumnVirtualization(true);
+ isAutosizingRef.current = false;
+ }
+ },
+ [apiRef, columnVirtualizationDisabled],
+ );
+
+ /**
+ * EFFECTS
+ */
+
+ React.useEffect(() => stopListening, [stopListening]);
+
+ useOnMount(() => {
+ if (props.autosizeOnMount) {
+ Promise.resolve().then(() => {
+ apiRef.current.autosizeColumns(props.autosizeOptions);
+ });
+ }
+ });
useGridNativeEventListener(
apiRef,
@@ -441,9 +683,18 @@ export const useGridColumnResize = (
{ passive: doesSupportTouchActionNone() },
);
- useGridApiEventHandler(apiRef, 'columnSeparatorMouseDown', handleColumnResizeMouseDown);
- useGridApiEventHandler(apiRef, 'columnResizeStart', handleResizeStart);
+ useGridApiMethod(
+ apiRef,
+ {
+ autosizeColumns,
+ },
+ 'public',
+ );
+
useGridApiEventHandler(apiRef, 'columnResizeStop', handleResizeStop);
+ useGridApiEventHandler(apiRef, 'columnResizeStart', handleResizeStart);
+ useGridApiEventHandler(apiRef, 'columnSeparatorMouseDown', handleColumnResizeMouseDown);
+ useGridApiEventHandler(apiRef, 'columnSeparatorDoubleClick', handleColumnSeparatorDoubleClick);
useGridApiOptionHandler(apiRef, 'columnResize', props.onColumnResize);
useGridApiOptionHandler(apiRef, 'columnWidthChange', props.onColumnWidthChange);
diff --git a/packages/grid/x-data-grid-pro/src/models/dataGridProProps.ts b/packages/grid/x-data-grid-pro/src/models/dataGridProProps.ts
index 95301282803bb..d602feb7b44b5 100644
--- a/packages/grid/x-data-grid-pro/src/models/dataGridProProps.ts
+++ b/packages/grid/x-data-grid-pro/src/models/dataGridProProps.ts
@@ -25,6 +25,7 @@ import {
import { GridInitialStatePro } from './gridStatePro';
import { GridProSlotsComponent, UncapitalizedGridProSlotsComponent } from './gridProSlotsComponent';
import type { GridProSlotProps } from './gridProSlotProps';
+import { GridAutosizeOptions } from '../hooks';
export interface GridExperimentalProFeatures extends GridExperimentalFeatures {
/**
@@ -101,6 +102,16 @@ export interface DataGridProPropsWithDefaultValue extends DataGridPropsWithDefau
* @returns {boolean} A boolean indicating if the group is expanded.
*/
isGroupExpandedByDefault?: (node: GridGroupNode) => boolean;
+ /**
+ * If `true`, columns are autosized after the datagrid is mounted.
+ * @default false
+ */
+ autosizeOnMount: boolean;
+ /**
+ * If `true`, column autosizing on header separator double-click is disabled.
+ * @default false
+ */
+ disableAutosize: boolean;
/**
* If `true`, the column pinning is disabled.
* @default false
@@ -157,6 +168,10 @@ export interface DataGridProPropsWithoutDefaultValue;
+ /**
+ * The options for autosize when user-initiated.
+ */
+ autosizeOptions?: GridAutosizeOptions;
/**
* The initial state of the DataGridPro.
* The data in it will be set in the state on initialization but will not be controlled.
diff --git a/packages/grid/x-data-grid-pro/src/models/gridApiPro.ts b/packages/grid/x-data-grid-pro/src/models/gridApiPro.ts
index bd5c6dddeccfc..5b541d730f6b1 100644
--- a/packages/grid/x-data-grid-pro/src/models/gridApiPro.ts
+++ b/packages/grid/x-data-grid-pro/src/models/gridApiPro.ts
@@ -8,6 +8,7 @@ import { GridPrivateOnlyApiCommon } from '@mui/x-data-grid/internals';
import { GridInitialStatePro, GridStatePro } from './gridStatePro';
import type {
GridColumnPinningApi,
+ GridColumnResizeApi,
GridDetailPanelApi,
GridRowPinningApi,
GridDetailPanelPrivateApi,
@@ -20,12 +21,12 @@ export interface GridApiPro
extends GridApiCommon,
GridRowProApi,
GridColumnPinningApi,
+ GridColumnResizeApi,
GridDetailPanelApi,
GridRowPinningApi,
// APIs that are private in Community plan, but public in Pro and Premium plans
GridRowMultiSelectionApi,
- GridColumnReorderApi,
- GridRowProApi {}
+ GridColumnReorderApi {}
export interface GridPrivateApiPro
extends GridApiPro,
diff --git a/packages/grid/x-data-grid-pro/src/models/index.ts b/packages/grid/x-data-grid-pro/src/models/index.ts
index 58cfd0bf2f8eb..36deff5c944b6 100644
--- a/packages/grid/x-data-grid-pro/src/models/index.ts
+++ b/packages/grid/x-data-grid-pro/src/models/index.ts
@@ -1,3 +1,4 @@
+export * from './gridApiPro';
export * from './gridGroupingColDefOverride';
export * from './gridRowScrollEndParams';
export * from './gridRowOrderChangeParams';
diff --git a/packages/grid/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx b/packages/grid/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx
index e56f110f2ff6a..2775ddf2af9ac 100644
--- a/packages/grid/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx
+++ b/packages/grid/x-data-grid-pro/src/tests/columns.DataGridPro.test.tsx
@@ -10,9 +10,10 @@ import {
gridColumnLookupSelector,
gridColumnFieldsSelector,
GridApi,
+ GridAutosizeOptions,
} from '@mui/x-data-grid-pro';
import { useGridPrivateApiContext } from '@mui/x-data-grid-pro/internals';
-import { getColumnHeaderCell, getCell } from 'test/utils/helperFn';
+import { getColumnHeaderCell, getCell, microtasks } from 'test/utils/helperFn';
const isJSDOM = /jsdom/.test(window.navigator.userAgent);
@@ -410,6 +411,91 @@ describe(' - Columns', () => {
});
});
+ describe('autosizing', () => {
+ before(function beforeHook() {
+ if (isJSDOM) {
+ // Need layouting
+ this.skip();
+ }
+ });
+
+ const rows = [
+ {
+ id: 0,
+ brand: 'Nike',
+ },
+ {
+ id: 1,
+ brand: 'Adidas',
+ },
+ {
+ id: 2,
+ brand: 'Puma',
+ },
+ {
+ id: 3,
+ brand: 'Lululemon Athletica',
+ },
+ ];
+ const columns = [
+ { field: 'id', headerName: 'This is the ID column' },
+ { field: 'brand', headerName: 'This is the brand column' },
+ ];
+
+ const getWidths = () => {
+ return columns.map((_, i) => parseInt(getColumnHeaderCell(i).style.width, 10));
+ };
+
+ it('should work through the API', async () => {
+ render();
+ await apiRef.current.autosizeColumns();
+ await microtasks();
+ expect(getWidths()).to.deep.equal([155, 177]);
+ });
+
+ it('should work through double-clicking the separator', async () => {
+ render();
+ const separator = document.querySelectorAll(
+ `.${gridClasses['columnSeparator--resizable']}`,
+ )[1];
+ fireEvent.doubleClick(separator);
+ await microtasks();
+ expect(getWidths()).to.deep.equal([100, 177]);
+ });
+
+ it('should work on mount', async () => {
+ render();
+ await microtasks(); /* first effect after render */
+ await microtasks(); /* async autosize operation */
+ expect(getWidths()).to.deep.equal([155, 177]);
+ });
+
+ describe('options', () => {
+ const autosize = async (options: GridAutosizeOptions | undefined, widths: number[]) => {
+ render();
+ await apiRef.current.autosizeColumns({ includeHeaders: false, ...options });
+ await microtasks();
+ expect(getWidths()).to.deep.equal(widths);
+ };
+
+ it('.columns works', async () => {
+ await autosize({ columns: [columns[0].field] }, [50, 100]);
+ });
+ it('.includeHeaders works', async () => {
+ await autosize({ includeHeaders: true }, [155, 177]);
+ });
+ it('.includeOutliers works', async () => {
+ await autosize({ includeOutliers: true }, [50, 145]);
+ });
+ it('.outliersFactor works', async () => {
+ await autosize({ outliersFactor: 40 }, [50, 145]);
+ });
+ it('.expand works', async () => {
+ await autosize({ expand: true }, [134, 149]);
+ });
+ });
+ });
+
describe('column pipe processing', () => {
type GridPrivateApiContextRef = ReturnType;
it('should not loose column width when re-applying pipe processing', () => {
diff --git a/packages/grid/x-data-grid-pro/src/utils/domUtils.ts b/packages/grid/x-data-grid-pro/src/utils/domUtils.ts
index 697c10999dee0..02e1b5ca563f3 100644
--- a/packages/grid/x-data-grid-pro/src/utils/domUtils.ts
+++ b/packages/grid/x-data-grid-pro/src/utils/domUtils.ts
@@ -56,3 +56,18 @@ export function findGridCellElementsFromCol(col: HTMLElement, api: GridPrivateAp
return cells;
}
+
+export function findGridHeader(api: GridPrivateApiPro, field: string) {
+ const headers = api.columnHeadersContainerElementRef!.current!;
+ return headers.querySelector(`:scope > div > div > [data-field="${field}"][role="columnheader"]`);
+}
+
+export function findGridCells(api: GridPrivateApiPro, field: string) {
+ const container = api.virtualScrollerRef!.current!;
+ const selectorFor = (role: string) =>
+ `:scope > div > div > div > [data-field="${field}"][role="${role}"]`;
+ // TODO(v7): Keep only the selector for the correct role
+ return Array.from(
+ container.querySelectorAll(`${selectorFor('cell')}, ${selectorFor('gridcell')}`),
+ );
+}
diff --git a/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx b/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx
index 2e0db8b05d205..17d5b9e59327a 100644
--- a/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx
+++ b/packages/grid/x-data-grid/src/DataGrid/useDataGridComponent.tsx
@@ -42,12 +42,16 @@ import {
useGridColumnGrouping,
columnGroupsStateInitializer,
} from '../hooks/features/columnGrouping/useGridColumnGrouping';
+import {
+ useGridVirtualization,
+ virtualizationStateInitializer,
+} from '../hooks/features/virtualization';
export const useDataGridComponent = (
inputApiRef: React.MutableRefObject | undefined,
props: DataGridProcessedProps,
) => {
- const privateApiRef = useGridInitialization(
+ const apiRef = useGridInitialization(
inputApiRef,
props,
);
@@ -55,49 +59,51 @@ export const useDataGridComponent = (
/**
* Register all pre-processors called during state initialization here.
*/
- useGridRowSelectionPreProcessors(privateApiRef, props);
- useGridRowsPreProcessors(privateApiRef);
+ useGridRowSelectionPreProcessors(apiRef, props);
+ useGridRowsPreProcessors(apiRef);
/**
* Register all state initializers here.
*/
- useGridInitializeState(rowSelectionStateInitializer, privateApiRef, props);
- useGridInitializeState(columnsStateInitializer, privateApiRef, props);
- useGridInitializeState(rowsStateInitializer, privateApiRef, props);
- useGridInitializeState(editingStateInitializer, privateApiRef, props);
- useGridInitializeState(focusStateInitializer, privateApiRef, props);
- useGridInitializeState(sortingStateInitializer, privateApiRef, props);
- useGridInitializeState(preferencePanelStateInitializer, privateApiRef, props);
- useGridInitializeState(filterStateInitializer, privateApiRef, props);
- useGridInitializeState(densityStateInitializer, privateApiRef, props);
- useGridInitializeState(paginationStateInitializer, privateApiRef, props);
- useGridInitializeState(rowsMetaStateInitializer, privateApiRef, props);
- useGridInitializeState(columnMenuStateInitializer, privateApiRef, props);
- useGridInitializeState(columnGroupsStateInitializer, privateApiRef, props);
+ useGridInitializeState(rowSelectionStateInitializer, apiRef, props);
+ useGridInitializeState(columnsStateInitializer, apiRef, props);
+ useGridInitializeState(rowsStateInitializer, apiRef, props);
+ useGridInitializeState(editingStateInitializer, apiRef, props);
+ useGridInitializeState(focusStateInitializer, apiRef, props);
+ useGridInitializeState(sortingStateInitializer, apiRef, props);
+ useGridInitializeState(preferencePanelStateInitializer, apiRef, props);
+ useGridInitializeState(filterStateInitializer, apiRef, props);
+ useGridInitializeState(densityStateInitializer, apiRef, props);
+ useGridInitializeState(paginationStateInitializer, apiRef, props);
+ useGridInitializeState(rowsMetaStateInitializer, apiRef, props);
+ useGridInitializeState(columnMenuStateInitializer, apiRef, props);
+ useGridInitializeState(columnGroupsStateInitializer, apiRef, props);
+ useGridInitializeState(virtualizationStateInitializer, apiRef, props);
- useGridKeyboardNavigation(privateApiRef, props);
- useGridRowSelection(privateApiRef, props);
- useGridColumns(privateApiRef, props);
- useGridRows(privateApiRef, props);
- useGridParamsApi(privateApiRef, props);
- useGridColumnSpanning(privateApiRef);
- useGridColumnGrouping(privateApiRef, props);
- useGridEditing(privateApiRef, props);
- useGridFocus(privateApiRef, props);
- useGridPreferencesPanel(privateApiRef, props);
- useGridFilter(privateApiRef, props);
- useGridSorting(privateApiRef, props);
- useGridDensity(privateApiRef, props);
- useGridPagination(privateApiRef, props);
- useGridRowsMeta(privateApiRef, props);
- useGridScroll(privateApiRef, props);
- useGridColumnMenu(privateApiRef);
- useGridCsvExport(privateApiRef, props);
- useGridPrintExport(privateApiRef, props);
- useGridClipboard(privateApiRef, props);
- useGridDimensions(privateApiRef, props);
- useGridEvents(privateApiRef, props);
- useGridStatePersistence(privateApiRef);
+ useGridKeyboardNavigation(apiRef, props);
+ useGridRowSelection(apiRef, props);
+ useGridColumns(apiRef, props);
+ useGridRows(apiRef, props);
+ useGridParamsApi(apiRef, props);
+ useGridColumnSpanning(apiRef);
+ useGridColumnGrouping(apiRef, props);
+ useGridEditing(apiRef, props);
+ useGridFocus(apiRef, props);
+ useGridPreferencesPanel(apiRef, props);
+ useGridFilter(apiRef, props);
+ useGridSorting(apiRef, props);
+ useGridDensity(apiRef, props);
+ useGridPagination(apiRef, props);
+ useGridRowsMeta(apiRef, props);
+ useGridScroll(apiRef, props);
+ useGridColumnMenu(apiRef);
+ useGridCsvExport(apiRef, props);
+ useGridPrintExport(apiRef, props);
+ useGridClipboard(apiRef, props);
+ useGridDimensions(apiRef, props);
+ useGridEvents(apiRef, props);
+ useGridStatePersistence(apiRef);
+ useGridVirtualization(apiRef, props);
- return privateApiRef;
+ return apiRef;
};
diff --git a/packages/grid/x-data-grid/src/components/DataGridVirtualScroller.tsx b/packages/grid/x-data-grid/src/components/DataGridVirtualScroller.tsx
index 2589c5423c7e1..e10e75dd2724f 100644
--- a/packages/grid/x-data-grid/src/components/DataGridVirtualScroller.tsx
+++ b/packages/grid/x-data-grid/src/components/DataGridVirtualScroller.tsx
@@ -5,30 +5,26 @@ import { GridVirtualScrollerRenderZone } from './virtualization/GridVirtualScrol
import { useGridVirtualScroller } from '../hooks/features/virtualization/useGridVirtualScroller';
import { GridOverlays } from './base/GridOverlays';
-interface DataGridVirtualScrollerProps extends React.HTMLAttributes {
- disableVirtualization?: boolean;
-}
+const DataGridVirtualScroller = React.forwardRef<
+ HTMLDivElement,
+ React.HTMLAttributes
+>(function DataGridVirtualScroller(props, ref) {
+ const { className, ...other } = props;
-const DataGridVirtualScroller = React.forwardRef(
- function DataGridVirtualScroller(props, ref) {
- const { className, disableVirtualization, ...other } = props;
+ const { getRootProps, getContentProps, getRenderZoneProps, getRows } = useGridVirtualScroller({
+ ref,
+ });
- const { getRootProps, getContentProps, getRenderZoneProps, getRows } = useGridVirtualScroller({
- ref,
- disableVirtualization,
- });
-
- return (
-
-
-
-
- {getRows()}
-
-
-
- );
- },
-);
+ return (
+
+
+
+
+ {getRows()}
+
+
+
+ );
+});
export { DataGridVirtualScroller };
diff --git a/packages/grid/x-data-grid/src/components/base/GridBody.tsx b/packages/grid/x-data-grid/src/components/base/GridBody.tsx
index 320501ca349ed..8c0ea5762311a 100644
--- a/packages/grid/x-data-grid/src/components/base/GridBody.tsx
+++ b/packages/grid/x-data-grid/src/components/base/GridBody.tsx
@@ -32,7 +32,6 @@ interface GridBodyProps {
VirtualScrollerComponent: React.JSXElementConstructor<
React.HTMLAttributes & {
ref: React.Ref;
- disableVirtualization: boolean;
}
>;
}
@@ -76,10 +75,6 @@ function GridBody(props: GridBodyProps) {
cellTabIndexState === null
);
- const [isVirtualizationDisabled, setIsVirtualizationDisabled] = React.useState(
- rootProps.disableVirtualization,
- );
-
useEnhancedEffect(() => {
apiRef.current.computeSizeAndPublishResizeEvent();
@@ -111,27 +106,6 @@ function GridBody(props: GridBodyProps) {
};
}, [apiRef]);
- const disableVirtualization = React.useCallback(() => {
- setIsVirtualizationDisabled(true);
- }, []);
-
- const enableVirtualization = React.useCallback(() => {
- setIsVirtualizationDisabled(false);
- }, []);
-
- React.useEffect(() => {
- setIsVirtualizationDisabled(rootProps.disableVirtualization);
- }, [rootProps.disableVirtualization]);
-
- // The `useGridApiMethod` hook can't be used here, because it only installs the
- // method if it doesn't exist yet. Once installed, it's never updated again.
- // This break the methods above, since their closure comes from the first time
- // they were installed. Which means that calling `setIsVirtualizationDisabled`
- // will trigger a re-render, but it won't update the state. That can be solved
- // by migrating the virtualization status to the global state.
- apiRef.current.unstable_disableVirtualization = disableVirtualization;
- apiRef.current.unstable_enableVirtualization = enableVirtualization;
-
const columnHeadersRef = React.useRef(null);
const columnsContainerRef = React.useRef(null);
const virtualScrollerRef = React.useRef(null);
@@ -174,7 +148,6 @@ function GridBody(props: GridBodyProps) {
// If this event is published while dimensions haven't been computed,
// the `onFetchRows` prop won't be called during mount.
ref={virtualScrollerRef}
- disableVirtualization={isVirtualizationDisabled}
/>
)}
diff --git a/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx b/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx
index ba7777efbed80..78592957c681d 100644
--- a/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx
+++ b/packages/grid/x-data-grid/src/components/columnHeaders/GridColumnHeaderItem.tsx
@@ -152,7 +152,10 @@ function GridColumnHeaderItem(props: GridColumnHeaderItemProps) {
);
const columnHeaderSeparatorProps = React.useMemo(
- () => ({ onMouseDown: publish('columnSeparatorMouseDown') }),
+ () => ({
+ onMouseDown: publish('columnSeparatorMouseDown'),
+ onDoubleClick: publish('columnSeparatorDoubleClick'),
+ }),
[publish],
);
diff --git a/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx b/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx
index e1f9470b15fe1..f10009980f68b 100644
--- a/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx
+++ b/packages/grid/x-data-grid/src/hooks/features/columnHeaders/useGridColumnHeaders.tsx
@@ -3,6 +3,7 @@ import * as ReactDOM from 'react-dom';
import { unstable_useForkRef as useForkRef } from '@mui/utils';
import { styled, useTheme } from '@mui/system';
import { defaultMemoize } from 'reselect';
+import { useGridSelector } from '../../utils';
import { useGridPrivateApiContext } from '../../utils/useGridPrivateApiContext';
import { useGridRootProps } from '../../utils/useGridRootProps';
import { GridRenderContext } from '../../../models/params/gridScrollParams';
@@ -15,6 +16,7 @@ import {
areRenderContextsEqual,
getRenderableIndexes,
} from '../virtualization/useGridVirtualScroller';
+import { gridVirtualizationColumnEnabledSelector } from '../virtualization';
import { GridColumnGroupHeader } from '../../../components/columnHeaders/GridColumnGroupHeader';
import { GridColumnGroup } from '../../../models/gridColumnGrouping';
import { GridStateColDef } from '../../../models/colDef/gridColDef';
@@ -100,6 +102,7 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
const [resizeCol, setResizeCol] = React.useState('');
const apiRef = useGridPrivateApiContext();
+ const hasVirtualization = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector);
const rootProps = useGridRootProps();
const innerRef = React.useRef(null);
@@ -243,7 +246,6 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
(params) => setDragCol(params.field),
[],
);
-
const handleColumnReorderStop = React.useCallback>(
() => setDragCol(''),
[],
@@ -276,20 +278,21 @@ export const useGridColumnHeaders = (props: UseGridColumnHeadersProps) => {
buffer: rootProps.rowBuffer,
});
- const firstColumnToRender = getFirstColumnIndexToRenderRef.current({
- firstColumnIndex: nextRenderContext!.firstColumnIndex,
- minColumnIndex: minFirstColumn,
- columnBuffer: rootProps.columnBuffer,
- apiRef,
- firstRowToRender,
- lastRowToRender,
- visibleRows: currentPage.rows,
- });
-
- const lastColumnToRender = Math.min(
- nextRenderContext.lastColumnIndex! + rootProps.columnBuffer,
- maxLastColumn,
- );
+ const firstColumnToRender = !hasVirtualization
+ ? 0
+ : getFirstColumnIndexToRenderRef.current({
+ firstColumnIndex: nextRenderContext!.firstColumnIndex,
+ minColumnIndex: minFirstColumn,
+ columnBuffer: rootProps.columnBuffer,
+ apiRef,
+ firstRowToRender,
+ lastRowToRender,
+ visibleRows: currentPage.rows,
+ });
+
+ const lastColumnToRender = !hasVirtualization
+ ? maxLastColumn
+ : Math.min(nextRenderContext.lastColumnIndex! + rootProps.columnBuffer, maxLastColumn);
const renderedColumns = visibleColumns.slice(firstColumnToRender, lastColumnToRender);
diff --git a/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx b/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx
index 87d108f92a674..b143d906338b3 100644
--- a/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx
+++ b/packages/grid/x-data-grid/src/hooks/features/export/useGridPrintExport.tsx
@@ -281,7 +281,7 @@ export const useGridPrintExport = (
apiRef.current.setColumnVisibilityModel(previousColumnVisibility.current);
}
- apiRef.current.unstable_enableVirtualization();
+ apiRef.current.unstable_setVirtualization(true);
apiRef.current.setRows(previousRows.current);
// Clear local state
@@ -329,7 +329,7 @@ export const useGridPrintExport = (
updateGridRowsForPrint(options.getRowsToExport);
}
- apiRef.current.unstable_disableVirtualization();
+ apiRef.current.unstable_setVirtualization(false);
await raf(); // wait for the state changes to take action
const printWindow = buildPrintWindow(options?.fileName);
if (process.env.NODE_ENV === 'test') {
diff --git a/packages/grid/x-data-grid/src/hooks/features/index.ts b/packages/grid/x-data-grid/src/hooks/features/index.ts
index dc565a3269ebb..b5d62c7b80d5b 100644
--- a/packages/grid/x-data-grid/src/hooks/features/index.ts
+++ b/packages/grid/x-data-grid/src/hooks/features/index.ts
@@ -13,3 +13,4 @@ export * from './sorting';
export * from './dimensions';
export * from './statePersistence';
export * from './headerFiltering';
+export * from './virtualization';
diff --git a/packages/grid/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts b/packages/grid/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts
new file mode 100644
index 0000000000000..4fed29c696b7f
--- /dev/null
+++ b/packages/grid/x-data-grid/src/hooks/features/virtualization/gridVirtualizationSelectors.ts
@@ -0,0 +1,26 @@
+import { createSelector } from '../../../utils/createSelector';
+import { GridStateCommunity } from '../../../models/gridStateCommunity';
+
+/**
+ * Get the columns state
+ * @category Virtualization
+ */
+export const gridVirtualizationSelector = (state: GridStateCommunity) => state.virtualization;
+
+/**
+ * Get the enabled state for virtualization
+ * @category Virtualization
+ */
+export const gridVirtualizationEnabledSelector = createSelector(
+ gridVirtualizationSelector,
+ (state) => state.enabled,
+);
+
+/**
+ * Get the enabled state for virtualization
+ * @category Virtualization
+ */
+export const gridVirtualizationColumnEnabledSelector = createSelector(
+ gridVirtualizationSelector,
+ (state) => state.enabledForColumns,
+);
diff --git a/packages/grid/x-data-grid/src/hooks/features/virtualization/index.ts b/packages/grid/x-data-grid/src/hooks/features/virtualization/index.ts
new file mode 100644
index 0000000000000..19098c42dfda5
--- /dev/null
+++ b/packages/grid/x-data-grid/src/hooks/features/virtualization/index.ts
@@ -0,0 +1,2 @@
+export * from './useGridVirtualization';
+export * from './gridVirtualizationSelectors';
diff --git a/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx b/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx
index dee0de795da83..145d7e2e64a4e 100644
--- a/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx
+++ b/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualScroller.tsx
@@ -28,6 +28,10 @@ import { GridStateColDef } from '../../../models/colDef/gridColDef';
import { getFirstNonSpannedColumnToRender } from '../columns/gridColumnsUtils';
import { getMinimalContentHeight } from '../rows/gridRowsUtils';
import { GridRowProps } from '../../../components/GridRow';
+import {
+ gridVirtualizationEnabledSelector,
+ gridVirtualizationColumnEnabledSelector,
+} from './gridVirtualizationSelectors';
// Uses binary search to avoid looping through all possible positions
export function binarySearch(
@@ -98,7 +102,6 @@ export const areRenderContextsEqual = (
interface UseGridVirtualScrollerProps {
ref: React.Ref;
- disableVirtualization?: boolean;
renderZoneMinColumnIndex?: number;
renderZoneMaxColumnIndex?: number;
onRenderZonePositioning?: (params: { top: number; left: number }) => void;
@@ -118,10 +121,11 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
const apiRef = useGridPrivateApiContext();
const rootProps = useGridRootProps();
const visibleColumns = useGridSelector(apiRef, gridVisibleColumnDefinitionsSelector);
+ const enabled = useGridSelector(apiRef, gridVirtualizationEnabledSelector);
+ const enabledForColumns = useGridSelector(apiRef, gridVirtualizationColumnEnabledSelector);
const {
ref,
- disableVirtualization,
onRenderZonePositioning,
renderZoneMinColumnIndex = 0,
renderZoneMaxColumnIndex = visibleColumns.length,
@@ -139,7 +143,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
const renderZoneRef = React.useRef(null);
const rootRef = React.useRef(null);
const handleRef = useForkRef(ref, rootRef);
- const [renderContext, setRenderContext] = React.useState(null);
+ const [renderContext, setRenderContextState] = React.useState(null);
const prevRenderContext = React.useRef(renderContext);
const scrollPosition = React.useRef({ top: 0, left: 0 });
const [containerDimensions, setContainerDimensions] = React.useState({
@@ -232,7 +236,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
);
const computeRenderContext = React.useCallback(() => {
- if (disableVirtualization) {
+ if (!enabled) {
return {
firstRowIndex: 0,
lastRowIndex: currentPage.rows.length,
@@ -251,26 +255,32 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
? firstRowIndex + currentPage.rows.length
: getNearestIndexToRender(top + containerDimensions.height!);
- let hasRowWithAutoHeight = false;
let firstColumnIndex = 0;
let lastColumnIndex = columnPositions.length;
- const [firstRowToRender, lastRowToRender] = getRenderableIndexes({
- firstIndex: firstRowIndex,
- lastIndex: lastRowIndex,
- minFirstIndex: 0,
- maxLastIndex: currentPage.rows.length,
- buffer: rootProps.rowBuffer,
- });
+ if (enabledForColumns) {
+ let hasRowWithAutoHeight = false;
- for (let i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) {
- const row = currentPage.rows[i];
- hasRowWithAutoHeight = apiRef.current.rowHasAutoHeight(row.id);
- }
+ const [firstRowToRender, lastRowToRender] = getRenderableIndexes({
+ firstIndex: firstRowIndex,
+ lastIndex: lastRowIndex,
+ minFirstIndex: 0,
+ maxLastIndex: currentPage.rows.length,
+ buffer: rootProps.rowBuffer,
+ });
- if (!hasRowWithAutoHeight) {
- firstColumnIndex = binarySearch(Math.abs(left), columnPositions);
- lastColumnIndex = binarySearch(Math.abs(left) + containerDimensions.width!, columnPositions);
+ for (let i = firstRowToRender; i < lastRowToRender && !hasRowWithAutoHeight; i += 1) {
+ const row = currentPage.rows[i];
+ hasRowWithAutoHeight = apiRef.current.rowHasAutoHeight(row.id);
+ }
+
+ if (!hasRowWithAutoHeight) {
+ firstColumnIndex = binarySearch(Math.abs(left), columnPositions);
+ lastColumnIndex = binarySearch(
+ Math.abs(left) + containerDimensions.width!,
+ columnPositions,
+ );
+ }
}
return {
@@ -280,7 +290,8 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
lastColumnIndex,
};
}, [
- disableVirtualization,
+ enabled,
+ enabledForColumns,
getNearestIndexToRender,
rowsMeta.positions.length,
rootProps.autoHeight,
@@ -293,14 +304,14 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
]);
useEnhancedEffect(() => {
- if (disableVirtualization) {
- renderZoneRef.current!.style.transform = `translate3d(0px, 0px, 0px)`;
- } else {
+ if (enabled) {
// TODO a scroll reset should not be necessary
rootRef.current!.scrollLeft = 0;
rootRef.current!.scrollTop = 0;
+ } else {
+ renderZoneRef.current!.style.transform = `translate3d(0px, 0px, 0px)`;
}
- }, [disableVirtualization]);
+ }, [enabled]);
useEnhancedEffect(() => {
setContainerDimensions({
@@ -367,7 +378,9 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
],
);
- const updateRenderContext = React.useCallback(
+ const getRenderContext = React.useCallback(() => prevRenderContext.current!, []);
+
+ const setRenderContext = React.useCallback(
(nextRenderContext: GridRenderContext) => {
if (
prevRenderContext.current &&
@@ -376,7 +389,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
updateRenderZonePosition(nextRenderContext);
return;
}
- setRenderContext(nextRenderContext);
+ setRenderContextState(nextRenderContext);
updateRenderZonePosition(nextRenderContext);
@@ -397,7 +410,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
},
[
apiRef,
- setRenderContext,
+ setRenderContextState,
prevRenderContext,
currentPage.rows.length,
rootProps.rowBuffer,
@@ -411,12 +424,12 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
}
const initialRenderContext = computeRenderContext();
- updateRenderContext(initialRenderContext);
+ setRenderContext(initialRenderContext);
const { top, left } = scrollPosition.current!;
const params = { top, left, renderContext: initialRenderContext };
apiRef.current.publishEvent('scrollPositionChange', params);
- }, [apiRef, computeRenderContext, containerDimensions.width, updateRenderContext]);
+ }, [apiRef, computeRenderContext, containerDimensions.width, setRenderContext]);
const handleScroll = useEventCallback((event: React.UIEvent) => {
const { scrollTop, scrollLeft } = event.currentTarget;
@@ -439,9 +452,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
}
// When virtualization is disabled, the context never changes during scroll
- const nextRenderContext = disableVirtualization
- ? prevRenderContext.current
- : computeRenderContext();
+ const nextRenderContext = enabled ? computeRenderContext() : prevRenderContext.current;
const topRowsScrolledSincePreviousRender = Math.abs(
nextRenderContext.firstRowIndex - prevRenderContext.current.firstRowIndex,
@@ -477,7 +488,7 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
if (shouldSetState) {
// Prevents batching render context changes
ReactDOM.flushSync(() => {
- updateRenderContext(nextRenderContext);
+ setRenderContext(nextRenderContext);
});
prevTotalWidth.current = columnsTotalWidth;
}
@@ -524,8 +535,8 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
return null;
}
- const rowBuffer = !disableVirtualization ? rootProps.rowBuffer : 0;
- const columnBuffer = !disableVirtualization ? rootProps.columnBuffer : 0;
+ const rowBuffer = enabled ? rootProps.rowBuffer : 0;
+ const columnBuffer = enabled ? rootProps.columnBuffer : 0;
const [firstRowToRender, lastRowToRender] = getRenderableIndexes({
firstIndex: nextRenderContext.firstRowIndex,
@@ -769,11 +780,9 @@ export const useGridVirtualScroller = (props: UseGridVirtualScrollerProps) => {
return style;
}, [needsHorizontalScrollbar, rootProps.autoHeight]);
- const getRenderContext = React.useCallback((): GridRenderContext => {
- return prevRenderContext.current!;
- }, []);
-
- apiRef.current.register('private', { getRenderContext });
+ apiRef.current.register('private', {
+ getRenderContext,
+ });
return {
renderContext,
diff --git a/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualization.tsx b/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualization.tsx
new file mode 100644
index 0000000000000..7634600ac1da8
--- /dev/null
+++ b/packages/grid/x-data-grid/src/hooks/features/virtualization/useGridVirtualization.tsx
@@ -0,0 +1,70 @@
+import * as React from 'react';
+import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity';
+import { DataGridProcessedProps } from '../../../models/props/DataGridProps';
+import { useGridApiMethod } from '../../utils/useGridApiMethod';
+import { GridStateInitializer } from '../../utils/useGridInitializeState';
+
+type RootProps = Pick;
+
+export type GridVirtualizationState = {
+ enabled: boolean;
+ enabledForColumns: boolean;
+};
+
+export const virtualizationStateInitializer: GridStateInitializer = (state, props) => {
+ const virtualization = {
+ enabled: !props.disableVirtualization,
+ enabledForColumns: true,
+ };
+
+ return {
+ ...state,
+ virtualization,
+ };
+};
+
+export function useGridVirtualization(
+ apiRef: React.MutableRefObject,
+ props: RootProps,
+): void {
+ /*
+ * API METHODS
+ */
+
+ const setVirtualization = (enabled: boolean) => {
+ apiRef.current.setState((state) => ({
+ ...state,
+ virtualization: {
+ ...state.virtualization,
+ enabled,
+ },
+ }));
+ };
+
+ const setColumnVirtualization = (enabled: boolean) => {
+ apiRef.current.setState((state) => ({
+ ...state,
+ virtualization: {
+ ...state.virtualization,
+ enabledForColumns: enabled,
+ },
+ }));
+ };
+
+ const api = {
+ unstable_setVirtualization: setVirtualization,
+ unstable_setColumnVirtualization: setColumnVirtualization,
+ };
+
+ useGridApiMethod(apiRef, api, 'public');
+
+ /*
+ * EFFECTS
+ */
+
+ /* eslint-disable react-hooks/exhaustive-deps */
+ React.useEffect(() => {
+ setVirtualization(!props.disableVirtualization);
+ }, [props.disableVirtualization]);
+ /* eslint-enable react-hooks/exhaustive-deps */
+}
diff --git a/packages/grid/x-data-grid/src/internals/index.ts b/packages/grid/x-data-grid/src/internals/index.ts
index 40a9d112efdad..282be9d713862 100644
--- a/packages/grid/x-data-grid/src/internals/index.ts
+++ b/packages/grid/x-data-grid/src/internals/index.ts
@@ -39,6 +39,7 @@ export {
export { useGridColumns, columnsStateInitializer } from '../hooks/features/columns/useGridColumns';
export { getTotalHeaderHeight } from '../hooks/features/columns/gridColumnsUtils';
export { useGridColumnSpanning } from '../hooks/features/columns/useGridColumnSpanning';
+export { gridColumnsStateSelector } from '../hooks/features/columns/gridColumnsSelector';
export {
useGridColumnGrouping,
columnGroupsStateInitializer,
@@ -113,6 +114,7 @@ export {
useGridVirtualScroller,
getRenderableIndexes,
} from '../hooks/features/virtualization/useGridVirtualScroller';
+export * from '../hooks/features/virtualization';
export { useTimeout } from '../hooks/utils/useTimeout';
export { useGridVisibleRows, getVisibleRows } from '../hooks/utils/useGridVisibleRows';
@@ -128,6 +130,7 @@ export type {
} from '../models/props/DataGridProps';
export { getColumnsToExport, defaultGetRowsToExport } from '../hooks/features/export/utils';
+export * from '../utils/createControllablePromise';
export {
createSelector,
createSelectorMemoized,
@@ -140,6 +143,7 @@ export { buildWarning } from '../utils/warning';
export { exportAs } from '../utils/exportAs';
export type { GridPrivateOnlyApiCommon } from '../models/api/gridApiCommon';
export { useGridPrivateApiContext } from '../hooks/utils/useGridPrivateApiContext';
+export * from '../hooks/utils/useOnMount';
export type { GridApiCommunity } from '../models/api/gridApiCommunity';
export type { GridApiCaches } from '../models/gridApiCaches';
diff --git a/packages/grid/x-data-grid/src/models/api/gridApiCommon.ts b/packages/grid/x-data-grid/src/models/api/gridApiCommon.ts
index 17c98ff59b273..79745cb26e03f 100644
--- a/packages/grid/x-data-grid/src/models/api/gridApiCommon.ts
+++ b/packages/grid/x-data-grid/src/models/api/gridApiCommon.ts
@@ -10,7 +10,6 @@ import { GridLocaleTextApi } from './gridLocaleTextApi';
import type { GridParamsApi } from './gridParamsApi';
import { GridPreferencesPanelApi } from './gridPreferencesPanelApi';
import { GridPrintExportApi } from './gridPrintExportApi';
-import { GridDisableVirtualizationApi } from './gridDisableVirtualizationApi';
import { GridRowApi } from './gridRowApi';
import { GridRowsMetaApi, GridRowsMetaPrivateApi } from './gridRowsMetaApi';
import { GridRowSelectionApi } from './gridRowSelectionApi';
@@ -18,7 +17,7 @@ import { GridSortApi } from './gridSortApi';
import { GridStateApi, GridStatePrivateApi } from './gridStateApi';
import { GridLoggerApi } from './gridLoggerApi';
import { GridScrollApi } from './gridScrollApi';
-import { GridVirtualScrollerApi } from './gridVirtualScrollerApi';
+import { GridVirtualizationApi, GridVirtualizationPrivateApi } from './gridVirtualizationApi';
import type {
GridPipeProcessingApi,
GridPipeProcessingPrivateApi,
@@ -56,7 +55,7 @@ export interface GridApiCommon<
GridColumnMenuApi,
GridPreferencesPanelApi,
GridPrintExportApi,
- GridDisableVirtualizationApi,
+ GridVirtualizationApi,
GridLocaleTextApi,
GridScrollApi,
GridColumnSpanningApi,
@@ -75,11 +74,11 @@ export interface GridPrivateOnlyApiCommon<
GridColumnSpanningPrivateApi,
GridRowsMetaPrivateApi,
GridDimensionsPrivateApi,
- GridVirtualScrollerApi,
GridEditingPrivateApi,
GridLoggerApi,
GridFocusPrivateApi,
- GridHeaderFilteringPrivateApi {}
+ GridHeaderFilteringPrivateApi,
+ GridVirtualizationPrivateApi {}
export interface GridPrivateApiCommon
extends GridApiCommon,
diff --git a/packages/grid/x-data-grid/src/models/api/gridDisableVirtualizationApi.ts b/packages/grid/x-data-grid/src/models/api/gridDisableVirtualizationApi.ts
deleted file mode 100644
index 1c31999395d87..0000000000000
--- a/packages/grid/x-data-grid/src/models/api/gridDisableVirtualizationApi.ts
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
- * The API to disable the virtualization that is available in the grid [[apiRef]].
- */
-export interface GridDisableVirtualizationApi {
- /**
- * Disables grid's virtualization.
- * @ignore - do not document. Remove before releasing v5 stable version.
- */
- unstable_disableVirtualization: () => void;
- /**
- * Enables grid's virtualization.
- * @ignore - do not document. Remove before releasing v5 stable version.
- */
- unstable_enableVirtualization: () => void;
-}
diff --git a/packages/grid/x-data-grid/src/models/api/gridVirtualScrollerApi.ts b/packages/grid/x-data-grid/src/models/api/gridVirtualScrollerApi.ts
deleted file mode 100644
index a91668ed1b3cb..0000000000000
--- a/packages/grid/x-data-grid/src/models/api/gridVirtualScrollerApi.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { GridRenderContext } from '../params/gridScrollParams';
-
-export interface GridVirtualScrollerApi {
- /**
- * Get the current grid rendering context.
- * @returns {GridRenderContext} The `GridRenderContext`.
- */
- getRenderContext: () => GridRenderContext;
-}
diff --git a/packages/grid/x-data-grid/src/models/api/gridVirtualizationApi.ts b/packages/grid/x-data-grid/src/models/api/gridVirtualizationApi.ts
new file mode 100644
index 0000000000000..6a5d787e72d39
--- /dev/null
+++ b/packages/grid/x-data-grid/src/models/api/gridVirtualizationApi.ts
@@ -0,0 +1,22 @@
+import { GridRenderContext } from '../params';
+
+export interface GridVirtualizationApi {
+ /**
+ * Enable/disable virtualization.
+ * @param {boolean} enabled The enabled value for virtualization
+ */
+ unstable_setVirtualization: (enabled: boolean) => void;
+ /**
+ * Enable/disable column virtualization.
+ * @param {boolean} enabled The enabled value for column virtualization
+ */
+ unstable_setColumnVirtualization: (enabled: boolean) => void;
+}
+
+export interface GridVirtualizationPrivateApi {
+ /**
+ * Get the current grid rendering context.
+ * @returns {GridRenderContext} The `GridRenderContext`.
+ */
+ getRenderContext: () => GridRenderContext;
+}
diff --git a/packages/grid/x-data-grid/src/models/api/index.ts b/packages/grid/x-data-grid/src/models/api/index.ts
index f84f56ee69a17..709885eea44ad 100644
--- a/packages/grid/x-data-grid/src/models/api/index.ts
+++ b/packages/grid/x-data-grid/src/models/api/index.ts
@@ -16,10 +16,9 @@ export * from './gridFilterApi';
export * from './gridColumnMenuApi';
export * from './gridPreferencesPanelApi';
export * from './gridPrintExportApi';
-export * from './gridDisableVirtualizationApi';
export * from './gridCallbackDetails';
export * from './gridScrollApi';
-export * from './gridVirtualScrollerApi';
+export * from './gridVirtualizationApi';
export type { GridApiCommon } from './gridApiCommon';
export type { GridEditingApi, GridCellModesModel, GridRowModesModel } from './gridEditingApi';
diff --git a/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts b/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts
index 294cba7f29c59..919ca4847bcfc 100644
--- a/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts
+++ b/packages/grid/x-data-grid/src/models/events/gridEventLookup.ts
@@ -175,6 +175,14 @@ export interface GridColumnHeaderEventLookup {
params: GridColumnHeaderParams;
event: React.DragEvent;
};
+ /**
+ * Fired when a `dblclick` DOM event happens in the column header separator.
+ * @ignore - do not document.
+ */
+ columnSeparatorDoubleClick: {
+ params: GridColumnHeaderParams;
+ event: React.MouseEvent;
+ };
/**
* Fired when a `mousedown` DOM event happens in the column header separator.
* @ignore - do not document.
diff --git a/packages/grid/x-data-grid/src/models/gridStateCommunity.ts b/packages/grid/x-data-grid/src/models/gridStateCommunity.ts
index ac5bf733807a1..a0ee71bdf0c2b 100644
--- a/packages/grid/x-data-grid/src/models/gridStateCommunity.ts
+++ b/packages/grid/x-data-grid/src/models/gridStateCommunity.ts
@@ -15,6 +15,7 @@ import type {
GridSortingInitialState,
GridSortingState,
GridTabIndexState,
+ GridVirtualizationState,
} from '../hooks';
import type { GridRowsMetaState } from '../hooks/features/rows/gridRowsMetaState';
import type { GridEditingState } from './gridEditRowModel';
@@ -42,6 +43,7 @@ export interface GridStateCommunity {
filter: GridFilterState;
preferencePanel: GridPreferencePanelState;
density: GridDensityState;
+ virtualization: GridVirtualizationState;
}
/**
diff --git a/packages/grid/x-data-grid/src/utils/createControllablePromise.ts b/packages/grid/x-data-grid/src/utils/createControllablePromise.ts
new file mode 100644
index 0000000000000..4582b40872fec
--- /dev/null
+++ b/packages/grid/x-data-grid/src/utils/createControllablePromise.ts
@@ -0,0 +1,16 @@
+export type ControllablePromise = Promise & {
+ resolve: T extends unknown ? (value?: T) => void : (value: T) => void;
+ reject: (reason?: any) => void;
+};
+
+export function createControllablePromise() {
+ let resolve: ControllablePromise['resolve'];
+ let reject: ControllablePromise['reject'];
+ const promise = new Promise((_resolve, _reject) => {
+ resolve = _resolve;
+ reject = _reject;
+ }) as ControllablePromise;
+ promise.resolve = resolve!;
+ promise.reject = reject!;
+ return promise;
+}
diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json
index 577959c886326..0846622cb10f0 100644
--- a/scripts/x-data-grid-premium.exports.json
+++ b/scripts/x-data-grid-premium.exports.json
@@ -31,6 +31,7 @@
{ "name": "DataGridPremiumProps", "kind": "Interface" },
{ "name": "DataGridPro", "kind": "Function" },
{ "name": "deDE", "kind": "Variable" },
+ { "name": "DEFAULT_GRID_AUTOSIZE_OPTIONS", "kind": "Variable" },
{ "name": "DEFAULT_GRID_COL_TYPE_KEY", "kind": "Variable" },
{ "name": "ElementSize", "kind": "Interface" },
{ "name": "elGR", "kind": "Variable" },
@@ -107,10 +108,12 @@
{ "name": "GridApi", "kind": "TypeAlias" },
{ "name": "GridApiCommon", "kind": "Interface" },
{ "name": "GridApiContext", "kind": "Variable" },
+ { "name": "GridApiPro", "kind": "Interface" },
{ "name": "GridArrowDownwardIcon", "kind": "Variable" },
{ "name": "GridArrowUpwardIcon", "kind": "Variable" },
{ "name": "GridAutoGeneratedGroupNode", "kind": "Interface" },
{ "name": "GridAutoGeneratedPinnedRowNode", "kind": "Interface" },
+ { "name": "GridAutosizeOptions", "kind": "TypeAlias" },
{ "name": "GridBasicGroupNode", "kind": "Interface" },
{ "name": "GridBody", "kind": "Function" },
{ "name": "GridBooleanCell", "kind": "Variable" },
@@ -209,6 +212,7 @@
{ "name": "gridColumnReorderDragColSelector", "kind": "Variable" },
{ "name": "gridColumnReorderSelector", "kind": "Variable" },
{ "name": "GridColumnReorderState", "kind": "Interface" },
+ { "name": "GridColumnResizeApi", "kind": "Interface" },
{ "name": "GridColumnResizeParams", "kind": "Interface" },
{ "name": "gridColumnResizeSelector", "kind": "Variable" },
{ "name": "GridColumnResizeState", "kind": "Interface" },
@@ -257,7 +261,6 @@
{ "name": "GridDetailPanelToggleCell", "kind": "Function" },
{ "name": "GridDimensions", "kind": "Interface" },
{ "name": "GridDimensionsApi", "kind": "Interface" },
- { "name": "GridDisableVirtualizationApi", "kind": "Interface" },
{ "name": "GridDragIcon", "kind": "Variable" },
{ "name": "GridEditBooleanCell", "kind": "Function" },
{ "name": "GridEditBooleanCellProps", "kind": "Interface" },
@@ -430,6 +433,7 @@
{ "name": "GridPrintExportMenuItemProps", "kind": "TypeAlias" },
{ "name": "GridPrintExportOptions", "kind": "Interface" },
{ "name": "GridPrintGetRowsToExportParams", "kind": "Interface" },
+ { "name": "GridPrivateApiPro", "kind": "Interface" },
{ "name": "GridProIconSlotsComponent", "kind": "Interface" },
{ "name": "GridProSlotsComponent", "kind": "Interface" },
{ "name": "GridPushPinLeftIcon", "kind": "Variable" },
@@ -571,7 +575,12 @@
{ "name": "GridViewColumnIcon", "kind": "Variable" },
{ "name": "GridViewHeadlineIcon", "kind": "Variable" },
{ "name": "GridViewStreamIcon", "kind": "Variable" },
- { "name": "GridVirtualScrollerApi", "kind": "Interface" },
+ { "name": "GridVirtualizationApi", "kind": "Interface" },
+ { "name": "gridVirtualizationColumnEnabledSelector", "kind": "Variable" },
+ { "name": "gridVirtualizationEnabledSelector", "kind": "Variable" },
+ { "name": "GridVirtualizationPrivateApi", "kind": "Interface" },
+ { "name": "gridVirtualizationSelector", "kind": "Variable" },
+ { "name": "GridVirtualizationState", "kind": "TypeAlias" },
{ "name": "GridVisibilityOffIcon", "kind": "Variable" },
{ "name": "gridVisibleColumnDefinitionsSelector", "kind": "Variable" },
{ "name": "gridVisibleColumnFieldsSelector", "kind": "Variable" },
@@ -641,8 +650,10 @@
{ "name": "useGridNativeEventListener", "kind": "Variable" },
{ "name": "useGridRootProps", "kind": "Variable" },
{ "name": "useGridSelector", "kind": "Variable" },
+ { "name": "useGridVirtualization", "kind": "Function" },
{ "name": "useKeepGroupedColumnsHidden", "kind": "Variable" },
{ "name": "ValueOptions", "kind": "TypeAlias" },
+ { "name": "virtualizationStateInitializer", "kind": "Variable" },
{ "name": "viVN", "kind": "Variable" },
{ "name": "zhCN", "kind": "Variable" },
{ "name": "zhTW", "kind": "Variable" }
diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json
index 819d55f8450c1..6457182e64df7 100644
--- a/scripts/x-data-grid-pro.exports.json
+++ b/scripts/x-data-grid-pro.exports.json
@@ -30,6 +30,7 @@
{ "name": "DataGridPro", "kind": "Variable" },
{ "name": "DataGridProProps", "kind": "Interface" },
{ "name": "deDE", "kind": "Variable" },
+ { "name": "DEFAULT_GRID_AUTOSIZE_OPTIONS", "kind": "Variable" },
{ "name": "DEFAULT_GRID_COL_TYPE_KEY", "kind": "Variable" },
{ "name": "ElementSize", "kind": "Interface" },
{ "name": "elGR", "kind": "Variable" },
@@ -85,10 +86,12 @@
{ "name": "GridApi", "kind": "TypeAlias" },
{ "name": "GridApiCommon", "kind": "Interface" },
{ "name": "GridApiContext", "kind": "Variable" },
+ { "name": "GridApiPro", "kind": "Interface" },
{ "name": "GridArrowDownwardIcon", "kind": "Variable" },
{ "name": "GridArrowUpwardIcon", "kind": "Variable" },
{ "name": "GridAutoGeneratedGroupNode", "kind": "Interface" },
{ "name": "GridAutoGeneratedPinnedRowNode", "kind": "Interface" },
+ { "name": "GridAutosizeOptions", "kind": "TypeAlias" },
{ "name": "GridBasicGroupNode", "kind": "Interface" },
{ "name": "GridBody", "kind": "Function" },
{ "name": "GridBooleanCell", "kind": "Variable" },
@@ -183,6 +186,7 @@
{ "name": "gridColumnReorderDragColSelector", "kind": "Variable" },
{ "name": "gridColumnReorderSelector", "kind": "Variable" },
{ "name": "GridColumnReorderState", "kind": "Interface" },
+ { "name": "GridColumnResizeApi", "kind": "Interface" },
{ "name": "GridColumnResizeParams", "kind": "Interface" },
{ "name": "gridColumnResizeSelector", "kind": "Variable" },
{ "name": "GridColumnResizeState", "kind": "Interface" },
@@ -231,7 +235,6 @@
{ "name": "GridDetailPanelToggleCell", "kind": "Function" },
{ "name": "GridDimensions", "kind": "Interface" },
{ "name": "GridDimensionsApi", "kind": "Interface" },
- { "name": "GridDisableVirtualizationApi", "kind": "Interface" },
{ "name": "GridDragIcon", "kind": "Variable" },
{ "name": "GridEditBooleanCell", "kind": "Function" },
{ "name": "GridEditBooleanCellProps", "kind": "Interface" },
@@ -392,6 +395,7 @@
{ "name": "GridPrintExportMenuItemProps", "kind": "TypeAlias" },
{ "name": "GridPrintExportOptions", "kind": "Interface" },
{ "name": "GridPrintGetRowsToExportParams", "kind": "Interface" },
+ { "name": "GridPrivateApiPro", "kind": "Interface" },
{ "name": "GridProIconSlotsComponent", "kind": "Interface" },
{ "name": "GridProSlotsComponent", "kind": "Interface" },
{ "name": "GridPushPinLeftIcon", "kind": "Variable" },
@@ -526,7 +530,12 @@
{ "name": "GridViewColumnIcon", "kind": "Variable" },
{ "name": "GridViewHeadlineIcon", "kind": "Variable" },
{ "name": "GridViewStreamIcon", "kind": "Variable" },
- { "name": "GridVirtualScrollerApi", "kind": "Interface" },
+ { "name": "GridVirtualizationApi", "kind": "Interface" },
+ { "name": "gridVirtualizationColumnEnabledSelector", "kind": "Variable" },
+ { "name": "gridVirtualizationEnabledSelector", "kind": "Variable" },
+ { "name": "GridVirtualizationPrivateApi", "kind": "Interface" },
+ { "name": "gridVirtualizationSelector", "kind": "Variable" },
+ { "name": "GridVirtualizationState", "kind": "TypeAlias" },
{ "name": "GridVisibilityOffIcon", "kind": "Variable" },
{ "name": "gridVisibleColumnDefinitionsSelector", "kind": "Variable" },
{ "name": "gridVisibleColumnFieldsSelector", "kind": "Variable" },
@@ -592,7 +601,9 @@
{ "name": "useGridNativeEventListener", "kind": "Variable" },
{ "name": "useGridRootProps", "kind": "Variable" },
{ "name": "useGridSelector", "kind": "Variable" },
+ { "name": "useGridVirtualization", "kind": "Function" },
{ "name": "ValueOptions", "kind": "TypeAlias" },
+ { "name": "virtualizationStateInitializer", "kind": "Variable" },
{ "name": "viVN", "kind": "Variable" },
{ "name": "zhCN", "kind": "Variable" },
{ "name": "zhTW", "kind": "Variable" }
diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json
index 32e6c5ec47f07..926ef92abb13a 100644
--- a/scripts/x-data-grid.exports.json
+++ b/scripts/x-data-grid.exports.json
@@ -207,7 +207,6 @@
{ "name": "gridDensityValueSelector", "kind": "Variable" },
{ "name": "GridDimensions", "kind": "Interface" },
{ "name": "GridDimensionsApi", "kind": "Interface" },
- { "name": "GridDisableVirtualizationApi", "kind": "Interface" },
{ "name": "GridDragIcon", "kind": "Variable" },
{ "name": "GridEditBooleanCell", "kind": "Function" },
{ "name": "GridEditBooleanCellProps", "kind": "Interface" },
@@ -481,7 +480,12 @@
{ "name": "GridViewColumnIcon", "kind": "Variable" },
{ "name": "GridViewHeadlineIcon", "kind": "Variable" },
{ "name": "GridViewStreamIcon", "kind": "Variable" },
- { "name": "GridVirtualScrollerApi", "kind": "Interface" },
+ { "name": "GridVirtualizationApi", "kind": "Interface" },
+ { "name": "gridVirtualizationColumnEnabledSelector", "kind": "Variable" },
+ { "name": "gridVirtualizationEnabledSelector", "kind": "Variable" },
+ { "name": "GridVirtualizationPrivateApi", "kind": "Interface" },
+ { "name": "gridVirtualizationSelector", "kind": "Variable" },
+ { "name": "GridVirtualizationState", "kind": "TypeAlias" },
{ "name": "GridVisibilityOffIcon", "kind": "Variable" },
{ "name": "gridVisibleColumnDefinitionsSelector", "kind": "Variable" },
{ "name": "gridVisibleColumnFieldsSelector", "kind": "Variable" },
@@ -545,7 +549,9 @@
{ "name": "useGridNativeEventListener", "kind": "Variable" },
{ "name": "useGridRootProps", "kind": "Variable" },
{ "name": "useGridSelector", "kind": "Variable" },
+ { "name": "useGridVirtualization", "kind": "Function" },
{ "name": "ValueOptions", "kind": "TypeAlias" },
+ { "name": "virtualizationStateInitializer", "kind": "Variable" },
{ "name": "viVN", "kind": "Variable" },
{ "name": "zhCN", "kind": "Variable" },
{ "name": "zhTW", "kind": "Variable" }
diff --git a/test/utils/helperFn.ts b/test/utils/helperFn.ts
index 7216af55c17b0..c0b808493e253 100644
--- a/test/utils/helperFn.ts
+++ b/test/utils/helperFn.ts
@@ -12,7 +12,7 @@ export function sleep(duration: number): Promise {
}
export function microtasks() {
- return act(() => Promise.resolve());
+ return act(() => Promise.resolve()) as unknown as Promise;
}
export function spyApi(api: GridApiCommon, methodName: string) {