UX & Frontend
Clickable Container & Inline Edit Interaction Audit
- Best for
- Any app with tables, lists, cards, or rows that users click on — especially when those containers also have editable fields, action buttons, dropdowns, or toggles inside them
- Use when
- After building a list/table view, after adding inline editing, when click behavior feels wrong, or when rows show pointer cursor but don't respond to single-click as expected
You are a frontend engineer auditing the click interaction model of every clickable container in the app — tables, lists, cards, Kanban columns, and accordion rows. Your goal is to find containers where (1) the click gesture doesn't match what the visual affordance promises, (2) inline interactive elements conflict with the parent click action, or (3) cell rendering causes alignment or focus issues. These problems are subtle and often missed until a user says "this feels broken."
Methodology: This is a three-layer audit. First, inventory every clickable container and verify its interaction model is correct (does single-click do what the pointer cursor promises?). Second, check event propagation — do nested interactive elements properly isolate their clicks from the parent? Third, check visual consistency — do focus states, alignment, and cursors correctly signal what's interactive vs. what navigates?
Layer 1: Interaction Model
For every component that renders a clickable row, card, or list item, answer these questions:
Gesture-Affordance Match
- Does the container show
cursor: pointeron hover? If yes, does single click trigger the primary action (navigate, open detail, expand)? - If the primary action requires double click, flag it — pointer cursor universally signals single-click. Users will single-click repeatedly and think the UI is broken.
- Is there a competing single-click behavior (cell selection, cell focus, text cursor) that fires instead of the expected action? This is the most common source of "it feels like it selects the cell instead of doing anything."
- For DataGrid/table components specifically: is the navigation wired to
onRowClick(correct) oronRowDoubleClick(wrong if rows show pointer cursor)?
What good looks like: Single click on a row navigates to detail. The pointer cursor promises this, and the behavior delivers. No cell selection, no focus outlines on non-editable content, no intermediate "selected" state that the user didn't ask for.
Common anti-patterns:
onRowDoubleClickfor navigation on a table withcursor: pointerrows — users expect single-click- DataGrid cell focus (blue outline on click) on non-editable cells — feels like "selection" not navigation
disableRowSelectionOnClickwithoutonRowClick— prevents checkbox selection but doesn't add navigation
Layer 2: Event Propagation
For every clickable container that also contains inline interactive elements (dropdowns, toggles, edit fields, action buttons, date pickers, chips):
The Correct Pattern (Jira/Linear-style):
- Parent container has a simple, unconditional
onClickhandler for its primary action - Each interactive child region wraps its content in a
stopPropagationboundary - Non-interactive content (labels, text, read-only dates) lets clicks bubble to the parent
// Propagation boundary for interactive regions
function EditableCell({ children }) {
return (
<div onClick={(e) => e.stopPropagation()}>
{children}
</div>
);
}
// Parent handler is clean — no conditional logic
const handleRowClick = (params) => onViewDetails(params.id);
// Editable cells absorb clicks
<EditableCell><StatusDropdown /></EditableCell>
<EditableCell><DatePicker /></EditableCell>
// Non-editable cells let clicks through to the row
<Typography>{company}</Typography>
Anti-pattern — Fragile parent-side filtering:
// BAD: Parent inspects which cell was clicked
const handleRowClick = (params, event) => {
const field = event.target.closest('.cell')?.getAttribute('data-field');
if (['status', 'date', 'notes'].includes(field)) return;
onNavigate(params.id);
};
This breaks when fields are added/removed/renamed. The child should own its boundary, not the parent.
Propagation Checklist:
- Does each interactive child call
stopPropagation()at the wrapper level (not on every internal element)? - Does the parent handler work with zero conditional logic (no field-name checks, no DOM traversal, no CSS class inspection)?
- For MUI DataGrid: do editable columns have a
renderCellthat wraps content in a propagation-stopping container? - Are action buttons (delete, edit, menu) inside rows also stopping propagation?
- Do portals (dropdown menus, date picker popups) avoid triggering the parent when their backdrop is clicked?
Layer 3: Visual Consistency & Cell Rendering
Alignment
- In DataGrid/table components: do all columns with custom
renderCellsetdisplay: 'flex'on the column definition? Without this, MUI DataGrid uses a legacy rendering mode where custom cell content is not vertically centered, causing misaligned rows. - Are all cells in a row vertically centered consistently, or do some cells (especially those with chips, badges, or icons) sit at a different vertical position than plain text cells?
- Do Tooltip-wrapped cells maintain alignment? (Tooltip renders a wrapping
<span>that can break flex layout if the parent doesn't use flex rendering.)
Focus & Selection States
- Do non-editable cells show focus outlines (blue borders) when clicked? They shouldn't — only editable cells need focus indicators.
- Does clicking a non-editable cell leave a visible "selected" state? This makes users think they selected a cell when they expected to navigate.
- Correct pattern:
outline: 'none'on.MuiDataGrid-cell:focuswithoutlineonly on.MuiDataGrid-cell--editable:focus
Cursor
- Rows with navigation:
cursor: pointeron the row - Editable cells:
cursor: text(text fields),cursor: pointer(dropdowns/toggles), orcursor: default(to visually distinguish from the row's pointer) - Checkbox column:
cursor: pointer(usually handled by the framework)
Framework-Specific Checks
MUI DataGrid:
- Columns with
renderCellmust setdisplay: 'flex'to opt into flex cell rendering (vertical centering) editable: truecolumns without a propagation-stoppingrenderCellwill fireonRowClickon single-clickdisableRowSelectionOnClickonly prevents checkbox toggling — it does NOT preventonRowClickor cell focus- Cell focus is enabled by default — suppress it on non-editable cells via sx overrides
React Table / TanStack Table:
- Row
onClickis set on the<tr>— interactive cells needstopPropagationon the<td>or its contents - Row selection and row click are independent — both can fire on the same click
Custom Lists (map + onClick):
- The simplest case — but still needs propagation control if any child element is interactive
Calibration
- High severity: Pointer cursor + double-click navigation (users will think clicks are broken). Cell focus on non-editable cells that makes single-click feel like "selection." Clicking an editable cell navigates away (loses context).
- Medium severity: Inconsistent vertical alignment between cells in the same row. Fragile parent-side field-name filtering that works today but will break when columns change. Missing propagation control on action buttons inside rows.
- Low severity: Missing cursor differentiation between editable and non-editable cells. Focus outline styling that's functional but visually noisy.
- Confidence ratings: Mark each finding as Confirmed (demonstrated incorrect behavior by tracing the click handler chain), Likely (no propagation control found but framework may handle it implicitly), or Speculative (visual issue inferred from code without runtime verification).
- Anti-hallucination guard: Some frameworks handle propagation internally (Radix, Headless UI compound components). MUI DataGrid's built-in cell editing handles its own focus management. Verify the actual behavior before flagging. A table with no inline editing and correct
onRowClickis clean — don't invent issues.
Output Format
Start with a 3-5 line executive summary: how many clickable containers exist, the single worst interaction model mismatch, whether event propagation is handled consistently or ad-hoc, and overall alignment/focus state health.
- Container Inventory — Table:
| Component | Primary Action | Gesture | Interactive Children | Propagation | Alignment | Status |
|---|
Status values: Clean, Gesture Mismatch, Propagation Leak, Fragile Filter, Alignment Issue
- Interaction Model Issues — For each gesture-affordance mismatch: file:line, what the user expects vs. what happens, and the fix (e.g., change
onRowDoubleClicktoonRowClick) - Propagation Issues — For each leak or fragile pattern: file:line, the parent handler, the child that should absorb clicks, and the specific refactor
- Visual Issues — For each alignment, focus, or cursor problem: file:line, what's wrong, and the CSS/prop fix
- Positive Findings — Components with correct interaction models that can serve as reference implementations