I have a MUI Data Grid where the user is allowed to cell edit values.
One of the cells render a MUI Dialog via renderEditCell
. In that dialog there are several input fields (MUI TextField) which I want to be able to tab through.
My problem now is, that as soon as I tab to the next field, the dialog is closed because the next cell of the data grid is focused.
I've tried to ignore the tab key in onCellEditStop
with event.defaultMuiPrevent = true
. With that, the dialog no longer closes, but the focus doesn't switch to the next input field. As far as I understood, I think this is because the event is first handled in the parent and thus the default is also prevented within the dialog.
I have a minimal reproducible example here (relevant code in onCellEditStop
is commented out).
How can I fix this? Is there a different approach?
You can attach onKeyDown event on Dialog
component and check if pressed key code is Tab
then prevent the event bubbling up to the parent.
onKeyDown={(e) => {
if (e.code === "Tab") {
e.stopPropagation();
}
}}
Here's the complete code:
CustomField.tsx
import Dialog from "@mui/material/Dialog";
import DialogContent from "@mui/material/DialogContent";
import TextField from "@mui/material/TextField";
import { useGridApiContext } from "@mui/x-data-grid";
import React from "react";
export default function CustomField({ id, field, row }) {
const [open, setOpen] = React.useState(true);
const apiRef = useGridApiContext();
const handleClose = () => {
setOpen(false);
apiRef.current.stopCellEditMode({ id, field });
};
return (
<Dialog
open={open}
onClose={handleClose}
fullWidth
maxWidth="md"
onKeyDown={(e) => {
if (e.code === "Tab") {
e.stopPropagation();
}
}}
>
<DialogContent>
<TextField
fullWidth
label="Min"
name="min"
size="small"
sx={{ m: 1 }}
type="number"
defaultValue={row.validationDef?.min || ""}
/>
<TextField
fullWidth
label="Max"
name="max"
size="small"
sx={{ m: 1 }}
type="number"
/>
</DialogContent>
</Dialog>
);
}
App.tsx
import {
DataGrid,
GridColDef,
GridRenderEditCellParams,
} from "@mui/x-data-grid";
import Alert from "@mui/material/Alert";
import CustomField from "./CustomField";
export default function App() {
const columns: GridColDef[] = [
{
field: "attribute",
headerName: "Attribute",
editable: true,
},
{
field: "customField",
headerName: "Dialog Field",
flex: 1.5,
renderEditCell: (params: GridRenderEditCellParams) => (
<CustomField {...params} />
),
editable: true,
},
{
field: "type",
type: "singleSelect",
headerName: "Type",
editable: true,
valueOptions: [
"COMPLEX",
"BOOLEAN",
"STRING",
"NUMBER",
"DATE",
"DATETIME",
],
},
];
const rows = [
{
id: 1,
attribute: "a name",
customField: "double click me",
type: "COMPLEX",
},
];
return (
<div>
<Alert severity="info">
<ul>
<li>Double click on the value in the "Dialog Field" column</li>
<li>Click into the first input field</li>
<li>Try to tab to the next input field</li>
</ul>
</Alert>
<DataGrid
rows={rows}
columns={columns}
rowHeight={40}
hideFooter={true}
/>
</div>
);
}