Skip to content

Commit 2b796f7

Browse files
Port GridSelectorModal/CardSlot clusters onto DisplayPage (#410)
* Port GridSelectorModal/CardSlot clusters onto DisplayPage (issue #272 wave 3) Un-skips 30 of 55 deferred tests (GridSelectorModal.spec.ts + GridSelectorModalVariants.spec.ts fully, CardSlot.spec.ts partially), retargeted onto CardbackToolbarButton's cardback picker and the sheet's page-preview-slot context menu; ports 2 of 6 CardSlot.visual.spec.ts aria-snapshot tests, regex-tolerant on unrelated pre-existing icon-font rendering leaves. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * Fold the DOM-API sheet gap into the doc body per edit-in-place convention --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent e32a147 commit 2b796f7

8 files changed

Lines changed: 528 additions & 764 deletions

File tree

docs/features/card-dom-api.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,19 @@ Documented in `frontend/docs/dom-api.md`. Test coverage in
5555
`QuestionFeed*.spec.ts` suite (unified vote queue, successor to the old
5656
standalone `PrintingTagQueue.tsx` this API originally shipped against)
5757
— real Playwright runs against the mocked backend, not just typecheck.
58+
59+
**Known gap: unimplemented on the unified `/editor` page's sheet.**
60+
`Card.tsx`/`CardSlot.tsx`/`CardDetailedViewModal.tsx` (plus the non-slot
61+
`orphanCard.ts` helper) are the only callers of `getCardDataAttributes`/
62+
`getCardSelectedEventDetail`/`CardSelectedEventName` — none of them render
63+
the _placed_ card for a project slot on the unified page.
64+
`PagePreview.tsx` (the sheet's own per-slot renderer,
65+
`frontend/src/features/pdf/PagePreview.tsx`) renders a plain, unwrapped
66+
`<img>` with no `data-card-*` attributes and dispatches no
67+
`mpc:card-selected` event at all. Any external tooling built against this
68+
contract (userscripts, browser extensions) that read a project's placed
69+
cards off the classic grid gets nothing on the unified page. The one
70+
classic test exercising this ("selecting an image in a CardSlot via the
71+
grid selector") was dropped in the issue #272 wave-3 port rather than
72+
weakened; wiring `PagePreview.tsx`'s slot `<img>` into
73+
`getCardDataAttributes` is a contained, mechanical fix once prioritized.

docs/features/grid-selector.md

Lines changed: 53 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,22 @@ matched image for a card slot, seeing them all at once in a grid
66
image-loading/error states, since those apply to every card render
77
across the app, not just inside this modal.
88

9+
**Post-route-swap reachability (2026-07-24, issue #272 parity wave 3):**
10+
per-slot picking on the unified `/editor` page goes entirely through the
11+
rail's own Select Version section below (a different component,
12+
`SelectVersionResults.tsx` — no modal, no grouping/filters-sidebar/Jump-to-
13+
Version UI of its own). `GridSelectorModal.tsx` itself has exactly one
14+
surviving mount post-swap: `CardbackToolbarButton`/`CommonCardback.tsx`'s
15+
project-wide cardback picker (testid `cardback-grid-selector`, title
16+
"Select Cardback"), reachable from the right rail's Cardback button once
17+
the project is non-empty. It's otherwise unchanged and fully generic (a
18+
bare `imageIdentifiers` array + `onClick` callback) — every grouping/
19+
filter/keyboard/mobile-viewport behavior below applies identically
20+
regardless of which caller's identifiers feed it. `GridSelectorModal.spec.ts`/
21+
`GridSelectorModalVariants.spec.ts`/`CardSlot.visual.spec.ts`'s own two
22+
grid-selector snapshot tests were re-ported onto this cardback mount in
23+
that wave — see `openDisplayCardbackGridSelector` (`frontend/tests/ test-utils.ts`) for the helper and its own comment for the full rationale.
24+
925
## Frontend-polish UX pass (PR-B, 2026-07-17)
1026

1127
Presentation/interaction fixes from the frontend-polish package's
@@ -480,17 +496,28 @@ described above.
480496
half of PR #325's backend contract)
481497
- `frontend/src/features/card/Card.tsx` (+ new `Card.test.tsx`),
482498
`CardSlot.tsx`
483-
- Tests: `frontend/tests/GridSelectorModalVariants.spec.ts` (keyboard nav
484-
- a large-grid focus-perf check, autofocus fallback, mobile filters
485-
default — merged from the former `GridSelectorModalAccessibility.spec.ts`
486-
and `GridSelectorModalMobile.spec.ts`), `frontend/tests/CardImageStates.spec.ts`
487-
(error placeholder + slow-load hint), `frontend/tests/SelectVersionSection.spec.ts`
488-
(grouping/ordering, moment (a)/(b)/(c) behavior on the sidebar layout — issue
489-
#167 — plus the funnel's implicit-cast/reset/ack and retract-on-reselect
490-
end-to-end flows), `frontend/src/features/gridSelector/SelectVersionResults.test.tsx`
491-
(axis exclusivity, membership-driven axis rendering, disclosure tiers,
492-
SUGGESTED-chip rendering, F5 votes-off completeness), `frontend/tests/ DisplayPage.spec.ts` (F6: right-click + the `` cue opening the shared
493-
context menu on the center sheet)
499+
- Tests: `frontend/tests/GridSelectorModal.spec.ts` (23 tests) +
500+
`GridSelectorModalVariants.spec.ts` (7 tests: keyboard nav, a large-grid
501+
focus-perf check, autofocus fallback, mobile filters default — merged
502+
from the former `GridSelectorModalAccessibility.spec.ts` and
503+
`GridSelectorModalMobile.spec.ts`) — parity wave 3 (2026-07-24, issue
504+
#272) ported both onto the cardback mount (see the "Post-route-swap
505+
reachability" note above); `frontend/tests/CardSlot.spec.ts` (15 of 25
506+
tests ported the same wave — delete/duplicate/change-query/context-menu/
507+
auto-select coverage against the sheet's own slots, `page-preview-slot` +
508+
`page-preview-slot-menu-cue`; see this doc's own "Known gaps" for what
509+
wasn't ported) and its `visual/CardSlot.visual.spec.ts` companion (2 of 6
510+
aria-snapshot tests ported, retargeted onto the cardback mount, regex-
511+
tolerant on a handful of pre-existing third-party icon-font/tree-select
512+
rendering leaves — see that file's own module comment),
513+
`frontend/tests/CardImageStates.spec.ts`
514+
(error placeholder + slow-load hint), `frontend/tests/SelectVersionSection.spec.ts`
515+
(grouping/ordering, moment (a)/(b)/(c) behavior on the sidebar layout — issue
516+
#167 — plus the funnel's implicit-cast/reset/ack and retract-on-reselect
517+
end-to-end flows), `frontend/src/features/gridSelector/SelectVersionResults.test.tsx`
518+
(axis exclusivity, membership-driven axis rendering, disclosure tiers,
519+
SUGGESTED-chip rendering, F5 votes-off completeness), `frontend/tests/ DisplayPage.spec.ts` (F6: right-click + the `` cue opening the shared
520+
context menu on the center sheet)
494521

495522
## Known gaps
496523

@@ -509,3 +536,18 @@ described above.
509536
caller specifically; `GridSelectorModal.tsx`'s own sidebar layout is
510537
unchanged, so this is a rail-only fix, not a change to the shared
511538
column-breakpoint default itself).
539+
- **Per-slot next/prev image-cycling has no unified-page equivalent**
540+
(found 2026-07-24, issue #272 parity wave 3). The classic grid's inline
541+
❯/❮ arrows (`CardSlot.tsx`) let a user cycle a slot's selected image one
542+
step at a time, with wrap-around; the sheet's Select Version section is
543+
a browse-and-click surface only — no "next"/"previous" concept at all.
544+
`CardSlot.spec.ts`'s 3 cycling tests were dropped, not ported, for this
545+
reason (see that file's own module comment). Not tracked against any
546+
numbered gap in issue #272's own checklist — a new finding, flagged for
547+
the owner alongside this same wave's `card-dom-api.md` gap below.
548+
- **`docs/features/card-dom-api.md`'s DOM API contract (`data-card-*`/
549+
`mpc:card-selected`) is unimplemented on the sheet's own placed-card
550+
slots** (found 2026-07-24, same wave) — see that doc's own "Known gap"
551+
entry for the full detail; cross-referenced here since the dropped test
552+
that surfaced it ("selecting an image in a CardSlot via the grid
553+
selector") lives in this feature's own `CardSlot.spec.ts`.

frontend/src/mocks/handlers.ts

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,78 @@ export const cardbacksServerError = http.post(buildRoute("2/cardbacks/"), () =>
322322
HttpResponse.json(createError("2/cardbacks"), { status: 500 })
323323
);
324324

325+
// GridSelectorModal parity port (2026-07-24, issue #272 wave 3). GridSelectorModal.tsx's only
326+
// surviving mount post-route-swap is CardbackToolbarButton's project-wide cardback picker
327+
// (CommonCardback.tsx) - it's fed by the `2/cardbacks` identifier list, not a search query, so
328+
// the classic per-slot cluster's own `2/cards/` + `3/editorSearch/` fixture pairs (below) need a
329+
// `2/cardbacks` counterpart naming the same identifiers to reuse unchanged for this wave's ported
330+
// tests. The modal itself doesn't care what a given identifier's underlying CardDocument's own
331+
// name/art actually depicts (see GridSelectorModal.tsx: a bare `imageIdentifiers` array + `onClick`
332+
// callback) - reusing `cardDocumentsThreeResults`' cast as "cardbacks" here is cosmetic only,
333+
// already an established pattern (see cardbacksTwoResults/cardbacksOneResult above, both cast
334+
// plain search-result cardDocument1/2 as cardbacks the same way).
335+
export const cardbacksThreeResults = http.post(buildRoute("2/cardbacks"), () =>
336+
HttpResponse.json(
337+
{
338+
cardbacks: [
339+
cardDocument1.identifier,
340+
cardDocument2.identifier,
341+
cardDocument3.identifier,
342+
],
343+
},
344+
{ status: 200 }
345+
)
346+
);
347+
348+
// Matches cardDocumentsFourResults' identifier set - used by CardSlot.visual.spec.ts's grid-
349+
// selector aria-snapshot pair, re-anchored onto the cardback picker this wave.
350+
export const cardbacksFourResults = http.post(buildRoute("2/cardbacks"), () =>
351+
HttpResponse.json(
352+
{
353+
cardbacks: [
354+
cardDocument1.identifier,
355+
cardDocument2.identifier,
356+
cardDocument3.identifier,
357+
cardDocument4.identifier,
358+
],
359+
},
360+
{ status: 200 }
361+
)
362+
);
363+
364+
// Matches cardDocumentsTwoSources' identifier set (card1+card2 from source1, card7 from source2)
365+
// - used by GridSelectorModal.spec.ts's source-filter test.
366+
export const cardbacksTwoSources = http.post(buildRoute("2/cardbacks"), () =>
367+
HttpResponse.json(
368+
{
369+
cardbacks: [
370+
cardDocument1.identifier,
371+
cardDocument2.identifier,
372+
cardDocument7.identifier,
373+
],
374+
},
375+
{ status: 200 }
376+
)
377+
);
378+
379+
// Matches cardDocumentsWithCanonicalCards' identifier set - used by GridSelectorModal.spec.ts's
380+
// CanonicalCardFilter/Printing-grouping tests.
381+
export const cardbacksWithCanonicalCards = http.post(
382+
buildRoute("2/cardbacks"),
383+
() =>
384+
HttpResponse.json(
385+
{
386+
cardbacks: [
387+
cardDocument8.identifier,
388+
cardDocument9.identifier,
389+
cardDocument10.identifier,
390+
cardDocument11.identifier,
391+
],
392+
},
393+
{ status: 200 }
394+
)
395+
);
396+
325397
//# endregion
326398

327399
//# region search results

0 commit comments

Comments
 (0)