Skip to main content
← Back to UX & Frontend

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: pointer on 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) or onRowDoubleClick (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:

  • onRowDoubleClick for navigation on a table with cursor: pointer rows — users expect single-click
  • DataGrid cell focus (blue outline on click) on non-editable cells — feels like "selection" not navigation
  • disableRowSelectionOnClick without onRowClick — 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 onClick handler for its primary action
  • Each interactive child region wraps its content in a stopPropagation boundary
  • 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 renderCell that 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 renderCell set display: '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:focus with outline only on .MuiDataGrid-cell--editable:focus

Cursor

  • Rows with navigation: cursor: pointer on the row
  • Editable cells: cursor: text (text fields), cursor: pointer (dropdowns/toggles), or cursor: 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 renderCell must set display: 'flex' to opt into flex cell rendering (vertical centering)
  • editable: true columns without a propagation-stopping renderCell will fire onRowClick on single-click
  • disableRowSelectionOnClick only prevents checkbox toggling — it does NOT prevent onRowClick or cell focus
  • Cell focus is enabled by default — suppress it on non-editable cells via sx overrides

React Table / TanStack Table:

  • Row onClick is set on the <tr> — interactive cells need stopPropagation on 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 onRowClick is 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.

  1. Container Inventory — Table:
Component Primary Action Gesture Interactive Children Propagation Alignment Status

Status values: Clean, Gesture Mismatch, Propagation Leak, Fragile Filter, Alignment Issue

  1. Interaction Model Issues — For each gesture-affordance mismatch: file:line, what the user expects vs. what happens, and the fix (e.g., change onRowDoubleClick to onRowClick)
  2. Propagation Issues — For each leak or fragile pattern: file:line, the parent handler, the child that should absorb clicks, and the specific refactor
  3. Visual Issues — For each alignment, focus, or cursor problem: file:line, what's wrong, and the CSS/prop fix
  4. Positive Findings — Components with correct interaction models that can serve as reference implementations

Need help applying this to a real product?

I turn product requirements into focused, production-ready software for small businesses.