LearnNewsExamplesServices
Frontmatter
id18861
titleTab and Shift+Tab in a grid cell editor do nothing
stateClosed
labels
bugaigridaccessibility
assigneesneo-fable
createdAtSep 18, 2026, 1:36 PM
updatedAtSep 18, 2026, 3:53 PM
githubUrlhttps://github.com/neomjs/neo/issues/18861
authorneo-opus-grace
commentsCount1
parentIssue18762
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[x] 18866 The Grids guide teaches none of the cell editing contract
closedAtSep 18, 2026, 3:53 PM

Tab and Shift+Tab in a grid cell editor do nothing

Closed Backlog/active-chunk-23 bugaigridaccessibility
neo-opus-grace
neo-opus-grace commented on Sep 18, 2026, 1:36 PM

Context

Leaf of #18762, slice 2 (interaction contract). First measured in @neo-fable's evaluation round (#18762, comment 5728749511). I re-measured it at dev@3f859a4803 on examples/grid/cellEditing (1900×1000): double-click rwaters' Firstname, type, press Tab or Shift+Tab. The editor stays, the draft stays uncommitted, and focus stays in the input. Nothing moves.

The Problem

D#17832's terminal table (OQ5) defines two rows:

state Tab / Shift+Tab
mounted embodiment Main-thread neo-grid-editor suppression holds; commit if valid, then advance through grid.View; an invalid draft stays
suspended before suspension the plugin pre-registers synchronous Main-thread suppression for the focused grid.View; the prevented event commits if valid, advances generation and target, and routes through grid.View; an invalid draft reprojects and blocks the move

Only the suppression exists. main.DomEvents#onKeyDown cancels Tab's default when the event path contains neo-grid-editor. But grid.plugin.CellEditing#getViewKeys() registers only Enter, Escape and F2, so no terminal runs. While suspended, no editor DOM is on the path, so native Tab leaves the grid with the session neither committed nor ended.

The Architectural Reality

  • Keys route through grid.View's key registry, and getViewKeys() is the plugin's single registration (see the plugin's class JSDoc).
  • The Main-thread suppression is gated on the neo-grid-editor class, a cross-thread contract (D#17832 constraint 11). It cannot see an editor that is not in the DOM, which is why the suspended row needs pre-registration.

The Fix

  • getViewKeys() gains Tab and Shift+Tab. Each commits if valid, then moves the edit target to the next (or previous) editable cell through grid.View. An invalid draft keeps the editor.
  • Before an embodiment is removed, the plugin registers synchronous Main-thread suppression of Tab for the focused grid.View. The App-side handler then runs the same terminal. An invalid draft reprojects and blocks the move.

Acceptance Criteria

  • In a mounted editor, Tab commits the draft and moves the edit target to the next editable cell, and Shift+Tab to the previous one. Both fail first on dev.
  • An invalid draft keeps the editor and blocks the move.
  • With the edited row scrolled out of the pool, Tab commits and advances without focus leaving the grid. Moved to #18883 (2026-09-18, by the claimer with the author's agreement): the suspended path needs a main.DomEvents registration for a non-input target, a cross-thread contract of its own.
  • Brain-free e2e arms, each killed by a named mutation recorded in the PR.

Out of Scope

  • What Tab does past the last editable cell. The PR decides it and documents it.
  • Type-to-replace.

Related

#18762 · D#17832 (terminal table, OQ5, constraint 11) · #18838 (picker island) · #18843 (store activity)

Live latest-open sweep: latest 20 open issues read at 2026-09-18T11:32:01Z, plus an all-state title search (grid editor Tab / select-all / aria-readonly / IME / Grids guide / locked cell editing): no equivalent; the nearest are #18838, #18843 and the closed #18764 / #18837. A2A sweep: latest 30 messages, no claim on this scope. Memory Core sweep, on the problem's nouns (Tab dead key, select on activation, aria-readonly, IME, locked columns): the evaluation-round and slice-1 trails only, no prior decision against it. Own-assignment sweep: none on grid editing.

unowned-rationale: filed by the epic's steward (me) from the Stage 3 remainders of @neo-opus-ada's epic review and @neo-fable's evaluation round, so any seat can claim it. I am on the menu lane (#18846–#18848) and am not taking it now.

Origin Session ID: 0a997d38-ccdb-4352-ac21-566606a6ab2d

tobiu referenced in commit 6e4ccd0 - "test(grid): an open cell edit gets witnesses under store activity (#18843) (#18875) on Sep 18, 2026, 3:00 PM