LearnNewsExamplesServices
Frontmatter
id18954
titleArrow navigation leaves a suspended grid edit unresolved
stateClosed
labels
bugaigridaccessibility
assigneesneo-gpt
createdAtSep 19, 2026, 2:38 PM
updatedAtSep 19, 2026, 3:58 PM
githubUrlhttps://github.com/neomjs/neo/issues/18954
authorneo-gpt
commentsCount0
parentIssue18762
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 19, 2026, 3:58 PM

Arrow navigation leaves a suspended grid edit unresolved

Closed v13.2.0/archive-v13-2-0-chunk-22 bugaigridaccessibility
neo-gpt
neo-gpt commented on Sep 19, 2026, 2:38 PM

Context

Leaf of #18762. The GPT return evaluation reproduced a missing part of D17832 OQ2 / criterion 5: an arrow while the editor is suspended is an explicit leave-edit intent. A valid draft commits before navigation; an invalid draft blocks navigation and reprojects.

Measured in headed Chromium 153 at dev@ab2e7cd17c4279cc8d5cb36b2ecd119f06f00a60, on the existing test/playwright/component/apps/grid-cell-editing fixture, both its stock 40-column/400-row shape and a 120-column scale variant. No page or console errors in the confirming runs.

The Problem

Click record 3's c3, press Enter, type arrow-draft, then set the pooled View's vertical scroll to 5000 so the editor unmounts. Press ArrowDown while the View holds focus.

Case Observed Required
valid c3 draft selection moves record 3 → 4; record 3 still holds r3c3; its edit session survives commit the draft, end that session, then navigate
required c5 cleared to an invalid draft selection still moves 3 → 4; editor reappears incidentally as the row comes back, without focus keep selection, reproject/focus the invalid editor and announce validation
Tab controls, same states valid commits and edits the adjacent cell; invalid reprojects, keeps selection and focuses its editor already correct

Keyboard activation avoids the known double-click selection-toggle confound. Reads follow rendered selection and an App-Worker round trip, not a same-tick focus guess.

The Architectural Reality

  • CellEditing#getViewKeys owns Enter, Escape, F2 and Tab; its onTabKey already owns commit-if-valid and invalid-session reprojection.
  • selection.grid.CellModel and RowModel consume arrows. Their shared BaseModel#hasEditorFocus checks the event's physical editor path; a suspended editor has no such path.
  • util.KeyNavigation#onKeyDown invokes every matching registration; appending another arrow handler would not establish ordering or veto the selection handler.
  • CellEditing#onSelectionChange retires owed refocus; it does not resolve the edit terminal.

The Fix

Add a shared navigation admission step before the existing selection models move. Keep edit validation, commitment and invalid-session reprojection in CellEditing. The mounted editor/picker retains its own key handling; with no suspended edit, navigation is unchanged.

Reuse the plugin's invalid-Tab reveal/focus behavior rather than copy field validation into each selection model. Extend the existing grid editing tests and document the suspended-arrow rule.

Prescription checked: src/grid/plugin/CellEditing.mjs owns edit terminals; src/selection/grid/BaseModel.mjs owns the shared selection-side gate. CellModel.mjs and RowModel.mjs are the existing arrow consumers. No new module or subsystem is prescribed. Structure map: N/A — Engine behavior, existing owners.

Contract Ledger

Surface Authority Behavior Edge case Docs Evidence
View arrows during a suspended edit D17832 OQ2 / criterion 5 resolve a valid edit before existing model navigation invalid blocks navigation and restores editor focus; no session stays unchanged plugin/gate JSDoc; Grids guide real-key, worker-state e2e controls
Mounted editor keys existing BaseModel#hasEditorFocus and field key handlers editor retains its keys picker and number-field controls stay valid preserve current docs existing field/grid coverage plus focused control

Acceptance Criteria

  • For a valid edit suspended by vertical or horizontal pooling, each applicable arrow direction commits exactly once, destroys the old editor/session, then uses the selection model's existing navigation.
  • For an invalid suspended draft, the arrow changes neither record nor logical selection; its cell is brought into sight and the same editor receives focus with validation visible.
  • Both cell- and row-selecting models obey the terminal; derived row/column combinations keep their normal navigation semantics.
  • Mounted input/picker keys, no-edit navigation, neutral scrolling and suspended Tab/Shift+Tab remain unchanged.
  • CI-executed real-key coverage is red before the fix and mutation-sensitive to omission of the terminal or invalid-draft veto; the guide states the delivered behavior.

Out of Scope

Pointer/focus-leave policy while suspended; RowModel activation and post-edit anchoring (#18941); column-highlight synchronization (#18950); fixture geometry (#18951); a general key-dispatch rewrite.

Avoided Traps

  • Adding a competing View arrow listener and depending on registration order.
  • Committing on every selectionChange: that occurs after the mutation and conflates pointer, programmatic and keyboard changes.
  • Raising a timer or waiting for containsFocus; the logical session and event target already name the state.

Decision Record: NOT_NEEDED, preserved from D17832. Decision Record impact: none.

Related

#18762 · #18883 · #18941 · #18950

Live latest-open sweep: latest 20 open issues read immediately before filing on 2026-09-19; no equivalent. All-state suspended-arrow/navigation searches and the relevant closed leaves were read. A2A all-state recency sweep found no competing claim; Mnemosyne explicitly left this terminal to me. MC rationale sweep recovered the Tab and RowModel work, not a decision against the settled arrow terminal. Own assignments: three unrelated open lanes, no overlap. KB ticket retrieval was inconclusive and did not replace the live sweep.

Origin Session ID: 553fd0f7-80d4-4937-884a-7dfcad72e19a Retrieval Hint: suspended grid arrow navigation valid uncommitted invalid draft Tab control