From c603d4dbf35b3046fefe115b63389855dda8b5a7 Mon Sep 17 00:00:00 2001 From: Armin Mehinovic Date: Thu, 30 Oct 2025 13:43:02 +0100 Subject: [PATCH] Update cell editable state if editable prop is updated in the column definition --- .../src/components/cell/GridCell.tsx | 9 +- .../src/tests/editing.DataGrid.test.tsx | 100 ++++++++++++++++++ 2 files changed, 108 insertions(+), 1 deletion(-) create mode 100644 packages/x-data-grid/src/tests/editing.DataGrid.test.tsx diff --git a/packages/x-data-grid/src/components/cell/GridCell.tsx b/packages/x-data-grid/src/components/cell/GridCell.tsx index 113a3c03c9546..87f92e7e0dcd2 100644 --- a/packages/x-data-grid/src/components/cell/GridCell.tsx +++ b/packages/x-data-grid/src/components/cell/GridCell.tsx @@ -195,7 +195,14 @@ const GridCell = forwardRef(function GridCell(pro formattedValue: forcedFormattedValue, }); cellParams.api = apiRef.current; - cellParams.isEditable = useGridSelector(apiRef, () => apiRef.current.isCellEditable(cellParams)); + + // Subscribe to changes of the `isCellEditable` API result to ensure cells re-render. + // We don't use the result. + // Subscription will trigger a new call of `getCellParamsForRow` above which will recompute the correct value of `isEditable`. + // This is to ensure both of the following cases work: + // - https://github.com/mui/mui-x/issues/19732 + // - https://github.com/mui/mui-x/issues/20143 + useGridSelector(apiRef, () => apiRef.current.isCellEditable(cellParams)); const isSelected = useGridSelector(apiRef, () => apiRef.current.unstable_applyPipeProcessors('isCellSelected', false, { diff --git a/packages/x-data-grid/src/tests/editing.DataGrid.test.tsx b/packages/x-data-grid/src/tests/editing.DataGrid.test.tsx new file mode 100644 index 0000000000000..2f8435f3b28b2 --- /dev/null +++ b/packages/x-data-grid/src/tests/editing.DataGrid.test.tsx @@ -0,0 +1,100 @@ +import * as React from 'react'; +import { createRenderer, screen } from '@mui/internal-test-utils'; +import { DataGrid, gridClasses, GridColDef, GridRowsProp } from '@mui/x-data-grid'; +import { getCell } from 'test/utils/helperFn'; + +describe(' - Cell editable state', () => { + const { render } = createRenderer(); + + function expectCellEditable(rowIndex: number, colIndex: number) { + expect(getCell(rowIndex, colIndex)).to.have.class(gridClasses['cell--editable']); + } + + function expectCellNotEditable(rowIndex: number, colIndex: number) { + expect(getCell(rowIndex, colIndex)).not.to.have.class(gridClasses['cell--editable']); + } + + // based on https://github.com/mui/mui-x/issues/19732 + it('should update cell editable state when `isCellEditable` prop changes', async () => { + const rows: GridRowsProp = [ + { id: 1, name: 'A', age: 20 }, + { id: 2, name: 'B', age: 21 }, + ]; + + const columns: GridColDef[] = [ + { field: 'name', editable: true, width: 150 }, + { field: 'age', type: 'number', editable: true, width: 150 }, + ]; + + function GridWithState() { + const [editable, setEditable] = React.useState(true); + const [editMode, setEditMode] = React.useState<'row' | 'cell'>('cell'); + + return ( +
+ + + params.row.age % 2 === 0 && editable && editMode === 'cell'} + /> +
+ ); + } + + const { user } = render(); + + // Initially: editMode = 'cell', editable = true → row 0 cells editable + expectCellEditable(0, 0); + expectCellEditable(0, 1); + expectCellNotEditable(1, 0); + + // Toggle editable → should remove editable class immediately + await user.click(screen.getByRole('button', { name: 'toggle-editable' })); + expectCellNotEditable(0, 0); + expectCellNotEditable(0, 1); + + // Toggle mode to 'cell' again (it was set to false only) by switching mode away and back + await user.click(screen.getByRole('button', { name: 'toggle-mode' })); + // Now editMode = 'row' → should not be editable regardless of row parity + expectCellNotEditable(0, 0); + + await user.click(screen.getByRole('button', { name: 'toggle-mode' })); + // Back to 'cell' but editable state is false → still not editable + expectCellNotEditable(0, 0); + }); + + // based on https://github.com/mui/mui-x/issues/20143 + it('should update cell editable state when `colDef.editable` changes', async () => { + const rows: GridRowsProp = [{ id: 1, firstName: 'Jon', lastName: 'Snow', age: 14 }]; + + function Component() { + const [isEditable, setIsEditable] = React.useState(true); + const cols: GridColDef[] = [ + { field: 'id', width: 100 }, + { field: 'firstName', width: 150, editable: isEditable }, + { field: 'lastName', width: 150, editable: true }, + ]; + + return ( +
+ + true} /> +
+ ); + } + + const { user } = render(); + + // firstName column is editable initially + expectCellEditable(0, 1); + + // Toggle colDef.editable to false → class should be removed immediately + await user.click(screen.getByRole('button', { name: 'toggle-coldef' })); + expectCellNotEditable(0, 1); + }); +});