diff --git a/docs/data/data-grid/style/StripedGrid.js b/docs/data/data-grid/style/StripedGrid.js
new file mode 100644
index 000000000000..3b0524776ee1
--- /dev/null
+++ b/docs/data/data-grid/style/StripedGrid.js
@@ -0,0 +1,58 @@
+import * as React from 'react';
+import { alpha, styled } from '@mui/material/styles';
+import { DataGrid, gridClasses } from '@mui/x-data-grid';
+import { useDemoData } from '@mui/x-data-grid-generator';
+
+const ODD_OPACITY = 0.2;
+
+const StripedDataGrid = styled(DataGrid)(({ theme }) => ({
+ [`& .${gridClasses.row}.even`]: {
+ backgroundColor: theme.palette.grey[200],
+ '&:hover, &.Mui-hovered': {
+ backgroundColor: alpha(theme.palette.primary.main, ODD_OPACITY),
+ '@media (hover: none)': {
+ backgroundColor: 'transparent',
+ },
+ },
+ '&.Mui-selected': {
+ backgroundColor: alpha(
+ theme.palette.primary.main,
+ ODD_OPACITY + theme.palette.action.selectedOpacity,
+ ),
+ '&:hover, &.Mui-hovered': {
+ backgroundColor: alpha(
+ theme.palette.primary.main,
+ ODD_OPACITY +
+ theme.palette.action.selectedOpacity +
+ theme.palette.action.hoverOpacity,
+ ),
+ // Reset on touch devices, it doesn't add specificity
+ '@media (hover: none)': {
+ backgroundColor: alpha(
+ theme.palette.primary.main,
+ ODD_OPACITY + theme.palette.action.selectedOpacity,
+ ),
+ },
+ },
+ },
+ },
+}));
+
+export default function StripedGrid() {
+ const { data, loading } = useDemoData({
+ dataSet: 'Employee',
+ rowLength: 200,
+ });
+
+ return (
+
+
+ params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
+ }
+ />
+
+ );
+}
diff --git a/docs/data/data-grid/style/StripedGrid.tsx b/docs/data/data-grid/style/StripedGrid.tsx
new file mode 100644
index 000000000000..3b0524776ee1
--- /dev/null
+++ b/docs/data/data-grid/style/StripedGrid.tsx
@@ -0,0 +1,58 @@
+import * as React from 'react';
+import { alpha, styled } from '@mui/material/styles';
+import { DataGrid, gridClasses } from '@mui/x-data-grid';
+import { useDemoData } from '@mui/x-data-grid-generator';
+
+const ODD_OPACITY = 0.2;
+
+const StripedDataGrid = styled(DataGrid)(({ theme }) => ({
+ [`& .${gridClasses.row}.even`]: {
+ backgroundColor: theme.palette.grey[200],
+ '&:hover, &.Mui-hovered': {
+ backgroundColor: alpha(theme.palette.primary.main, ODD_OPACITY),
+ '@media (hover: none)': {
+ backgroundColor: 'transparent',
+ },
+ },
+ '&.Mui-selected': {
+ backgroundColor: alpha(
+ theme.palette.primary.main,
+ ODD_OPACITY + theme.palette.action.selectedOpacity,
+ ),
+ '&:hover, &.Mui-hovered': {
+ backgroundColor: alpha(
+ theme.palette.primary.main,
+ ODD_OPACITY +
+ theme.palette.action.selectedOpacity +
+ theme.palette.action.hoverOpacity,
+ ),
+ // Reset on touch devices, it doesn't add specificity
+ '@media (hover: none)': {
+ backgroundColor: alpha(
+ theme.palette.primary.main,
+ ODD_OPACITY + theme.palette.action.selectedOpacity,
+ ),
+ },
+ },
+ },
+ },
+}));
+
+export default function StripedGrid() {
+ const { data, loading } = useDemoData({
+ dataSet: 'Employee',
+ rowLength: 200,
+ });
+
+ return (
+
+
+ params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
+ }
+ />
+
+ );
+}
diff --git a/docs/data/data-grid/style/StripedGrid.tsx.preview b/docs/data/data-grid/style/StripedGrid.tsx.preview
new file mode 100644
index 000000000000..73eed7717645
--- /dev/null
+++ b/docs/data/data-grid/style/StripedGrid.tsx.preview
@@ -0,0 +1,7 @@
+
+ params.indexRelativeToCurrentPage % 2 === 0 ? 'even' : 'odd'
+ }
+/>
\ No newline at end of file
diff --git a/docs/data/data-grid/style/style.md b/docs/data/data-grid/style/style.md
index cad6e47923ee..7959ebaf04df 100644
--- a/docs/data/data-grid/style/style.md
+++ b/docs/data/data-grid/style/style.md
@@ -117,9 +117,17 @@ Choose between one of the following values: 'left' | 'right' | 'center'.
**Note**: You must use `headerAlign` to align the content of the header.
+## Striped rows
+
+The following demo illustrates how the rows of the grid can be stripped.
+
+{{"demo": "StripedGrid.js", "bg": "inline"}}
+
## Custom theme
-The following demo leverages the CSS customization API to match the Ant Design specification.
+You can use the `indexRelativeToCurrentPage` param passed to `getRowClassName` to apply alternating styles to the rows.
+
+The following demo illustrates how this can be achieved.
{{"demo": "AntDesignGrid.js", "defaultCodeOpen": false}}
diff --git a/docs/pages/x/api/data-grid/grid-row-class-name-params.md b/docs/pages/x/api/data-grid/grid-row-class-name-params.md
index 63b47636b634..dae66bcad724 100644
--- a/docs/pages/x/api/data-grid/grid-row-class-name-params.md
+++ b/docs/pages/x/api/data-grid/grid-row-class-name-params.md
@@ -12,11 +12,12 @@ import { GridRowClassNameParams } from '@mui/x-data-grid';
## Properties
-| Name | Type | Description |
-| :-------------------------------------------- | :---------------------------------------------------------------------- | :--------------------------------------------------------- |
-| columns | GridColumns | All grid columns. |
-| getValue | (id: GridRowId, field: string) => any | Get the cell value of a row and field. |
-| id | GridRowId | The grid row id. |
-| isFirstVisible | boolean | Whether this row is the first visible or not. |
-| isLastVisible | boolean | Whether this row is the last visible or not. |
-| row | R | The row model of the row that the current cell belongs to. |
+| Name | Type | Description |
+| :-------------------------------------------------------- | :---------------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- |
+| columns | GridColumns | All grid columns. |
+| getValue | (id: GridRowId, field: string) => any | Get the cell value of a row and field. |
+| id | GridRowId | The grid row id. |
+| indexRelativeToCurrentPage | number | Index of the row in the current page.
If the pagination is disabled, it will be the index relative to all filtered rows. |
+| isFirstVisible | boolean | Whether this row is the first visible or not. |
+| isLastVisible | boolean | Whether this row is the last visible or not. |
+| row | R | The row model of the row that the current cell belongs to. |
diff --git a/docs/pages/x/api/data-grid/grid-row-spacing-params.md b/docs/pages/x/api/data-grid/grid-row-spacing-params.md
index c06eba15b42b..828bc621478a 100644
--- a/docs/pages/x/api/data-grid/grid-row-spacing-params.md
+++ b/docs/pages/x/api/data-grid/grid-row-spacing-params.md
@@ -12,9 +12,10 @@ import { GridRowSpacingParams } from '@mui/x-data-grid';
## Properties
-| Name | Type | Description |
-| :-------------------------------------------- | :--------------------------------------- | :-------------------------------------------- |
-| id | GridRowId | The row id. |
-| isFirstVisible | boolean | Whether this row is the first visible or not. |
-| isLastVisible | boolean | Whether this row is the last visible or not. |
-| model | R | The row model. |
+| Name | Type | Description |
+| :-------------------------------------------------------- | :--------------------------------------- | :---------------------------------------------------------------------------------------------------------------------------- |
+| id | GridRowId | The row id. |
+| indexRelativeToCurrentPage | number | Index of the row in the current page.
If the pagination is disabled, it will be the index relative to all filtered rows. |
+| isFirstVisible | boolean | Whether this row is the first visible or not. |
+| isLastVisible | boolean | Whether this row is the last visible or not. |
+| model | R | The row model. |
diff --git a/packages/grid/x-data-grid/src/components/GridRow.tsx b/packages/grid/x-data-grid/src/components/GridRow.tsx
index 9396b9d45a2b..ac3b74988cb6 100644
--- a/packages/grid/x-data-grid/src/components/GridRow.tsx
+++ b/packages/grid/x-data-grid/src/components/GridRow.tsx
@@ -25,10 +25,15 @@ import { useGridVisibleRows } from '../hooks/utils/useGridVisibleRows';
import { findParentElementFromClassName } from '../utils/domUtils';
import { GRID_CHECKBOX_SELECTION_COL_DEF } from '../colDef/gridCheckboxSelectionColDef';
import { GRID_ACTIONS_COLUMN_TYPE } from '../colDef/gridActionsColDef';
+import { GridRenderEditCellParams } from '../models/params/gridCellParams';
export interface GridRowProps {
rowId: GridRowId;
selected: boolean;
+ /**
+ * Index of the row in the whole sorted and filtered dataset.
+ * If some rows above have expanded children, this index also take those children into account.
+ */
index: number;
rowHeight: number;
containerWidth: number;
@@ -214,6 +219,7 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) {
...apiRef.current.getRowParams(rowId),
isFirstVisible: indexRelativeToCurrentPage === 0,
isLastVisible: indexRelativeToCurrentPage === currentPage.rows.length - 1,
+ indexRelativeToCurrentPage,
};
rowClassName = rootProps.getRowClassName(rowParams);
@@ -257,7 +263,12 @@ function GridRow(props: React.HTMLAttributes & GridRowProps) {
}
if (editCellState != null && column.renderEditCell) {
- const params = { ...cellParams, ...editCellState, api: apiRef.current };
+ const params: GridRenderEditCellParams = {
+ ...cellParams,
+ ...editCellState,
+ api: apiRef.current,
+ };
+
content = column.renderEditCell(params);
// TODO move to GridCell
classNames.push(clsx(gridClasses['cell--editing'], rootProps.classes?.['cell--editing']));
@@ -336,6 +347,10 @@ GridRow.propTypes = {
containerWidth: PropTypes.number.isRequired,
editRowsState: PropTypes.object.isRequired,
firstColumnToRender: PropTypes.number.isRequired,
+ /**
+ * Index of the row in the whole sorted and filtered dataset.
+ * If some rows above have expanded children, this index also take those children into account.
+ */
index: PropTypes.number.isRequired,
isLastVisible: PropTypes.bool,
lastColumnToRender: PropTypes.number.isRequired,
diff --git a/packages/grid/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts b/packages/grid/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts
index e24fc695c1ee..c37064490155 100644
--- a/packages/grid/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts
+++ b/packages/grid/x-data-grid/src/hooks/features/rows/useGridRowsMeta.ts
@@ -75,12 +75,15 @@ export const useGridRowsMeta = (
const initialHeights: Record = { base: baseRowHeight };
if (getRowSpacing) {
- const index = apiRef.current.getRowIndexRelativeToVisibleRows(row.id);
+ const indexRelativeToCurrentPage = apiRef.current.getRowIndexRelativeToVisibleRows(
+ row.id,
+ );
const spacing = getRowSpacing({
...row,
- isFirstVisible: index === 0,
- isLastVisible: index === currentPage.rows.length - 1,
+ isFirstVisible: indexRelativeToCurrentPage === 0,
+ isLastVisible: indexRelativeToCurrentPage === currentPage.rows.length - 1,
+ indexRelativeToCurrentPage,
});
initialHeights.spacingTop = spacing.top ?? 0;
diff --git a/packages/grid/x-data-grid/src/models/params/gridRowParams.ts b/packages/grid/x-data-grid/src/models/params/gridRowParams.ts
index ae2314fb4e97..99129adbb822 100644
--- a/packages/grid/x-data-grid/src/models/params/gridRowParams.ts
+++ b/packages/grid/x-data-grid/src/models/params/gridRowParams.ts
@@ -36,6 +36,11 @@ interface GridRowVisibilityParams {
* Whether this row is the last visible or not.
*/
isLastVisible: boolean;
+ /**
+ * Index of the row in the current page.
+ * If the pagination is disabled, it will be the index relative to all filtered rows.
+ */
+ indexRelativeToCurrentPage: number;
}
/**
diff --git a/packages/grid/x-data-grid/src/tests/rows.DataGrid.test.tsx b/packages/grid/x-data-grid/src/tests/rows.DataGrid.test.tsx
index 7a9cf4a3554d..6da1c5e0574c 100644
--- a/packages/grid/x-data-grid/src/tests/rows.DataGrid.test.tsx
+++ b/packages/grid/x-data-grid/src/tests/rows.DataGrid.test.tsx
@@ -374,16 +374,17 @@ describe(' - Rows', () => {
it('should be called with the correct params', () => {
const getRowSpacing = stub().returns({});
render();
-
expect(getRowSpacing.args[0][0]).to.deep.equal({
isFirstVisible: true,
isLastVisible: false,
+ indexRelativeToCurrentPage: 0,
id: 0,
model: rows[0],
});
expect(getRowSpacing.args[1][0]).to.deep.equal({
isFirstVisible: false,
isLastVisible: true,
+ indexRelativeToCurrentPage: 1,
id: 1,
model: rows[1],
});
@@ -394,12 +395,14 @@ describe(' - Rows', () => {
expect(getRowSpacing.args[0][0]).to.deep.equal({
isFirstVisible: true,
isLastVisible: false,
+ indexRelativeToCurrentPage: 0,
id: 2,
model: rows[2],
});
expect(getRowSpacing.args[1][0]).to.deep.equal({
isFirstVisible: false,
isLastVisible: true,
+ indexRelativeToCurrentPage: 1,
id: 3,
model: rows[3],
});