Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions docs/features/card-dom-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,19 @@ Documented in `frontend/docs/dom-api.md`. Test coverage in
`QuestionFeed*.spec.ts` suite (unified vote queue, successor to the old
standalone `PrintingTagQueue.tsx` this API originally shipped against)
— real Playwright runs against the mocked backend, not just typecheck.

**Known gap: unimplemented on the unified `/editor` page's sheet.**
`Card.tsx`/`CardSlot.tsx`/`CardDetailedViewModal.tsx` (plus the non-slot
`orphanCard.ts` helper) are the only callers of `getCardDataAttributes`/
`getCardSelectedEventDetail`/`CardSelectedEventName` — none of them render
the _placed_ card for a project slot on the unified page.
`PagePreview.tsx` (the sheet's own per-slot renderer,
`frontend/src/features/pdf/PagePreview.tsx`) renders a plain, unwrapped
`<img>` with no `data-card-*` attributes and dispatches no
`mpc:card-selected` event at all. Any external tooling built against this
contract (userscripts, browser extensions) that read a project's placed
cards off the classic grid gets nothing on the unified page. The one
classic test exercising this ("selecting an image in a CardSlot via the
grid selector") was dropped in the issue #272 wave-3 port rather than
weakened; wiring `PagePreview.tsx`'s slot `<img>` into
`getCardDataAttributes` is a contained, mechanical fix once prioritized.
64 changes: 53 additions & 11 deletions docs/features/grid-selector.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,22 @@ matched image for a card slot, seeing them all at once in a grid
image-loading/error states, since those apply to every card render
across the app, not just inside this modal.

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

## Frontend-polish UX pass (PR-B, 2026-07-17)

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

## Known gaps

Expand All @@ -509,3 +536,18 @@ described above.
caller specifically; `GridSelectorModal.tsx`'s own sidebar layout is
unchanged, so this is a rail-only fix, not a change to the shared
column-breakpoint default itself).
- **Per-slot next/prev image-cycling has no unified-page equivalent**
(found 2026-07-24, issue #272 parity wave 3). The classic grid's inline
❯/❮ arrows (`CardSlot.tsx`) let a user cycle a slot's selected image one
step at a time, with wrap-around; the sheet's Select Version section is
a browse-and-click surface only — no "next"/"previous" concept at all.
`CardSlot.spec.ts`'s 3 cycling tests were dropped, not ported, for this
reason (see that file's own module comment). Not tracked against any
numbered gap in issue #272's own checklist — a new finding, flagged for
the owner alongside this same wave's `card-dom-api.md` gap below.
- **`docs/features/card-dom-api.md`'s DOM API contract (`data-card-*`/
`mpc:card-selected`) is unimplemented on the sheet's own placed-card
slots** (found 2026-07-24, same wave) — see that doc's own "Known gap"
entry for the full detail; cross-referenced here since the dropped test
that surfaced it ("selecting an image in a CardSlot via the grid
selector") lives in this feature's own `CardSlot.spec.ts`.
72 changes: 72 additions & 0 deletions frontend/src/mocks/handlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,78 @@ export const cardbacksServerError = http.post(buildRoute("2/cardbacks/"), () =>
HttpResponse.json(createError("2/cardbacks"), { status: 500 })
);

// GridSelectorModal parity port (2026-07-24, issue #272 wave 3). GridSelectorModal.tsx's only
// surviving mount post-route-swap is CardbackToolbarButton's project-wide cardback picker
// (CommonCardback.tsx) - it's fed by the `2/cardbacks` identifier list, not a search query, so
// the classic per-slot cluster's own `2/cards/` + `3/editorSearch/` fixture pairs (below) need a
// `2/cardbacks` counterpart naming the same identifiers to reuse unchanged for this wave's ported
// tests. The modal itself doesn't care what a given identifier's underlying CardDocument's own
// name/art actually depicts (see GridSelectorModal.tsx: a bare `imageIdentifiers` array + `onClick`
// callback) - reusing `cardDocumentsThreeResults`' cast as "cardbacks" here is cosmetic only,
// already an established pattern (see cardbacksTwoResults/cardbacksOneResult above, both cast
// plain search-result cardDocument1/2 as cardbacks the same way).
export const cardbacksThreeResults = http.post(buildRoute("2/cardbacks"), () =>
HttpResponse.json(
{
cardbacks: [
cardDocument1.identifier,
cardDocument2.identifier,
cardDocument3.identifier,
],
},
{ status: 200 }
)
);

// Matches cardDocumentsFourResults' identifier set - used by CardSlot.visual.spec.ts's grid-
// selector aria-snapshot pair, re-anchored onto the cardback picker this wave.
export const cardbacksFourResults = http.post(buildRoute("2/cardbacks"), () =>
HttpResponse.json(
{
cardbacks: [
cardDocument1.identifier,
cardDocument2.identifier,
cardDocument3.identifier,
cardDocument4.identifier,
],
},
{ status: 200 }
)
);

// Matches cardDocumentsTwoSources' identifier set (card1+card2 from source1, card7 from source2)
// - used by GridSelectorModal.spec.ts's source-filter test.
export const cardbacksTwoSources = http.post(buildRoute("2/cardbacks"), () =>
HttpResponse.json(
{
cardbacks: [
cardDocument1.identifier,
cardDocument2.identifier,
cardDocument7.identifier,
],
},
{ status: 200 }
)
);

// Matches cardDocumentsWithCanonicalCards' identifier set - used by GridSelectorModal.spec.ts's
// CanonicalCardFilter/Printing-grouping tests.
export const cardbacksWithCanonicalCards = http.post(
buildRoute("2/cardbacks"),
() =>
HttpResponse.json(
{
cardbacks: [
cardDocument8.identifier,
cardDocument9.identifier,
cardDocument10.identifier,
cardDocument11.identifier,
],
},
{ status: 200 }
)
);

//# endregion

//# region search results
Expand Down
Loading
Loading