LearnNewsExamplesServices
Frontmatter
id12800
titleGrid Multi-Body: dedicated grid.header.Wrapper orchestrator
stateClosed
labels
enhancementairefactoringgrid
assigneesneo-opus-grace
createdAtJun 9, 2026, 2:11 AM
updatedAtJun 9, 2026, 10:31 AM
githubUrlhttps://github.com/neomjs/neo/issues/12800
authorneo-opus-grace
commentsCount0
parentIssue9872
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtJun 9, 2026, 10:31 AM

Grid Multi-Body: dedicated grid.header.Wrapper orchestrator

Closed v13.0.0/archive-v13-0-0-chunk-17 enhancementairefactoringgrid
neo-opus-grace
neo-opus-grace commented on Jun 9, 2026, 2:11 AM

Context

#9872 (Grid Multi-Body: 3-Tier Component Orchestration) identifies the "God Object" anti-pattern: Neo.grid.Container manually instantiates and orchestrates the split header toolbars (headerStart, headerToolbar, headerEnd) inline, mixing header concerns into the macro layout layer and forcing VDOM/selection-model thrash across unrelated domains. This ticket is the Tier-2 slice of that refactor — extract the header region into a dedicated orchestrator, mirroring how Neo.grid.View already owns the body region.

Live latest-open sweep: checked latest 20 open issues at 2026-06-09T00:09:51Z; no equivalent found (#9872 is the parent feature ticket, #9486 the epic, #9491/#9611 distinct multi-body siblings).

The Problem

All header-toolbar lifecycle + orchestration currently lives in grid.Container:

  • createOrUpdateSubGrids() creates/destroys headerStart/headerEnd (interleaved with the body sub-grids) and assembles them into a generic BaseContainer header wrapper.
  • onColumnsMutate() re-homes and re-orders the column header buttons across the three toolbars.
  • getButton() resolves a header button by dataField across the three toolbars.

The header wrapper is a bare container.Base with no header-specific identity, so header logic has no natural home and bleeds into the container.

The Architectural Reality

  • grid.View (src/grid/View.mjs) is already the dedicated body orchestrator (owns bodyStart/body/bodyEnd synchronization via syncBodies). The header side has no equivalent — it is a bare BaseContainer driven externally by Container.
  • Every headerStart/headerEnd reference is confined to three Container methods (createOrUpdateSubGrids, onColumnsMutate, getButton) — verified zero references elsewhere in src/ or test/.
  • neo-header-wrapper has no CSS-selector dependency (only the inline cls), so a dedicated class can preserve it as a baseCls with no style regression.

The Fix

Introduce src/grid/header/Wrapper.mjsNeo.grid.header.Wrapper extends Neo.container.Base (ntype: 'grid-header-wrapper'), the header-side counterpart to grid.View. It owns:

  • headerStart / headerEnd configs + their create/destroy lifecycle.
  • updateHeaders(lockedStartButtons, centerButtons, lockedEndButtons) — centre-toolbar refresh + locked-toolbar lifecycle + left-to-right assembly.
  • applyColumnButtonOrder(lockedStartColumns, centerColumns, lockedEndColumns) — column-button re-homing/re-ordering.
  • getButton(dataField) — header-button lookup.

grid.Container becomes a thin delegator: createOrUpdateSubGrids calls headerWrapper.updateHeaders(...) (retaining only body creation), onColumnsMutate calls headerWrapper.applyColumnButtonOrder(...), getButton delegates. The headerStart/headerEnd configs move off Container entirely.

Decision Record impact

none — grid header-component orchestration is not ADR-governed (the multi-body SelectionModel design-lock is #9492, a design comment, not an ADR; unaffected here).

Acceptance Criteria

  • src/grid/header/Wrapper.mjs exists as a dedicated container.Base subclass owning headerStart/headerEnd + the three header-orchestration methods.
  • grid.Container no longer references headerStart/headerEnd directly (delegates via headerWrapper).
  • grid.header.Wrapper registered in src/grid/header/_export.mjs.
  • neo-header-wrapper cls preserved (no style regression).
  • Grid unit suite shows no new failures vs the pre-change baseline (the 7 pre-existing local-env bucket-B failures unchanged); GridScrollProfile passes.

Out of Scope

  • Tier-1 (grid.Container → pure macro layout coordinator; drop body instantiation): separate follow-up.
  • Tier-3 (body creation/lifecycle → grid.View): separate follow-up; overlaps #12758's in-review createOrUpdateSubGrids changes, so sequenced after that merges.
  • Full migration of the centre headerToolbar reactive config off Container (it remains a Container config; the Wrapper places it).

Related

  • Parent: #9872 (3-Tier Orchestration); epic #9486.
  • #12758 (View-owned SelectionModel, in review) — touches the same createOrUpdateSubGrids; this work's header/body split is sequenced to rebase cleanly after it merges.
  • Siblings: #9491 (SortZone DnD), #9611 (native scrollbar).

Retrieval Hint: "grid.header.Wrapper Tier-2 extraction from Container God-Object"; branch claude/9872-header-wrapper.

Authored by Claude Opus 4.8 (Claude Code)

tobiu referenced in commit d04bc04 - "refactor(grid): extract dedicated header.Wrapper orchestrator (#12800) (#12801) on Jun 9, 2026, 10:31 AM
tobiu closed this issue on Jun 9, 2026, 10:31 AM