Back to Plate

Editor Selection Navigation Coverage

docs/slate-v2/selection-navigation-coverage.md

53.0.86.4 KB
Original Source

Editor Selection Navigation Coverage

Use this when a Slate v2 bug touches selection, navigation, focus, hidden DOM, multi-root behavior, synced roots, editable voids, clipboard-after-selection, delete-after-selection, or insert-break-after-selection.

This matrix is generic editor law. It is not a content-root checklist.

How To Use

For each slate-patch bug, pick the smallest honest slice that covers the bug class. Add one sibling topology when the same owner likely handles it. Record skipped rows when the final handoff could otherwise imply broader coverage.

Do not run the whole matrix for every bug. Do not claim full coverage unless the whole relevant command x direction x topology x state surface is actually green.

Command Families

FamilyRows
Character movementArrowLeft, ArrowRight, ArrowUp, ArrowDown
Character extensionShift+ArrowLeft, Shift+ArrowRight, Shift+ArrowUp, Shift+ArrowDown
Word movementOption+ArrowLeft, Option+ArrowRight
Word extensionShift+Option+ArrowLeft, Shift+Option+ArrowRight
Document or line edge movementCmd+ArrowLeft, Cmd+ArrowRight, Cmd+ArrowUp, Cmd+ArrowDown, Home, End
Document or line edge extensionShift+Cmd+ArrowLeft, Shift+Cmd+ArrowRight, Shift+Cmd+ArrowUp, Shift+Cmd+ArrowDown, Shift+Home, Shift+End
Mouse selectionclick, click-drag, drag top-to-bottom, drag bottom-to-top, drag starting from unfocused editor
Follow-up mutationtype, Backspace, Delete, Enter, paste after movement or range selection
Follow-up statecopy, undo, redo, focus another root then return

Directions

DirectionWhat It Proves
forward intocaret or focus enters the target surface correctly
backward intoreverse navigation is not a mirror-image afterthought
forward outselection can leave the surface without getting trapped
backward outreverse exit preserves anchor/focus semantics
up/down intovisual-line movement maps to the right model point
up/down outvertical movement exits nested or hidden surfaces cleanly
across one boundarythe local owner works
across many boundariesrepeated owner handoff works without double selection or skipped nodes

Topologies

TopologyExamples
plain blocksnormal paragraphs/headings/lists
inline boundarieslinks, inline voids, marks split across leaves
block voidimage/media-like blocks
editable voidvoid shell with editable descendants
hidden DOMaccordion, tabs, collapsible, popover-like hidden content
DOM coverage boundaryboundary, model-backed, materialize policies
content rootroot owned by a block shell
multi-rootheader/body/footer or sibling editable roots
synced rootsame model state mounted in multiple places
virtualized DOMpartial DOM or page/block virtualization
mixed topologyvisible -> hidden -> root -> synced/void -> visible

Starting States

StateRequired Assertions
collapsed at startnext movement lands on the first intended point
collapsed at endforward movement crosses the next boundary exactly once
expanded forwardShift movement extends from the stable anchor
expanded backwardreverse selection keeps anchor/focus orientation sane
selected allnext extend/collapse avoids double highlight and stale DOM
multi-line blockup/down chooses visual line positions, not only model order
after focus handoffclick or keyboard focus does not import stale DOM selection
after undo/redorestored selection belongs to the original root/path
after hidden materializerevealed content gets real DOM selection once mounted

Required Rows For DOM Coverage Materialize

When a bug touches selectionPolicy: 'materialize', the slice must prove the first keyboard entry opens hidden content and the next extension does not stay model-owned after that content is mounted.

Required minimum:

  • Shift+ArrowDown and Shift+ArrowUp from visible text into closed hidden content, with exact model selection plus mounted DOM assertion.
  • A mid-line caret on the last rendered line before hidden content. Do not only test block-edge positions; native vertical movement skips hidden DOM from mid-line too.
  • At least two hidden component shapes when the owner is generic hidden DOM coverage, for example accordion plus collapsible or tab panel.
  • A negative row after materialize: once the boundary is selected/mounted, the next plain vertical Shift+Arrow stays native unless it enters another unmounted materialize boundary.
  • Mutually exclusive hidden surfaces such as tabs: repeated Shift+Arrow extension across a spanning selection must keep the focus-side panel active. Interior hidden panels must not materialize and cycle active state.

Assertions

Every selected slice should assert the model first:

  • exact Slate selection anchor/focus paths and offsets;
  • value mutation after type/delete/backspace/enter/paste;
  • undo/redo restores both value and selection;
  • copy payload matches the model range when copy is involved.

Browser-visible slices also assert:

  • DOM selection is not doubled;
  • browser selection does not include non-editable chrome;
  • focus belongs to the expected editable/root;
  • hidden content only opens when the policy says it should;
  • no stale placeholder, zero-width, or boundary DOM captures the selection;
  • follow-up typing lands at the selected model point.

Coverage Claim Levels

ClaimBar
exact reproone failing user path is red/green
class sliceexact repro plus one sibling direction or topology sharing the owner
route coverageall relevant command families for one example route
engine coveragesame command families across plain, hidden DOM, root, void, synced, and virtualized topologies
release claimengine coverage plus focused browser proof, package tests, and full-gate decision

Default Slice For Selection Bugs

Start here unless the bug clearly needs less:

  1. Exact reported command and direction.
  2. Reverse direction for the same command family.
  3. Shift variant if the report is collapsed movement, or collapsed variant if the report is extended movement.
  4. One follow-up mutation: type, delete/backspace, or insert break.
  5. One sibling topology that uses the same owner.

If any of those rows fail, fix the owner before adding more route-specific tests.