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:
@@ -66,21 +66,27 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Picker phase — flexible spread layout. Sig pins .sea-pos- #}
|
||||
{# core; the 6 surrounding positions (crown/leave/loom/lay/ #}
|
||||
{# cover/cross) all render unconditionally so the SPREAD #}
|
||||
{# dropdown can swap `.my-sea-cross[data-spread-shape]` #}
|
||||
{# between `three-card` (default — hides crown/lay/cross via #}
|
||||
{# SCSS) + `six-card` (Celtic Cross variants — shows all). #}
|
||||
{# Hidden until FREE DRAW click swaps data-phase to `picker`. #}
|
||||
{# Picker phase — per-spread flexible layout. Sig pins .sea- #}
|
||||
{# pos-core; the 6 surrounding positions all render in DOM #}
|
||||
{# so the SPREAD dropdown can swap `.my-sea-cross[data- #}
|
||||
{# spread]` between the 4 three-card variants (each w. its #}
|
||||
{# own 3-position subset + draw order) + the 2 six-card #}
|
||||
{# Celtic Cross variants (all 6 surrounding positions). #}
|
||||
{# Each empty slot carries a `.sea-pos-label` caption (re- #}
|
||||
{# appropriated from the GRAVITY/LEVITY .sea-stack-name look) #}
|
||||
{# that JS updates per spread. #}
|
||||
<div class="my-sea-picker" style="display:none">
|
||||
<div class="sea-cards-col">
|
||||
<div class="sea-cross my-sea-cross" data-spread-shape="three-card">
|
||||
<div class="sea-cross my-sea-cross" data-spread="{{ default_spread }}">
|
||||
<div class="sea-crucifix-cell sea-pos-crown">
|
||||
<div class="sea-card-slot sea-card-slot--empty"></div>
|
||||
<div class="sea-card-slot sea-card-slot--empty">
|
||||
<span class="sea-pos-label" data-position="crown">Outcome</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sea-crucifix-cell sea-pos-leave">
|
||||
<div class="sea-card-slot sea-card-slot--empty"></div>
|
||||
<div class="sea-card-slot sea-card-slot--empty">
|
||||
<span class="sea-pos-label" data-position="leave"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sea-crucifix-cell sea-pos-core">
|
||||
<div class="sig-stage-card sea-sig-card"
|
||||
@@ -89,17 +95,25 @@
|
||||
{% if significator.suit_icon %}<i class="fa-solid {{ significator.suit_icon }}"></i>{% endif %}
|
||||
</div>
|
||||
<div class="sea-pos-cover">
|
||||
<div class="sea-card-slot sea-card-slot--empty"></div>
|
||||
<div class="sea-card-slot sea-card-slot--empty">
|
||||
<span class="sea-pos-label" data-position="cover">Action</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sea-pos-cross">
|
||||
<div class="sea-card-slot sea-card-slot--empty sea-card-slot--crossing"></div>
|
||||
<div class="sea-card-slot sea-card-slot--empty sea-card-slot--crossing">
|
||||
<span class="sea-pos-label" data-position="cross"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sea-crucifix-cell sea-pos-loom">
|
||||
<div class="sea-card-slot sea-card-slot--empty"></div>
|
||||
<div class="sea-card-slot sea-card-slot--empty">
|
||||
<span class="sea-pos-label" data-position="loom"></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sea-crucifix-cell sea-pos-lay">
|
||||
<div class="sea-card-slot sea-card-slot--empty"></div>
|
||||
<div class="sea-card-slot sea-card-slot--empty">
|
||||
<span class="sea-pos-label" data-position="lay">Situation</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -170,20 +184,49 @@
|
||||
<script src="{% static 'apps/epic/combobox.js' %}"></script>
|
||||
<script>
|
||||
(function () {
|
||||
// Spread → cross-shape mapping. Celtic Cross variants
|
||||
// (Waite-Smith / Escape Velocity) get the 6-card shape;
|
||||
// every other spread is 3-card (cover/leave/loom only).
|
||||
var SIX_CARD_SPREADS = ['waite-smith', 'escape-velocity'];
|
||||
// Per-spread draw order + position labels — locked in spec
|
||||
// (user 2026-05-19). Each three-card spread uses a DIFFERENT
|
||||
// 3-position subset of the 6 surrounding positions, in a
|
||||
// specific order. The Celtic Cross variants share position
|
||||
// labels (Crown/Beneath/Cover/Cross/Before/Behind — gameroom
|
||||
// vocabulary) but differ in draw order.
|
||||
//
|
||||
// DRAW_ORDER feeds iter 4's deck-click-deposit logic; for
|
||||
// iter 3 it's just baked-in metadata.
|
||||
var DRAW_ORDER = {
|
||||
'past-present-future': ['leave', 'cover', 'loom'],
|
||||
'situation-action-outcome': ['lay', 'cover', 'crown'],
|
||||
'mind-body-spirit': ['crown', 'lay', 'loom'],
|
||||
'desire-obstacle-solution': ['loom', 'cross', 'cover'],
|
||||
'waite-smith': ['cover', 'cross', 'crown', 'lay', 'loom', 'leave'],
|
||||
'escape-velocity': ['cover', 'cross', 'lay', 'leave', 'crown', 'loom'],
|
||||
};
|
||||
var POSITION_LABELS = {
|
||||
'past-present-future': { leave: 'Past', cover: 'Present', loom: 'Future' },
|
||||
'situation-action-outcome': { lay: 'Situation', cover: 'Action', crown: 'Outcome' },
|
||||
'mind-body-spirit': { crown: 'Mind', lay: 'Body', loom: 'Spirit' },
|
||||
'desire-obstacle-solution': { loom: 'Desire', cross: 'Obstacle',cover: 'Solution' },
|
||||
'waite-smith': { crown: 'Crown', leave: 'Beneath', cover: 'Cover', cross: 'Cross', loom: 'Before', lay: 'Behind' },
|
||||
'escape-velocity': { crown: 'Crown', leave: 'Beneath', cover: 'Cover', cross: 'Cross', loom: 'Before', lay: 'Behind' },
|
||||
};
|
||||
var hidden = document.getElementById('id_sea_spread');
|
||||
var cross = document.querySelector('.my-sea-cross');
|
||||
if (!hidden || !cross) return;
|
||||
function syncLabels(spread) {
|
||||
var labels = POSITION_LABELS[spread] || {};
|
||||
cross.querySelectorAll('.sea-pos-label').forEach(function (el) {
|
||||
var pos = el.dataset.position;
|
||||
el.textContent = labels[pos] || '';
|
||||
});
|
||||
}
|
||||
function sync() {
|
||||
var shape = SIX_CARD_SPREADS.indexOf(hidden.value) >= 0
|
||||
? 'six-card' : 'three-card';
|
||||
cross.setAttribute('data-spread-shape', shape);
|
||||
cross.setAttribute('data-spread', hidden.value);
|
||||
syncLabels(hidden.value);
|
||||
}
|
||||
hidden.addEventListener('change', sync);
|
||||
sync();
|
||||
// Exposed for iter 4 (deck-click-deposit reads DRAW_ORDER).
|
||||
window._mySeaDrawOrder = DRAW_ORDER;
|
||||
}());
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user