My Sea per-spread positions + draw-order JS config + position labels — Sprint 5 iter 3 follow-up — TDD

User-locked spec 2026-05-19: each three-card spread uses a DIFFERENT 3-position subset of the 6 surrounding positions, in its own draw order. Replaces the iter-3 binary `data-spread-shape="three-card|six-card"` model w. per-spread `data-spread="<value>"`. Closes iter 3 cleanly + scaffolds the draw-order data iter 4 will consume.

Position subsets (per spread):
  PPF → leave (1) · cover (2) · loom  (3)
  SAO → lay   (1) · cover (2) · crown (3)
  MBS → crown (1) · lay   (2) · loom  (3)
  DOS → loom  (1) · cross (2) · cover (3)
  Waite-Smith     → all 6 surrounding (cover · cross · crown · lay · loom · leave)
  Escape Velocity → all 6 surrounding (cover · cross · lay · leave · crown · loom)

All 6 cells continue to render in DOM unconditionally — `.my-sea-cross[data-spread="<value>"]` SCSS rules hide inactive positions per spread via `display: none`. Cover/cross live nested inside `.sea-pos-core` so their absolute-overlay positioning rules from `_card-deck.scss:1310-1331` carry over for free.

**Position labels** (re-appropriated `.sea-stack-name` typography per user) — `.sea-pos-label` inside each empty `.sea-card-slot--empty` carries the per-spread caption. Server-renders SAO's labels by default (lay=Situation, cover=Action, crown=Outcome); JS swaps labels via `POSITION_LABELS[spread]` lookup on combobox change. Inactive-for-spread positions render their span w. empty `textContent` so JS only has to set text, never toggle visibility. Celtic Cross variants share the gameroom's existing position vocabulary (Crown/Beneath/Cover/Cross/Before/Behind).

**DRAW_ORDER JS const** baked into the inline picker IIFE — array of position names per spread, ready for iter 4's deck-click-deposit logic to consume. Exposed via `window._mySeaDrawOrder` so iter-4 click handlers can `window._mySeaDrawOrder[currentSpread][nextSlotIdx]` to resolve the target position. No click handlers wired yet — iter 4 territory.

**Selenium trap caught**: the combobox click-twice-on-the-toggle bug — re-clicking the combobox while `aria-expanded='true'` closes the dropdown (combobox.js's toggle behavior). Test 3's spread-cycling iterates through 6 spreads, each needs the dropdown OPEN before clicking a new option; added a `_pick(value)` helper that checks `aria-expanded` first.

Files:
- `templates/apps/gameboard/my_sea.html` — `.my-sea-cross[data-spread]` w. server-rendered default; each empty slot wraps a `<span class="sea-pos-label" data-position="<name>">` (SAO labels seeded inline, others empty initially); inline IIFE adds `DRAW_ORDER` + `POSITION_LABELS` consts + `syncLabels()` that swaps captions on `change`.
- `static_src/scss/_gameboard.scss` — drops the `data-spread-shape="three-card"|"six-card"` rules; adds 4 per-spread visibility rules (PPF/SAO/MBS/DOS). Celtic Cross variants inherit the gameroom's full 3×3 grid w. no overrides. `.sea-pos-label` style mirrors `.sea-stack-name` from _card-deck.scss line 1557 (small-uppercase-letter-spaced-scaleY) sans the polarity color — these aren't deck identifiers, just spread-position captions.
- `apps/gameboard/tests/integrated/test_views.py` — IT `test_cross_carries_initial_three_card_spread_shape` renamed + retargeted to `data-spread="situation-action-outcome"`; new IT `test_template_renders_sao_position_labels_on_default` pins the seeded SAO labels + empty spans for inactive positions.
- `functional_tests/test_game_my_sea.py` — iter-2's `test_picker_hides_six_card_only_positions_by_default` renamed to `test_picker_renders_sao_default_position_subset` w. SAO-specific visibility expectations (lay/cover/crown visible; leave/loom/cross hidden). iter-3's `test_picking_celtic_cross_reveals_six_card_positions` rewritten + expanded to `test_picking_spread_swaps_data_spread_and_position_visibility` — cycles through all 6 spreads, asserts `data-spread` attribute + per-position `is_displayed()` for each. New `test_per_spread_position_labels_render_and_update` cycles through 5 spreads (SAO default + 4 switches) asserting captions match the spec.

Tests: 33/33 FT green across test_bill_my_sign + test_game_my_sea; 1049/1049 IT/UT green in 52s.

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-19 19:38:53 -04:00
parent fd5db951a7
commit f154d660bd
4 changed files with 270 additions and 93 deletions

View File

@@ -285,25 +285,64 @@ body.page-gameboard {
gap: 1rem;
}
// .my-sea-cross renders all 6 surrounding positions (crown/lay/cross
// PLUS leave/loom/cover) so the SPREAD dropdown can toggle 3-card vs
// 6-card layout via a single data-attribute swap (no DOM mutation).
// Default `three-card` hides the 3 forsaken positions via `display:
// none`; `six-card` (Celtic Cross variants) renders all 7 cells.
// Cover always shows (it's part of both shapes — overlaid on sig).
.my-sea-cross[data-spread-shape="three-card"] {
grid-template-areas:
"leave core loom";
grid-template-rows: auto;
// .my-sea-cross renders all 6 surrounding positions (crown/leave/lay/
// loom + cover/cross overlaid on core) unconditionally. The SPREAD
// dropdown sets `data-spread="<name>"` on this element; per-spread
// rules below hide the positions each spread doesn't use. Inherits
// the 3×3 `grid-template-areas` from _card-deck.scss line 1189-1200
// so visible cells land in their canonical positions; hidden cells
// just leave their grid slots empty.
//
// Per-spread position subsets — user-locked 2026-05-19:
// PPF: leave (1) cover (2) loom (3) — horizontal middle row
// SAO: lay (1) cover (2) crown (3) — vertical center column
// MBS: crown (1) lay (2) loom (3) — T-shape (crown + lay vertical, loom right)
// DOS: loom (1) cross (2) cover (3) — sig-anchored cluster + loom
// CC variants: all 6 positions (Waite-Smith / Escape Velocity differ in DRAW ORDER only,
// not in position visibility).
.my-sea-cross[data-spread="past-present-future"] {
.sea-pos-crown,
.sea-pos-lay,
.sea-pos-cross,
.sea-pos-lay { display: none; }
}
.my-sea-cross[data-spread="situation-action-outcome"] {
.sea-pos-leave,
.sea-pos-loom,
.sea-pos-cross { display: none; }
}
.my-sea-cross[data-spread-shape="six-card"] {
// Inherits `.sea-cross`'s 3×3 template from _card-deck.scss line 1189-
// 1200; nothing to override layout-wise. All cells visible.
.my-sea-cross[data-spread="mind-body-spirit"] {
.sea-pos-leave,
.sea-pos-cover,
.sea-pos-cross { display: none; }
}
.my-sea-cross[data-spread="desire-obstacle-solution"] {
.sea-pos-leave,
.sea-pos-crown,
.sea-pos-lay { display: none; }
}
// Celtic Cross variants (waite-smith / escape-velocity) — all positions
// visible by default. No `display: none` overrides needed.
// Position-name caption inside each empty `.sea-card-slot--empty` —
// re-appropriates the GRAVITY/LEVITY `.sea-stack-name` typographic
// look (_card-deck.scss line 1557): small uppercase letter-spaced w.
// a subtle scaleY stretch, --terUser ink at 0.6 opacity. No polarity
// coloring — these are spread-position labels, not deck identifiers.
.sea-pos-label {
font-size: 0.65rem;
letter-spacing: 0.08em;
text-transform: uppercase;
font-weight: 600;
opacity: 0.6;
transform: scaleY(1.2);
color: rgba(var(--terUser), 1);
text-align: center;
pointer-events: none;
}
// Section dividers inside the SPREAD combobox — labels "3-card spreads"