Open a detail panel from a row
Show the active row in a side panel that stays open while you move through the grid.
A side panel that shows one record needs two things: a gesture that opens it and state that holds the row. The panel in the demo does not block the grid. It slides in from the right and shows the active row. Keyboard focus stays in the grid, so the arrow keys keep moving the active cell and the panel follows. Click a cell, or press Ctrl+Shift+Enter on the active cell:
Click a cell, or press Ctrl+Shift+Enter on the active cell, to show its row. The panel follows the active cell while you move with the arrow keys. Escape closes it when focus is inside the panel.
Open the panel
The panel is an aside beside the grid. It has role="complementary" and an aria-label, and it carries no modal semantics: no overlay, no focus trap, no scroll lock. A CSS transform slides it in. While it is closed, the inert attribute removes it from the tab order and the accessibility tree.
Use a dialog only when the task must stop until the user answers. A dialog traps focus and blocks the grid, so it breaks the flow of moving down a list.
Mouse
onCellClick fires for a click on any data cell. The checkbox toggle fires onCellClick too, so compare ctx.columnId to skip the checkbox column:
<DataGrid
data={orders}
columns={columns}
onCellClick={(ctx) => {
if (ctx.columnId !== "selected") setPanelOpen(true);
}}
/>A click on the row marker selects the row and fires no click callback. Read onSelectionChange with details.getRowIds() to react to it.
Keyboard
Enter, F2, and a double-click edit the cell, so the open gesture needs its own key. A custom command reads the active row from ctx.row, and keymap binds it. Declare the command name once so keymap accepts it. Ctrl+Enter is taken by scrollActiveIntoView, so the example binds mod+shift+Enter:
declare module "@/components/data-grid/types" {
interface GridActions {
openRow: true;
}
}
const commands = {
openRow: {
label: "Open row",
when: (ctx) => ctx.row !== undefined,
run: () => setPanelOpen(true),
},
};
<DataGrid
data={orders}
columns={columns}
commands={commands}
keymap={{ openRow: ["mod+shift+Enter"] }}
/>when runs the command only while a cell is active. After Escape the selection is empty, but the active cell stays, so the command still opens the panel.
Follow the active row
onActiveCellChange fires each time the active cell moves. The panel reads next.rowId and clears itself on null:
<DataGrid
data={orders}
columns={columns}
onActiveCellChange={(next) => setPanelRowId(next?.rowId ?? null)}
/>Look the row up by id in your own data. The event carries the cell reference, not the row.
Focus and Escape
Focus stays in the grid while the panel is open, so Escape in the grid keeps its grid meaning. Escape closes the panel when focus is inside it, for example after Tab onto the close button. Closing the panel returns focus to the grid.
Related
- Edit a row with a form: put a form in the panel and save it as one undoable change.
- Events & state:
onActiveCellChange: the callback that moves the panel.