Skip to content

Commit cd2c62d

Browse files
authored
[DataGrid] Update cell editable state if editable prop is updated in the column definition (#20147)
1 parent 49816fd commit cd2c62d

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

packages/x-data-grid/src/components/cell/GridCell.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,14 @@ const GridCell = forwardRef<HTMLDivElement, GridCellProps>(function GridCell(pro
195195
formattedValue: forcedFormattedValue,
196196
});
197197
cellParams.api = apiRef.current;
198-
cellParams.isEditable = useGridSelector(apiRef, () => apiRef.current.isCellEditable(cellParams));
198+
199+
// Subscribe to changes of the `isCellEditable` API result to ensure cells re-render.
200+
// We don't use the result.
201+
// Subscription will trigger a new call of `getCellParamsForRow` above which will recompute the correct value of `isEditable`.
202+
// This is to ensure both of the following cases work:
203+
// - https://github.com/mui/mui-x/issues/19732
204+
// - https://github.com/mui/mui-x/issues/20143
205+
useGridSelector(apiRef, () => apiRef.current.isCellEditable(cellParams));
199206

200207
const isSelected = useGridSelector(apiRef, () =>
201208
apiRef.current.unstable_applyPipeProcessors('isCellSelected', false, {
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import * as React from 'react';
2+
import { createRenderer, screen } from '@mui/internal-test-utils';
3+
import { DataGrid, gridClasses, GridColDef, GridRowsProp } from '@mui/x-data-grid';
4+
import { getCell } from 'test/utils/helperFn';
5+
6+
describe('<DataGrid /> - Cell editable state', () => {
7+
const { render } = createRenderer();
8+
9+
function expectCellEditable(rowIndex: number, colIndex: number) {
10+
expect(getCell(rowIndex, colIndex)).to.have.class(gridClasses['cell--editable']);
11+
}
12+
13+
function expectCellNotEditable(rowIndex: number, colIndex: number) {
14+
expect(getCell(rowIndex, colIndex)).not.to.have.class(gridClasses['cell--editable']);
15+
}
16+
17+
// based on https://github.com/mui/mui-x/issues/19732
18+
it('should update cell editable state when `isCellEditable` prop changes', async () => {
19+
const rows: GridRowsProp = [
20+
{ id: 1, name: 'A', age: 20 },
21+
{ id: 2, name: 'B', age: 21 },
22+
];
23+
24+
const columns: GridColDef[] = [
25+
{ field: 'name', editable: true, width: 150 },
26+
{ field: 'age', type: 'number', editable: true, width: 150 },
27+
];
28+
29+
function GridWithState() {
30+
const [editable, setEditable] = React.useState(true);
31+
const [editMode, setEditMode] = React.useState<'row' | 'cell'>('cell');
32+
33+
return (
34+
<div style={{ width: 400, height: 400 }}>
35+
<button onClick={() => setEditable((s) => !s)}>toggle-editable</button>
36+
<button onClick={() => setEditMode((s) => (s === 'row' ? 'cell' : 'row'))}>
37+
toggle-mode
38+
</button>
39+
<DataGrid
40+
rows={rows}
41+
columns={columns}
42+
editMode={editMode}
43+
isCellEditable={(params) => params.row.age % 2 === 0 && editable && editMode === 'cell'}
44+
/>
45+
</div>
46+
);
47+
}
48+
49+
const { user } = render(<GridWithState />);
50+
51+
// Initially: editMode = 'cell', editable = true → row 0 cells editable
52+
expectCellEditable(0, 0);
53+
expectCellEditable(0, 1);
54+
expectCellNotEditable(1, 0);
55+
56+
// Toggle editable → should remove editable class immediately
57+
await user.click(screen.getByRole('button', { name: 'toggle-editable' }));
58+
expectCellNotEditable(0, 0);
59+
expectCellNotEditable(0, 1);
60+
61+
// Toggle mode to 'cell' again (it was set to false only) by switching mode away and back
62+
await user.click(screen.getByRole('button', { name: 'toggle-mode' }));
63+
// Now editMode = 'row' → should not be editable regardless of row parity
64+
expectCellNotEditable(0, 0);
65+
66+
await user.click(screen.getByRole('button', { name: 'toggle-mode' }));
67+
// Back to 'cell' but editable state is false → still not editable
68+
expectCellNotEditable(0, 0);
69+
});
70+
71+
// based on https://github.com/mui/mui-x/issues/20143
72+
it('should update cell editable state when `colDef.editable` changes', async () => {
73+
const rows: GridRowsProp = [{ id: 1, firstName: 'Jon', lastName: 'Snow', age: 14 }];
74+
75+
function Component() {
76+
const [isEditable, setIsEditable] = React.useState(true);
77+
const cols: GridColDef[] = [
78+
{ field: 'id', width: 100 },
79+
{ field: 'firstName', width: 150, editable: isEditable },
80+
{ field: 'lastName', width: 150, editable: true },
81+
];
82+
83+
return (
84+
<div style={{ width: 500, height: 400 }}>
85+
<button onClick={() => setIsEditable((p) => !p)}>toggle-coldef</button>
86+
<DataGrid rows={rows} columns={cols} isCellEditable={() => true} />
87+
</div>
88+
);
89+
}
90+
91+
const { user } = render(<Component />);
92+
93+
// firstName column is editable initially
94+
expectCellEditable(0, 1);
95+
96+
// Toggle colDef.editable to false → class should be removed immediately
97+
await user.click(screen.getByRole('button', { name: 'toggle-coldef' }));
98+
expectCellNotEditable(0, 1);
99+
});
100+
});

0 commit comments

Comments
 (0)