LearnNewsExamplesServices
Frontmatter
id18941
titleA row-selecting grid starts no cell edit from the keyboard
stateClosed
labels
bugaigridaccessibility
assigneesneo-fable
createdAtSep 19, 2026, 12:33 PM
updatedAtSep 19, 2026, 3:08 PM
githubUrlhttps://github.com/neomjs/neo/issues/18941
authorneo-fable
commentsCount0
parentIssue18762
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtSep 19, 2026, 3:08 PM

A row-selecting grid starts no cell edit from the keyboard

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

Context

Leaf of #18762. RowModel is the grid's default selection model (learn/guides/datahandling/Grids.md, "Selection Models"). With cellEditing: true and that default, a keyboard user cannot start an edit at all, and after a pointer-started edit the arrow keys lose their place. Measured in real Chromium (Playwright e2e rig, dev@7db03e862c), not derived.

The Problem

Fixture test/playwright/component/apps/grid-cell-editing, grid #grid-cell-editing-pooled, view.selectionModel switched to RowModel at runtime:

Step Editors Selected rows
click record 3's c3 0 record 3
Enter 0 record 3
F2 0 record 3
ArrowDown, then Enter 0 record 4
double-click record 3's c3 (control) 1 none — the two clicks toggled the row on and off
type, Enter (commit) 0 none, focus on the View
ArrowDown 0 record 2 — the arrows restart from the store's first record

So under the default model the feature is pointer-only (WCAG 2.1.1), and a committed edit on row 250 sends the next ArrowDown to row 2.

The public example hides this: examples/grid/cellEditing offers only the four cell-selecting models. The guide says "Enter or F2 edits the selected cell" and never names the precondition.

Source authority: D#17832's interaction convergence — "double-click, F2 and Enter activate an editable active target" and "Selection is neither necessary nor sufficient across Row/Cell selection models". The shipped keyboard path takes its target from the cell selection alone.

The Architectural Reality

  • src/grid/plugin/CellEditing.mjs#editSelectedCell (:178–184) reads view.selectedCells[0]; grid.View#selectedCells (src/grid/View.mjs:137) returns [] for a model without selectsCells. Enter and F2 on the View both route there.
  • #onEnterKey (:388–404) re-selects the edited cell after a commit only when selectionModel.selectsCells; a row-only model gets no anchor back.
  • src/selection/grid/RowModel.mjs:75: with no selected row the nav keys start from store.getAt(0).
  • A row's selection toggles per click, so a double-click deselects the row it edits (RowModel#onRowClick).
  • The born-suspended start (startEdit(record, dataField, true) + view.scrollByRows / scrollByColumns, the Tab path in #onTabKey) already embodies an edit whose cell is outside the mounted window.

The Fix

In src/grid/plugin/CellEditing.mjs:

  1. The keyboard target follows the selection: a selected cell wins (unchanged). With a selected row and no selected cell → that row's record, in the column of the grid's last edit while that column is still editable, else the record's first editable column in arrow-walk order. A target out of sight is scrolled into it through the Tab path (one helper, editInSight, for both).
  2. A model that selects rows follows the edit by its row: startEdit selects the edited record's row when it is not selected, so every end (Enter, Escape, Tab past either end) leaves it selected.

Scope amendment (2026-09-19, measured while implementing): rule 2 was first scoped to models that select rows and no cells. A probe on the public example under "Cell & Row" falsified that guard: today Tab into the next record selects the cell and leaves no row selected, and a double-click edit ended by Escape leaves nothing selected; with the rule applied to every row-selecting model the cell's row follows the Tab and stays as the anchor after Escape, where Enter edits it again. The guard is gone, and two arms on the public example pin it.

New spec test/playwright/e2e/grid/CellEditingRowModel.spec.mjs (sibling of the six CellEditing*.spec.mjs); the fixture's gridDriver.mjs gains the runtime model switch, so no existing witness's fixture default changes. The guide's "Starting an Edit" names the rule per model family.

Contract Ledger

Target surface Source of authority Proposed behavior Fallback Docs Evidence
Enter / F2 on grid.View with cellEditing D#17832 "active target"; CellEditing#editSelectedCell a selected row and no selected cell: edits that row's record (last-edit column, else first editable), scrolled into sight neither a cell nor a row selected: starts nothing Grids guide, "Starting an Edit" e2e arms, red on dev
an edit under a row-selecting model (RowModel, CellRowModel, CellColumnRowModel) CellEditing#onEnterKey re-select for cell models the edited record's row is selected from the start of the edit, and grid.View fires select for it — "Ending an Edit" e2e arms: ArrowDown after a commit selects the next record; "Cell & Row" on the public example

Decision Record impact

none (aligned with D#17832's active-target principle).

Acceptance Criteria

  • Under RowModel, Enter and F2 on a selected row each start an edit on that record; with no selected row nothing starts and no error is logged.
  • The column is the last edit's while still editable, else the record's first editable one; a target outside the mounted column window is scrolled into sight and embodied, focused.
  • After Enter, Escape, and a Tab that ends the edit at an end of the store (witnessed: Shift+Tab before the first cell) under RowModel, the edited record's row is selected, and ArrowDown selects the next record — also for an edit started by double-click.
  • Under a model that selects cells and rows, Tab into the next record selects its cell and its row, and a double-click edit ended by Escape keeps its row selected.
  • The existing grid e2e and component arms stay green.
  • The new arms are red on dev and mutation-pinned; --repeat-each=20 clean.
  • The Grids guide states the keyboard rule, with every behavioural claim mapped to an arm.

Out of Scope

  • Changing RowModel's click-toggle itself (a double-click deselecting the row it lands on).
  • Type-to-replace activation (D#17832 keeps it outside the first epic).
  • Adding RowModel to the public example's radio group.

Avoided Traps

  • Forcing a cell-selecting model whenever cellEditing is on: it overrides an app's selection contract to fix an activation path.
  • Documenting the precondition only: the default configuration would stay keyboard-inaccessible.

Related

#18762 (epic) · #18861, #18883 (Tab paths this reuses) · #18925 (the previous keyboard-reachability leaf) · D#17832

Live latest-open sweep: checked latest 20 open issues at 2026-09-19T10:32:37Z; no equivalent found. A2A claim sweep: latest 12 messages (all read-states) at 10:33Z; no overlapping claim. MC sweep: "grid cellEditing keyboard user cannot start an edit row selection model RowModel Enter F2 selected row no selected cell", 6 results, no prior decision found; KB ticket sweep: only the 2024 table.plugin.CellEditing tickets (#6154, #6164). Own-assignment sweep: 1 open (#15252), not overlapping.

Origin Session ID: 2c9503fc-beef-4d80-939b-7d616601cd97 Retrieval Hint: "RowModel keyboard activation cell editing active target last edit column"