Skip to content

Proposal H, Step 2 PR 2a: real candidate/version picker in the display rail - #96

Merged
WilfordGrimley merged 2 commits into
masterfrom
claude/proposal-h-2a-candidate-picker-04bam2
Jul 18, 2026
Merged

Proposal H, Step 2 PR 2a: real candidate/version picker in the display rail#96
WilfordGrimley merged 2 commits into
masterfrom
claude/proposal-h-2a-candidate-picker-04bam2

Conversation

@WilfordGrimley

Copy link
Copy Markdown

Description

Instrument parity, PR 2a of the Step 2 sequence for docs/proposals/proposal-h-unified-display-page.md
(behind NEXT_PUBLIC_UNIFIED_DISPLAY_ENABLED, unmerged Step 1 shell from PR #87).

The rail's "Choose Image" accordion section previously rendered a stub. This PR wires it up to the
real candidate/version-picker machinery, extracted out of GridSelectorModal.tsx so the classic
modal and the new rail share one implementation instead of forking it:

  • useGridSelectorSearch.ts (new) — GridSelectorModal's entire filtering/sorting/search state
    machine, extracted verbatim into a hook (show renamed active).
  • GridSelectorResults.tsx (new) — the filters-column + results-column body, extracted out of the
    modal. Takes a variant: "modal" | "embedded" — "modal" wraps each column in the existing
    OverflowCol (unchanged behavior); "embedded" uses a plain Col so the display rail's own
    scroll container isn't fighting a second, nested one.
  • GridSelectorModal.tsx — refactored to consume both of the above. Its own behavior is unchanged
    and is proven so by its full existing Playwright suite (GridSelectorModal.spec.ts,
    GridSelectorModalAccessibility.spec.ts, GridSelectorModalMobile.spec.ts — 26 tests), plus
    CardSlot.spec.ts and DeckbuilderConfirmAffordance.spec.ts, all passing unmodified.
  • DisplayPage.tsx — "Choose Image" now renders a ChooseImageSection that resolves the selected
    slot's candidates the same way CardSlot.tsx does and dispatches the same setSelectedImages
    action on selection, so the sheet updates immediately.
  • CardResultSet.tsx — its result grid's column count (row-cols-*) is driven by viewport width,
    correct for the classic full-width modal but not for the rail's much narrower results column. Added
    a variant prop so "embedded" pins a fixed 2-column layout regardless of viewport size, instead of
    squeezing candidate cards down to a few px wide.

Checklist

  • I have installed pre-commit and installed the hooks with pre-commit install before creating any commits.
  • I have updated any related tests for code I modified or added new tests where appropriate.
  • I have manually tested my changes as follows:
    • Full DisplayPage.spec.ts suite (9 tests, incl. the embedded candidate grid's real selection flow and no-nested-scroll-region check).
    • Full GridSelectorModal/GridSelectorModalAccessibility/GridSelectorModalMobile/CardSlot/DeckbuilderConfirmAffordance suites (55 tests) to confirm zero behavior change to the classic modal.
    • npx tsc --noEmit clean.
    • npx jest — 345 tests passing.
    • NEXT_PUBLIC_UNIFIED_DISPLAY_ENABLED=true npx next build — clean production build with the flag on (the standing verification bar added after PR Fix production build: RailProps.cardDocumentsByIdentifier needs | undefined #90's build failure).
  • I have updated any relevant documentation or created new documentation where appropriate.

Per the Step 2 instructions: zero changes to the classic editor's/modal's behavior (verified above);
"After 2f: STOP" — steps 3/4 (inline export, switchover) are not part of this or any remaining Step 2 PR.


Generated by Claude Code

claude added 2 commits July 18, 2026 22:04
Wires the actual grid-selector machinery into the "Choose Image"
accordion section, replacing its Step 1 stub:

- Extracted GridSelectorModal.tsx's filtering/search state machine
  into useGridSelectorSearch.ts (a hook, parameterized by `active`
  instead of an inline `show` check) and its filters+results layout
  into GridSelectorResults.tsx (a `variant: "modal" | "embedded"`
  presentational component - "embedded" uses plain Col instead of
  OverflowCol, which would otherwise nest a second 100vh-relative
  scroll region inside the rail's own already-scrolling container).
- GridSelectorModal.tsx itself now delegates to both rather than
  duplicating the logic - its own Modal/Header/Body/Footer JSX is
  otherwise unchanged, verified byte-identical in behavior by its
  full existing Playwright suite (GridSelectorModal[.Accessibility|
  .Mobile].spec.ts, 26 tests) plus CardSlot.spec.ts and
  DeckbuilderConfirmAffordance.spec.ts (11 tests) - all still passing.
- DisplayPage.tsx's new ChooseImageSection mounts the same hook +
  component for the selected slot's search results, dispatching the
  same setSelectedImages action CardSlot.tsx's own grid selector uses
  - the sheet's thumbnail for that slot updates immediately (same
  Redux state, same PagePreview render path).

Tests: 3 new/updated cases in tests/DisplayPage.spec.ts (selecting a
candidate updates the sheet immediately; the embedded variant has no
nested forced-scroll ancestor; the two Step 1 stub-text assertions
this stub's removal made stale, updated to assert the real content).
Full Jest suite (345 tests), tsc, eslint, and a real production build
with the flag on (this task's own new standing verification bar) all
pass.
…d the same bug

CardRow's column-count props (xxl/xl/lg/md/sm/xs) key off *viewport* width via
media queries, correct for the classic modal but wrong once the same grid is
embedded in the display rail's ~150-250px results column - at a normal desktop
viewport the viewport-driven breakpoints still picked 4-6 columns, squeezing
each candidate card to a few px wide (effectively invisible, zero-width).
CardResultSet/CardRow now take a variant prop; "embedded" pins a fixed
2-column layout that fits the rail regardless of viewport size.

Separately, DisplayPage.spec.ts's new "Option N" assertions were missing the
explicit Compressed-view toggle CardSlot.spec.ts already established as
required precedent (Card.tsx doesn't render its header/title text at all
under the default compressed=true view setting).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MHapYojTkT5wenrQwGbGYk
@WilfordGrimley
WilfordGrimley merged commit cccc658 into master Jul 18, 2026
7 checks passed
@WilfordGrimley
WilfordGrimley deleted the claude/proposal-h-2a-candidate-picker-04bam2 branch July 18, 2026 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants