diff --git a/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.js b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.js index d9d61b7c7433f..9fe1c320f95be 100644 --- a/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.js +++ b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.js @@ -15,6 +15,8 @@ function CustomToolbar() { } export default function DensitySelectorSmallGrid() { + const [density, setDensity] = React.useState('compact'); + const { data } = useDemoData({ dataSet: 'Commodity', rowLength: 4, @@ -25,7 +27,11 @@ export default function DensitySelectorSmallGrid() {
{ + console.info(`Density updated to: ${newDensity}`); + setDensity(newDensity); + }} slots={{ toolbar: CustomToolbar, }} diff --git a/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx index d9d61b7c7433f..d7d04073e9425 100644 --- a/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx +++ b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx @@ -3,6 +3,7 @@ import { DataGrid, GridToolbarContainer, GridToolbarDensitySelector, + GridDensity, } from '@mui/x-data-grid'; import { useDemoData } from '@mui/x-data-grid-generator'; @@ -15,6 +16,8 @@ function CustomToolbar() { } export default function DensitySelectorSmallGrid() { + const [density, setDensity] = React.useState('compact'); + const { data } = useDemoData({ dataSet: 'Commodity', rowLength: 4, @@ -25,7 +28,11 @@ export default function DensitySelectorSmallGrid() {
{ + console.info(`Density updated to: ${newDensity}`); + setDensity(newDensity); + }} slots={{ toolbar: CustomToolbar, }} diff --git a/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx.preview b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx.preview index 9d198ee8add6a..cd87eee43b444 100644 --- a/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx.preview +++ b/docs/data/data-grid/accessibility/DensitySelectorSmallGrid.tsx.preview @@ -1,6 +1,10 @@ { + console.info(`Density updated to: ${newDensity}`); + setDensity(newDensity); + }} slots={{ toolbar: CustomToolbar, }} diff --git a/docs/data/data-grid/accessibility/accessibility.md b/docs/data/data-grid/accessibility/accessibility.md index 3a54c27b73b07..afb47c68a23a7 100644 --- a/docs/data/data-grid/accessibility/accessibility.md +++ b/docs/data/data-grid/accessibility/accessibility.md @@ -22,22 +22,56 @@ The [WAI-ARIA Authoring Practices](https://www.w3.org/WAI/ARIA/apg/patterns/grid You can change the density of the rows and the column header. -### Density selector +### Density selection from the toolbar -To enable the density selector, create a toolbar containing the `GridToolbarDensitySelector` component and apply it using the `toolbar` property in the Data Grid's `slots` prop. -The user can then change the density of the Data Grid by using the density selector from the toolbar, as the following demo illustrates: +To enable the density selection from the toolbar, you can do one of the following: + +1. Enable the default toolbar component by passing the `slots.toolbar` prop to the Data Grid. +2. Create a specific toolbar containing only the `GridToolbarDensitySelector` component and apply it using the `toolbar` property in the Data Grid's `slots` prop. + +The user can then change the density of the Data Grid by using the density selection menu from the toolbar, as the following demo illustrates: {{"demo": "DensitySelectorGrid.js", "bg": "inline"}} -To hide the density selector, add the `disableDensitySelector` prop to the Data Grid. +To disable the density selection menu, pass the `disableDensitySelector` prop to the Data Grid. + +### Set the density programmatically + +The Data Grid exposes the `density` prop which supports the following values: + +- `standard` (default) +- `compact` +- `comfortable` + +You can set the density programmatically in one of the following ways: + +1. Uncontrolled – initialize the density with the `initialState.density` prop. + + ```tsx + + ``` + +2. Controlled – pass the `density` and `onDensityChange` props. For more advanced use cases, you can also subscribe to the `densityChange` grid event. + + ```tsx + const [density, setDensity] = React.useState('compact'); -### Density prop + return ( + setDensity(newDensity)} + /> + ); + ``` -Set the vertical density of the Data Grid using the `density` prop. -This prop applies the values determined by the `rowHeight` and `columnHeaderHeight` props, if supplied. +The `density` prop applies the values determined by the `rowHeight` and `columnHeaderHeight` props, if supplied. The user can override this setting with the optional toolbar density selector. -The following demo shows a Data Grid with the default density set to `compact`: +The following demo shows a Data Grid with the controlled density set to `compact` and outputs the current density to the console when the user changes it using the density selector from the toolbar: {{"demo": "DensitySelectorSmallGrid.js", "bg": "inline"}} diff --git a/docs/data/data-grid/events/events.json b/docs/data/data-grid/events/events.json index 6bb6029718fa7..64c820e1c397d 100644 --- a/docs/data/data-grid/events/events.json +++ b/docs/data/data-grid/events/events.json @@ -202,6 +202,13 @@ "event": "MuiEvent<{}>", "componentProp": "onResize" }, + { + "projects": ["x-data-grid", "x-data-grid-pro", "x-data-grid-premium"], + "name": "densityChange", + "description": "Fired when the density changes.", + "params": "GridDensity", + "event": "MuiEvent<{}>" + }, { "projects": ["x-data-grid-premium"], "name": "excelExportStateChange", diff --git a/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md index 8e693b6e8e61c..fd89b82c9ecda 100644 --- a/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md +++ b/docs/data/migration/migration-data-grid-v6/migration-data-grid-v6.md @@ -303,6 +303,32 @@ See the [Direct state access](/x/react-data-grid/state/#direct-selector-access) + groupingValueGetter: (value: { name: string }) => value.name, ``` +### Density + +- The `density` is a [controlled prop](/x/react-data-grid/accessibility/#set-the-density-programmatically) now, if you were previously passing the `density` prop to the Data Grid, you will need to do one of the following: + +1. Move it to the `initialState.density` to initialize it. + +```diff + +``` + +2. Move it to the state and use `onDensityChange` callback to update the `density` prop accordingly for it to work as expected. + +```diff + const [density, setDensity] = React.useState('compact'); + setDensity(newDensity)} + /> +``` + +- The selector `gridDensityValueSelector` was removed, use the `gridDensitySelector` instead. + 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 559aa75b2e3f4..4c43103ed7e0b 100644 --- a/docs/pages/x/api/data-grid/data-grid-premium.json +++ b/docs/pages/x/api/data-grid/data-grid-premium.json @@ -361,6 +361,13 @@ "describedArgs": ["params", "event", "details"] } }, + "onDensityChange": { + "type": { "name": "func" }, + "signature": { + "type": "function(density: GridDensity) => void", + "describedArgs": ["density"] + } + }, "onDetailPanelExpandedRowIdsChange": { "type": { "name": "func" }, "signature": { 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 198c5f68c6da7..6c2382cfa2982 100644 --- a/docs/pages/x/api/data-grid/data-grid-pro.json +++ b/docs/pages/x/api/data-grid/data-grid-pro.json @@ -318,6 +318,13 @@ "describedArgs": ["params", "event", "details"] } }, + "onDensityChange": { + "type": { "name": "func" }, + "signature": { + "type": "function(density: GridDensity) => void", + "describedArgs": ["density"] + } + }, "onDetailPanelExpandedRowIdsChange": { "type": { "name": "func" }, "signature": { diff --git a/docs/pages/x/api/data-grid/data-grid.json b/docs/pages/x/api/data-grid/data-grid.json index 468423143dd70..9fd2b885eb548 100644 --- a/docs/pages/x/api/data-grid/data-grid.json +++ b/docs/pages/x/api/data-grid/data-grid.json @@ -278,6 +278,13 @@ "describedArgs": ["params", "event", "details"] } }, + "onDensityChange": { + "type": { "name": "func" }, + "signature": { + "type": "function(density: GridDensity) => void", + "describedArgs": ["density"] + } + }, "onFilterModelChange": { "type": { "name": "func" }, "signature": { diff --git a/docs/pages/x/api/data-grid/selectors.json b/docs/pages/x/api/data-grid/selectors.json index fc2fa812d4817..1140b1bf1d946 100644 --- a/docs/pages/x/api/data-grid/selectors.json +++ b/docs/pages/x/api/data-grid/selectors.json @@ -124,15 +124,9 @@ }, { "name": "gridDensitySelector", - "returnType": "GridDensityState", - "description": "", - "supportsApiRef": false - }, - { - "name": "gridDensityValueSelector", "returnType": "GridDensity", "description": "", - "supportsApiRef": true + "supportsApiRef": false }, { "name": "gridDetailPanelExpandedRowIdsSelector", diff --git a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json index 96b941032c92d..55da4f984364f 100644 --- a/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json +++ b/docs/translations/api-docs/data-grid/data-grid-premium/data-grid-premium.json @@ -394,6 +394,10 @@ "details": "Additional details for this callback." } }, + "onDensityChange": { + "description": "Callback fired when the density changes.", + "typeDescriptions": { "density": "New density value." } + }, "onDetailPanelExpandedRowIdsChange": { "description": "Callback fired when the detail panel of a row is opened or closed.", "typeDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json index c4f04eb81c606..32c54712411fd 100644 --- a/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json +++ b/docs/translations/api-docs/data-grid/data-grid-pro/data-grid-pro.json @@ -351,6 +351,10 @@ "details": "Additional details for this callback." } }, + "onDensityChange": { + "description": "Callback fired when the density changes.", + "typeDescriptions": { "density": "New density value." } + }, "onDetailPanelExpandedRowIdsChange": { "description": "Callback fired when the detail panel of a row is opened or closed.", "typeDescriptions": { diff --git a/docs/translations/api-docs/data-grid/data-grid/data-grid.json b/docs/translations/api-docs/data-grid/data-grid/data-grid.json index c6d25a3f35a80..a9dd4e497f260 100644 --- a/docs/translations/api-docs/data-grid/data-grid/data-grid.json +++ b/docs/translations/api-docs/data-grid/data-grid/data-grid.json @@ -295,6 +295,10 @@ "details": "Additional details for this callback." } }, + "onDensityChange": { + "description": "Callback fired when the density changes.", + "typeDescriptions": { "density": "New density value." } + }, "onFilterModelChange": { "description": "Callback fired when the Filter model changes before the filters are applied.", "typeDescriptions": { diff --git a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx index a411583a776c3..8991449eda96b 100644 --- a/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx +++ b/packages/x-data-grid-premium/src/DataGridPremium/DataGridPremium.tsx @@ -678,6 +678,11 @@ DataGridPremiumRaw.propTypes = { * @param {GridCallbackDetails} details Additional details for this callback. */ onColumnWidthChange: PropTypes.func, + /** + * Callback fired when the density changes. + * @param {GridDensity} density New density value. + */ + onDensityChange: PropTypes.func, /** * Callback fired when the detail panel of a row is opened or closed. * @param {GridRowId[]} ids The ids of the rows which have the detail panel open. diff --git a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx index 55bae26091799..df1e21319472d 100644 --- a/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx +++ b/packages/x-data-grid-pro/src/DataGridPro/DataGridPro.tsx @@ -591,6 +591,11 @@ DataGridProRaw.propTypes = { * @param {GridCallbackDetails} details Additional details for this callback. */ onColumnWidthChange: PropTypes.func, + /** + * Callback fired when the density changes. + * @param {GridDensity} density New density value. + */ + onDensityChange: PropTypes.func, /** * Callback fired when the detail panel of a row is opened or closed. * @param {GridRowId[]} ids The ids of the rows which have the detail panel open. diff --git a/packages/x-data-grid/src/DataGrid/DataGrid.tsx b/packages/x-data-grid/src/DataGrid/DataGrid.tsx index 475dc646e981f..fe0f14bbb9d6c 100644 --- a/packages/x-data-grid/src/DataGrid/DataGrid.tsx +++ b/packages/x-data-grid/src/DataGrid/DataGrid.tsx @@ -506,6 +506,11 @@ DataGridRaw.propTypes = { * @param {GridCallbackDetails} details Additional details for this callback. */ onColumnWidthChange: PropTypes.func, + /** + * Callback fired when the density changes. + * @param {GridDensity} density New density value. + */ + onDensityChange: PropTypes.func, /** * Callback fired when the Filter model changes before the filters are applied. * @param {GridFilterModel} model With all properties from [[GridFilterModel]]. diff --git a/packages/x-data-grid/src/DataGrid/useDataGridProps.ts b/packages/x-data-grid/src/DataGrid/useDataGridProps.ts index b0f91d49cf291..3759d21e04241 100644 --- a/packages/x-data-grid/src/DataGrid/useDataGridProps.ts +++ b/packages/x-data-grid/src/DataGrid/useDataGridProps.ts @@ -36,7 +36,6 @@ export const DATA_GRID_PROPS_DEFAULT_VALUES: DataGridPropsWithDefaultValues = { columnThreshold: 3, rowThreshold: 3, rowSelection: true, - density: 'standard', disableColumnFilter: false, disableColumnMenu: false, disableColumnSelector: false, diff --git a/packages/x-data-grid/src/components/containers/GridRoot.tsx b/packages/x-data-grid/src/components/containers/GridRoot.tsx index b42fcb4341ed4..a8e56cecfc30a 100644 --- a/packages/x-data-grid/src/components/containers/GridRoot.tsx +++ b/packages/x-data-grid/src/components/containers/GridRoot.tsx @@ -14,7 +14,7 @@ import { useGridSelector } from '../../hooks/utils/useGridSelector'; import { useGridPrivateApiContext } from '../../hooks/utils/useGridPrivateApiContext'; import { useGridRootProps } from '../../hooks/utils/useGridRootProps'; import { getDataGridUtilityClass } from '../../constants/gridClasses'; -import { gridDensityValueSelector } from '../../hooks/features/density/densitySelector'; +import { gridDensitySelector } from '../../hooks/features/density/densitySelector'; import { DataGridProcessedProps } from '../../models/props/DataGridProps'; import { GridDensity } from '../../models/gridDensity'; @@ -55,13 +55,13 @@ const GridRoot = React.forwardRef(function GridRo const rootProps = useGridRootProps(); const { children, className, ...other } = props; const apiRef = useGridPrivateApiContext(); - const densityValue = useGridSelector(apiRef, gridDensityValueSelector); + const density = useGridSelector(apiRef, gridDensitySelector); const rootElementRef = apiRef.current.rootElementRef; const handleRef = useForkRef(rootElementRef, ref); const ownerState = { ...rootProps, - density: densityValue, + density, }; const classes = useUtilityClasses(ownerState); diff --git a/packages/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx b/packages/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx index 1c4b0b6a63511..4c790f2ee720b 100644 --- a/packages/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx +++ b/packages/x-data-grid/src/components/toolbar/GridToolbarDensitySelector.tsx @@ -6,7 +6,7 @@ import { ButtonProps } from '@mui/material/Button'; import { TooltipProps } from '@mui/material/Tooltip'; import MenuItem from '@mui/material/MenuItem'; import ListItemIcon from '@mui/material/ListItemIcon'; -import { gridDensityValueSelector } from '../../hooks/features/density/densitySelector'; +import { gridDensitySelector } from '../../hooks/features/density/densitySelector'; import { GridDensity } from '../../models/gridDensity'; import { isHideMenuKey, isTabKey } from '../../utils/keyboardUtils'; import { useGridApiContext } from '../../hooks/utils/useGridApiContext'; @@ -33,7 +33,7 @@ const GridToolbarDensitySelector = React.forwardRef< const tooltipProps = slotProps.tooltip || {}; const apiRef = useGridApiContext(); const rootProps = useGridRootProps(); - const densityValue = useGridSelector(apiRef, gridDensityValueSelector); + const density = useGridSelector(apiRef, gridDensitySelector); const densityButtonId = useId(); const densityMenuId = useId(); @@ -60,7 +60,7 @@ const GridToolbarDensitySelector = React.forwardRef< ]; const startIcon = React.useMemo(() => { - switch (densityValue) { + switch (density) { case 'compact': return ; case 'comfortable': @@ -68,7 +68,7 @@ const GridToolbarDensitySelector = React.forwardRef< default: return ; } - }, [densityValue, rootProps]); + }, [density, rootProps]); const handleDensitySelectorOpen = (event: React.MouseEvent) => { setOpen((prevOpen) => !prevOpen); @@ -100,7 +100,7 @@ const GridToolbarDensitySelector = React.forwardRef< handleDensityUpdate(option.value)} - selected={option.value === densityValue} + selected={option.value === density} > {option.icon} {option.label} diff --git a/packages/x-data-grid/src/hooks/features/density/densitySelector.ts b/packages/x-data-grid/src/hooks/features/density/densitySelector.ts index 277cc3cfbfc61..0fcbacc3ec7f4 100644 --- a/packages/x-data-grid/src/hooks/features/density/densitySelector.ts +++ b/packages/x-data-grid/src/hooks/features/density/densitySelector.ts @@ -1,14 +1,19 @@ import { createSelector } from '../../../utils/createSelector'; import { GridStateCommunity } from '../../../models/gridStateCommunity'; +import { GridDensity } from '../../../models/gridDensity'; -export const gridDensitySelector = (state: GridStateCommunity) => state.density; +export const COMPACT_DENSITY_FACTOR = 0.7; +export const COMFORTABLE_DENSITY_FACTOR = 1.3; -export const gridDensityValueSelector = createSelector( - gridDensitySelector, - (density) => density.value, -); +const DENSITY_FACTORS: Record = { + compact: COMPACT_DENSITY_FACTOR, + comfortable: COMFORTABLE_DENSITY_FACTOR, + standard: 1, +}; + +export const gridDensitySelector = (state: GridStateCommunity) => state.density; export const gridDensityFactorSelector = createSelector( gridDensitySelector, - (density) => density.factor, + (density) => DENSITY_FACTORS[density], ); diff --git a/packages/x-data-grid/src/hooks/features/density/densityState.ts b/packages/x-data-grid/src/hooks/features/density/densityState.ts index bebaa1844b514..fdc6aa28a9a10 100644 --- a/packages/x-data-grid/src/hooks/features/density/densityState.ts +++ b/packages/x-data-grid/src/hooks/features/density/densityState.ts @@ -1,6 +1,3 @@ import { GridDensity } from '../../../models/gridDensity'; -export interface GridDensityState { - value: GridDensity; - factor: number; -} +export type GridDensityState = GridDensity; diff --git a/packages/x-data-grid/src/hooks/features/density/useGridDensity.tsx b/packages/x-data-grid/src/hooks/features/density/useGridDensity.tsx index 3b3bf2d7dab45..172b161c2cdbf 100644 --- a/packages/x-data-grid/src/hooks/features/density/useGridDensity.tsx +++ b/packages/x-data-grid/src/hooks/features/density/useGridDensity.tsx @@ -1,59 +1,52 @@ import * as React from 'react'; -import { GridDensity } from '../../../models/gridDensity'; +import useEventCallback from '@mui/utils/useEventCallback'; import { useGridLogger } from '../../utils/useGridLogger'; import { GridPrivateApiCommunity } from '../../../models/api/gridApiCommunity'; import { useGridApiMethod } from '../../utils/useGridApiMethod'; import { GridDensityApi } from '../../../models/api/gridDensityApi'; import { DataGridProcessedProps } from '../../../models/props/DataGridProps'; import { gridDensitySelector } from './densitySelector'; -import { isDeepEqual } from '../../../utils/utils'; import { GridStateInitializer } from '../../utils/useGridInitializeState'; -export const COMPACT_DENSITY_FACTOR = 0.7; -export const COMFORTABLE_DENSITY_FACTOR = 1.3; - -const DENSITY_FACTORS: Record = { - compact: COMPACT_DENSITY_FACTOR, - comfortable: COMFORTABLE_DENSITY_FACTOR, - standard: 1, -}; - export const densityStateInitializer: GridStateInitializer< - Pick + Pick > = (state, props) => ({ ...state, - density: { value: props.density, factor: DENSITY_FACTORS[props.density] }, + density: props.initialState?.density ?? props.density ?? 'standard', }); export const useGridDensity = ( apiRef: React.MutableRefObject, - props: Pick, + props: Pick, ): void => { const logger = useGridLogger(apiRef, 'useDensity'); - const setDensity = React.useCallback( - (newDensity): void => { - logger.debug(`Set grid density to ${newDensity}`); - apiRef.current.setState((state) => { - const currentDensityState = gridDensitySelector(state); - const newDensityState = { value: newDensity, factor: DENSITY_FACTORS[newDensity] }; + apiRef.current.registerControlState({ + stateId: 'density', + propModel: props.density, + propOnChange: props.onDensityChange, + stateSelector: gridDensitySelector, + changeEvent: 'densityChange', + }); + + const setDensity = useEventCallback((newDensity): void => { + const currentDensity = gridDensitySelector(apiRef.current.state); + if (currentDensity === newDensity) { + return; + } - if (isDeepEqual(currentDensityState, newDensityState)) { - return state; - } + logger.debug(`Set grid density to ${newDensity}`); - return { - ...state, - density: newDensityState, - }; - }); - apiRef.current.forceUpdate(); - }, - [logger, apiRef], - ); + apiRef.current.setState((state) => ({ + ...state, + density: newDensity, + })); + }); React.useEffect(() => { - apiRef.current.setDensity(props.density); + if (props.density) { + apiRef.current.setDensity(props.density); + } }, [apiRef, props.density]); const densityApi: GridDensityApi = { diff --git a/packages/x-data-grid/src/models/events/gridEventLookup.ts b/packages/x-data-grid/src/models/events/gridEventLookup.ts index 451988f596a1d..5f29dceffb45d 100644 --- a/packages/x-data-grid/src/models/events/gridEventLookup.ts +++ b/packages/x-data-grid/src/models/events/gridEventLookup.ts @@ -25,6 +25,7 @@ import type { GridStrategyProcessorName } from '../../hooks/core/strategyProcess import { GridRowEditStartParams, GridRowEditStopParams } from '../params/gridRowParams'; import { GridCellModesModel, GridRowModesModel } from '../api/gridEditingApi'; import { GridPaginationModel } from '../gridPaginationProps'; +import { GridDensity } from '../gridDensity'; export interface GridRowEventLookup { /** @@ -362,6 +363,10 @@ export interface GridControlledStateEventLookup { * Fired when the row count change. */ rowCountChange: { params: number }; + /** + * Fired when the density changes. + */ + densityChange: { params: GridDensity }; } export interface GridControlledStateReasonLookup { diff --git a/packages/x-data-grid/src/models/gridStateCommunity.ts b/packages/x-data-grid/src/models/gridStateCommunity.ts index 95015838ddc1b..7e00992692bf8 100644 --- a/packages/x-data-grid/src/models/gridStateCommunity.ts +++ b/packages/x-data-grid/src/models/gridStateCommunity.ts @@ -63,4 +63,5 @@ export interface GridInitialStateCommunity { filter?: GridFilterInitialState; columns?: GridColumnsInitialState; preferencePanel?: GridPreferencePanelInitialState; + density?: GridDensityState; } diff --git a/packages/x-data-grid/src/models/props/DataGridProps.ts b/packages/x-data-grid/src/models/props/DataGridProps.ts index 90c2b031ccbea..13918cd9b5ab5 100644 --- a/packages/x-data-grid/src/models/props/DataGridProps.ts +++ b/packages/x-data-grid/src/models/props/DataGridProps.ts @@ -152,11 +152,6 @@ export interface DataGridPropsWithDefaultValues { * @default 3 */ columnThreshold: number; - /** - * Set the density of the Data Grid. - * @default "standard" - */ - density: GridDensity; /** * If `true`, column filters are disabled. * @default false @@ -408,6 +403,11 @@ export interface DataGridPropsWithoutDefaultValue; + /** + * Set the density of the Data Grid. + * @default "standard" + */ + density?: GridDensity; /** * Set the total number of rows, if it is different from the length of the value `rows` prop. * If some rows have children (for instance in the tree data), this number represents the amount of top level rows. @@ -561,6 +561,11 @@ export interface DataGridPropsWithoutDefaultValue; + /** + * Callback fired when the density changes. + * @param {GridDensity} density New density value. + */ + onDensityChange?: (density: GridDensity) => void; /** * Callback fired when a row is clicked. * Not called if the target clicked is an interactive element added by the built-in columns. diff --git a/packages/x-data-grid/src/tests/density.DataGrid.test.tsx b/packages/x-data-grid/src/tests/density.DataGrid.test.tsx new file mode 100644 index 0000000000000..6e71774e9651a --- /dev/null +++ b/packages/x-data-grid/src/tests/density.DataGrid.test.tsx @@ -0,0 +1,229 @@ +import * as React from 'react'; +import { spy } from 'sinon'; +import { createRenderer, fireEvent, screen } from '@mui-internal/test-utils'; +import { grid } from 'test/utils/helperFn'; +import { expect } from 'chai'; +import { DataGrid, DataGridProps, GridToolbar, gridClasses } from '@mui/x-data-grid'; +import { + COMFORTABLE_DENSITY_FACTOR, + COMPACT_DENSITY_FACTOR, +} from '../hooks/features/density/densitySelector'; + +const isJSDOM = /jsdom/.test(window.navigator.userAgent); + +describe(' - Density', () => { + const { render, clock } = createRenderer({ clock: 'fake' }); + + const baselineProps = { + autoHeight: isJSDOM, + rows: [ + { + id: 0, + brand: 'Nike', + }, + { + id: 1, + brand: 'Adidas', + }, + { + id: 2, + brand: 'Puma', + }, + ], + columns: [ + { + field: 'id', + }, + { + field: 'brand', + }, + ], + }; + + function expectHeight(value: number) { + expect(screen.getAllByRole('row')[1]).toHaveInlineStyle({ + maxHeight: `${Math.floor(value)}px`, + }); + + expect(getComputedStyle(screen.getAllByRole('gridcell')[1]).height).to.equal( + `${Math.floor(value)}px`, + ); + } + + before(function beforeHook() { + if (isJSDOM) { + // JSDOM seem to not support CSS variables properly and `height: var(--height)` ends up being `height: ''` + this.skip(); + } + }); + + describe('prop: `initialState.density`', () => { + it('should set the density to the value of initialState.density', () => { + const rowHeight = 30; + render( +
+ +
, + ); + + expectHeight(rowHeight * COMPACT_DENSITY_FACTOR); + }); + }); + + describe('prop: `density`', () => { + it('should set the density value using density prop', () => { + const rowHeight = 30; + render( +
+ +
, + ); + + expectHeight(rowHeight * COMPACT_DENSITY_FACTOR); + }); + + it('should allow to control the density from the prop using state', () => { + const rowHeight = 30; + + function Grid(props: Partial) { + return ( +
+ +
+ ); + } + + const { setProps, getByText } = render(); + + expectHeight(rowHeight); + + fireEvent.click(getByText('Density')); + clock.tick(100); + fireEvent.click(getByText('Compact')); + + // Not updated because of the controlled prop + expectHeight(rowHeight); + + // Explicitly update the prop + setProps({ density: 'compact' }); + clock.tick(200); + expectHeight(rowHeight * COMPACT_DENSITY_FACTOR); + }); + + it('should call `onDensityChange` prop when density gets updated', () => { + const onDensityChange = spy(); + function Test() { + return ( +
+ +
+ ); + } + const { getByText } = render(); + fireEvent.click(getByText('Density')); + fireEvent.click(getByText('Comfortable')); + expect(onDensityChange.callCount).to.equal(1); + expect(onDensityChange.firstCall.args[0]).to.equal('comfortable'); + }); + }); + + describe('density selection menu', () => { + it('should increase grid density when selecting compact density', () => { + const rowHeight = 30; + const { getByText } = render( +
+ +
, + ); + + fireEvent.click(getByText('Density')); + clock.tick(100); + fireEvent.click(getByText('Compact')); + + expectHeight(rowHeight * COMPACT_DENSITY_FACTOR); + }); + + it('should decrease grid density when selecting comfortable density', () => { + const rowHeight = 30; + const { getByText } = render( +
+ +
, + ); + + fireEvent.click(getByText('Density')); + fireEvent.click(getByText('Comfortable')); + + expectHeight(rowHeight * COMFORTABLE_DENSITY_FACTOR); + }); + + it('should increase grid density even if toolbar is not enabled', () => { + const rowHeight = 30; + render( +
+ +
, + ); + + expectHeight(rowHeight * COMPACT_DENSITY_FACTOR); + }); + + it('should decrease grid density even if toolbar is not enabled', () => { + const rowHeight = 30; + render( +
+ +
, + ); + + expectHeight(rowHeight * COMFORTABLE_DENSITY_FACTOR); + }); + + it('should apply to the root element a class corresponding to the current density', () => { + function Test(props: Partial) { + return ( +
+ +
+ ); + } + const { setProps } = render(); + expect(grid('root')).to.have.class(gridClasses['root--densityStandard']); + setProps({ density: 'compact' }); + expect(grid('root')).to.have.class(gridClasses['root--densityCompact']); + setProps({ density: 'comfortable' }); + expect(grid('root')).to.have.class(gridClasses['root--densityComfortable']); + }); + }); +}); diff --git a/packages/x-data-grid/src/tests/rows.DataGrid.test.tsx b/packages/x-data-grid/src/tests/rows.DataGrid.test.tsx index 0056600086a73..12a40be44b724 100644 --- a/packages/x-data-grid/src/tests/rows.DataGrid.test.tsx +++ b/packages/x-data-grid/src/tests/rows.DataGrid.test.tsx @@ -35,7 +35,7 @@ import { } from 'test/utils/helperFn'; import Dialog from '@mui/material/Dialog'; -import { COMPACT_DENSITY_FACTOR } from '../hooks/features/density/useGridDensity'; +import { COMPACT_DENSITY_FACTOR } from '../hooks/features/density/densitySelector'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); diff --git a/packages/x-data-grid/src/tests/toolbar.DataGrid.test.tsx b/packages/x-data-grid/src/tests/toolbar.DataGrid.test.tsx index 1839faa819dc0..cfba3fb2e0f15 100644 --- a/packages/x-data-grid/src/tests/toolbar.DataGrid.test.tsx +++ b/packages/x-data-grid/src/tests/toolbar.DataGrid.test.tsx @@ -1,23 +1,13 @@ import * as React from 'react'; import { createRenderer, fireEvent, screen, act } from '@mui-internal/test-utils'; -import { getColumnHeadersTextContent, grid } from 'test/utils/helperFn'; +import { getColumnHeadersTextContent } from 'test/utils/helperFn'; import { expect } from 'chai'; -import { - DataGrid, - DataGridProps, - GridToolbar, - gridClasses, - GridColumnsManagementProps, -} from '@mui/x-data-grid'; -import { - COMFORTABLE_DENSITY_FACTOR, - COMPACT_DENSITY_FACTOR, -} from '../hooks/features/density/useGridDensity'; +import { DataGrid, GridToolbar, GridColumnsManagementProps } from '@mui/x-data-grid'; const isJSDOM = /jsdom/.test(window.navigator.userAgent); describe(' - Toolbar', () => { - const { render, clock } = createRenderer({ clock: 'fake' }); + const { render } = createRenderer({ clock: 'fake' }); const baselineProps = { autoHeight: isJSDOM, @@ -45,104 +35,6 @@ describe(' - Toolbar', () => { ], }; - describe('density selector', () => { - before(function beforeHook() { - if (isJSDOM) { - // JSDOM seem to not support CSS variables properly and `height: var(--height)` ends up being `height: ''` - this.skip(); - } - }); - - function expectHeight(value: number) { - expect(screen.getAllByRole('row')[1]).toHaveInlineStyle({ - maxHeight: `${Math.floor(value)}px`, - }); - - expect(getComputedStyle(screen.getAllByRole('gridcell')[1]).height).to.equal( - `${Math.floor(value)}px`, - ); - } - - it('should increase grid density when selecting compact density', () => { - const rowHeight = 30; - const { getByText } = render( -
- -
, - ); - - fireEvent.click(getByText('Density')); - clock.tick(100); - fireEvent.click(getByText('Compact')); - - expectHeight(rowHeight * COMPACT_DENSITY_FACTOR); - }); - - it('should decrease grid density when selecting comfortable density', () => { - const rowHeight = 30; - const { getByText } = render( -
- -
, - ); - - fireEvent.click(getByText('Density')); - fireEvent.click(getByText('Comfortable')); - - expectHeight(rowHeight * COMFORTABLE_DENSITY_FACTOR); - }); - - it('should increase grid density even if toolbar is not enabled', () => { - const rowHeight = 30; - render( -
- -
, - ); - - expectHeight(rowHeight * COMPACT_DENSITY_FACTOR); - }); - - it('should decrease grid density even if toolbar is not enabled', () => { - const rowHeight = 30; - render( -
- -
, - ); - - expectHeight(rowHeight * COMFORTABLE_DENSITY_FACTOR); - }); - - it('should apply to the root element a class corresponding to the current density', () => { - function Test(props: Partial) { - return ( -
- -
- ); - } - const { setProps } = render(); - expect(grid('root')).to.have.class(gridClasses['root--densityStandard']); - setProps({ density: 'compact' }); - expect(grid('root')).to.have.class(gridClasses['root--densityCompact']); - setProps({ density: 'comfortable' }); - expect(grid('root')).to.have.class(gridClasses['root--densityComfortable']); - }); - }); - describe('column selector', () => { it('should hide "id" column when hiding it from the column selector', () => { const { getByText } = render( diff --git a/scripts/x-data-grid-premium.exports.json b/scripts/x-data-grid-premium.exports.json index 227740915c9d6..8420497c48d8f 100644 --- a/scripts/x-data-grid-premium.exports.json +++ b/scripts/x-data-grid-premium.exports.json @@ -20,6 +20,8 @@ { "name": "ColumnsManagementPropsOverrides", "kind": "Interface" }, { "name": "ColumnsPanelPropsOverrides", "kind": "Interface" }, { "name": "ColumnsStylesInterface", "kind": "Interface" }, + { "name": "COMFORTABLE_DENSITY_FACTOR", "kind": "Variable" }, + { "name": "COMPACT_DENSITY_FACTOR", "kind": "Variable" }, { "name": "createUseGridApiEventHandler", "kind": "Function" }, { "name": "CursorCoordinates", "kind": "Interface" }, { "name": "DATA_GRID_PREMIUM_PROPS_DEFAULT_VALUES", "kind": "Variable" }, @@ -251,8 +253,7 @@ { "name": "gridDensityFactorSelector", "kind": "Variable" }, { "name": "GridDensityOption", "kind": "Interface" }, { "name": "gridDensitySelector", "kind": "Variable" }, - { "name": "GridDensityState", "kind": "Interface" }, - { "name": "gridDensityValueSelector", "kind": "Variable" }, + { "name": "GridDensityState", "kind": "TypeAlias" }, { "name": "GridDetailPanelApi", "kind": "Interface" }, { "name": "gridDetailPanelExpandedRowIdsSelector", "kind": "Variable" }, { "name": "gridDetailPanelExpandedRowsContentCacheSelector", "kind": "Variable" }, diff --git a/scripts/x-data-grid-pro.exports.json b/scripts/x-data-grid-pro.exports.json index f9e4d9888c0e0..ef75b2918cb76 100644 --- a/scripts/x-data-grid-pro.exports.json +++ b/scripts/x-data-grid-pro.exports.json @@ -19,6 +19,8 @@ { "name": "ColumnMenuPropsOverrides", "kind": "Interface" }, { "name": "ColumnsManagementPropsOverrides", "kind": "Interface" }, { "name": "ColumnsPanelPropsOverrides", "kind": "Interface" }, + { "name": "COMFORTABLE_DENSITY_FACTOR", "kind": "Variable" }, + { "name": "COMPACT_DENSITY_FACTOR", "kind": "Variable" }, { "name": "createUseGridApiEventHandler", "kind": "Function" }, { "name": "CursorCoordinates", "kind": "Interface" }, { "name": "DATA_GRID_PRO_PROPS_DEFAULT_VALUES", "kind": "Variable" }, @@ -225,8 +227,7 @@ { "name": "gridDensityFactorSelector", "kind": "Variable" }, { "name": "GridDensityOption", "kind": "Interface" }, { "name": "gridDensitySelector", "kind": "Variable" }, - { "name": "GridDensityState", "kind": "Interface" }, - { "name": "gridDensityValueSelector", "kind": "Variable" }, + { "name": "GridDensityState", "kind": "TypeAlias" }, { "name": "GridDetailPanelApi", "kind": "Interface" }, { "name": "gridDetailPanelExpandedRowIdsSelector", "kind": "Variable" }, { "name": "gridDetailPanelExpandedRowsContentCacheSelector", "kind": "Variable" }, diff --git a/scripts/x-data-grid.exports.json b/scripts/x-data-grid.exports.json index 5464d87a7253d..75ca11aa57a44 100644 --- a/scripts/x-data-grid.exports.json +++ b/scripts/x-data-grid.exports.json @@ -19,6 +19,8 @@ { "name": "ColumnMenuPropsOverrides", "kind": "Interface" }, { "name": "ColumnsManagementPropsOverrides", "kind": "Interface" }, { "name": "ColumnsPanelPropsOverrides", "kind": "Interface" }, + { "name": "COMFORTABLE_DENSITY_FACTOR", "kind": "Variable" }, + { "name": "COMPACT_DENSITY_FACTOR", "kind": "Variable" }, { "name": "createUseGridApiEventHandler", "kind": "Function" }, { "name": "CursorCoordinates", "kind": "Interface" }, { "name": "DATA_GRID_PROPS_DEFAULT_VALUES", "kind": "Variable" }, @@ -211,8 +213,7 @@ { "name": "gridDensityFactorSelector", "kind": "Variable" }, { "name": "GridDensityOption", "kind": "Interface" }, { "name": "gridDensitySelector", "kind": "Variable" }, - { "name": "GridDensityState", "kind": "Interface" }, - { "name": "gridDensityValueSelector", "kind": "Variable" }, + { "name": "GridDensityState", "kind": "TypeAlias" }, { "name": "GridDimensions", "kind": "Interface" }, { "name": "GridDimensionsApi", "kind": "Interface" }, { "name": "gridDimensionsSelector", "kind": "Variable" },