LearnNewsExamplesServices
Frontmatter
id19798
titleLocked grid headers keep stale sort arrows and miss header settings
stateClosed
labels
bugaigrid
assigneesneo-opus-grace
createdAtOct 16, 2026, 12:59 PM
updatedAtOct 16, 2026, 1:43 PM
githubUrlhttps://github.com/neomjs/neo/issues/19798
authorneo-opus-grace
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtOct 16, 2026, 1:43 PM
milestonev13.3

Locked grid headers keep stale sort arrows and miss header settings

neo-opus-grace
neo-opus-grace commented on Oct 16, 2026, 12:59 PM

Context

Found during a manual v13.3 sweep of examples/grid/issues on dev af81fc0f86. Sort by Title, then by State: three headers now show a sort arrow (# descending, Title ascending, State ascending), but the Store sorts by State alone. The showcase locks # and Title at the start and State at the end, so every sort there leaves stale arrows behind.

The Problem

Neo.data.Store#sort keeps a single sorter (src/data/Store.mjs:1798). When a header sorts, grid.Container#onSortColumn clears every other header's sort state (src/grid/Container.mjs:1334). That clear, removeSortingCss (:1428), only walks the center headerToolbar. Buttons in the locked-start and locked-end toolbars keep their arrow and their isSorted.

Three grid-level header configs propagate along the same center-only path. afterSetShowHeaderFilters, afterSetSortable and afterSetUseTriStateSorting (:588, :600, :612) each assign only this.headerToolbar.<config>. A locked toolbar takes these configs only when it is cloned from the center one (#19772). After a runtime change, its buttons keep the old value: grid.sortable = false, for example, leaves the locked headers sortable. A locked toolbar created after the change reverts it too, because Neo.cloneNeoInstance starts from the center toolbar's construction configs (src/Neo.mjs:262). No shipped example toggles these configs on a grid with locked columns; the unit specs below are the witnesses.

The Architectural Reality

  • grid.header.Wrapper owns the locked toolbars headerStart and headerEnd, created on demand, beside the container's center headerToolbar. The left-to-right list of those three is rebuilt inline in three places: Wrapper#applyColumnButtonOrder (Wrapper.mjs:87), Wrapper#updateHeaders (:224–226) and Container#onColumnLockChange (Container.mjs:1155).
  • Design authority: the code's own contract. The Store keeps one sorter, and onSortColumn clears the sort state of every other header after store.sort(). Locked buttons fall outside that walk only because they live in other toolbars.

The Fix

  • Wrapper#getToolbars() returns the header toolbars in left-to-right order and skips absent regions. The three inline lists use it.
  • Container#removeSortingCss and the three afterSet* hooks walk getToolbars(), so every region gets the same state.
  • Wrapper#updateHeaders clones a locked toolbar with the center toolbar's current showHeaderFilters, sortable and useTriStateSorting.

Acceptance Criteria

  • In examples/grid/issues, after any header sort, exactly one header shows a sort arrow, whichever region holds it.
  • Unit (test/playwright/unit/grid/LockedColumns.spec.mjs): sorting a center column clears the isSorted of a locked-start and a locked-end button and hides their arrows, and sorting a locked column clears the center's. Red on dev.
  • Unit (test/playwright/unit/grid/LockedSubGridConfigs.spec.mjs): runtime sortable, useTriStateSorting and showHeaderFilters reach the locked toolbars and buttons, including a locked toolbar created after the change. Red on dev.
  • The existing grid unit specs and the grid header e2e specs stay green.

Out of Scope

  • The locked-start body seam in the issues showcase, captured as a separate defect-note.
  • #19793's filtered-column header state. It needs the same all-region walk and can reuse getToolbars().

Related

#19793 · #19772 · #19652

Live latest-open sweep: the latest 20 open issues at 2026-10-16T10:59Z, no equivalent. #19793 shares the surface, not the defect. A2A claim sweep: the latest 30 messages, through 10:51Z. The only grid claim is Ada's #19793. MC sweep: "grid locked column header sort arrow stays after sorting another column removeSortingCss headerStart headerEnd", 6 results. The only same-method hit is a February regression (an early return), since fixed; no prior decision on the locked regions. Own-assignment sweep: 4 open (#19766, #19765, #19652, #15000), none overlapping.

Origin Session ID: bb2e8968-a6d6-4ef7-a4ed-5ab3157b1ac8 Retrieval Hint: "grid locked header toolbars stale sort arrow center-only headerToolbar walk getToolbars"

tobiu referenced in commit 0ca204c - "fix(grid): header-wide state reaches the locked header regions (#19798) (#19799) on Oct 16, 2026, 1:43 PM
tobiu closed this issue on Oct 16, 2026, 1:43 PM