My Sea form col + SPREAD dropdown w. 3-card/6-card section dividers — Sprint 5 iter 3 of My Sea roadmap — TDD
Picker phase form col: SPREAD combobox w. 6 spread options under 2 horizontal section dividers ("3-card spreads" / "6-card spreads"), reversal-% caption, GRAVITY + LEVITY deck swatches, LOCK HAND + DEL btns. Default = Situation, Action, Outcome (a 3-card spread). Selecting a 6-card spread (Celtic Cross Waite-Smith or Escape Velocity) swaps `.my-sea-cross[data-spread-shape]` from `three-card` to `six-card` — revealing the crown / lay / cross cells that the default 3-card variants hide.
Naming correction (user-locked): the spread itself is a "three-card spread" not a "three-card cross" — "cross" stays scoped to the Celtic Cross variants (6-card spreads). CSS class `.my-sea-cross` carries grid-container semantics regardless of which spread shape is active; the spread-vs-cross distinction lives at the spread-name layer only.
- **View** (gameboard/views.py): `my_sea` adds `default_spread = "situation-action-outcome"` + `reversals_pct = 25` context keys.
- **Template** (my_sea.html): renders all 7 cross cells (crown/leave/core+cover+cross/loom/lay) unconditionally + adds `data-spread-shape="three-card"` to `.my-sea-cross`. Form col DRY-reuses gameroom `_sea_overlay.html`'s `.sea-form-col` shape — `.sea-form-main` w. `.sea-field` (SPREAD label + reversal hint + custom combobox) + `.sea-stacks` (GRAVITY + LEVITY swatches) + `.sea-form-actions` (LOCK HAND + DEL). 6 options + 2 dividers in the combobox `<ul>`; dividers are `role="presentation"` so `combobox.js` skips them naturally. Inline IIFE listens for the hidden `<input id="id_sea_spread">`'s `change` event + sets `.my-sea-cross`'s `data-spread-shape` based on whether the value is in `['waite-smith', 'escape-velocity']`. No new combobox.js wiring — the existing module's `change`-bubbling contract feeds straight in.
- **SCSS** (_gameboard.scss):
- `.my-sea-cross[data-spread-shape="three-card"]` — single-row `"leave core loom"` grid + `display: none` on crown/lay/cross.
- `.my-sea-cross[data-spread-shape="six-card"]` — inherits the gameroom `.sea-cross`'s 3×3 grid + reveals all cells.
- `.sea-select-divider` — section header style mirrors `.kit-bag-label`'s small-uppercase-underlined-letter-spaced --quaUser/0.75 treatment but HORIZONTAL (kit-bag uses `writing-mode: vertical-rl`; dropdown menus are flat). `pointer-events: none` belt-and-braces against accidental click/hover.
- `.my-sea-form-col` — width-constrains the form col so the picker's cross + form sit side-by-side.
**Iter-2 contract updated** (cells in DOM, hidden via CSS for 3-card default):
- FT `test_picker_does_not_render_forsaken_positions` → renamed to `test_picker_hides_six_card_only_positions_by_default` — asserts the 3 cells are in the DOM but `is_displayed() == False` so iter-3's spread switch can reveal them via CSS without re-rendering.
- IT `test_picker_does_not_render_forsaken_positions` → renamed to `test_picker_renders_six_card_only_positions_for_spread_switch` — assertContains the classes (server now renders them unconditionally).
**Tests**:
- 4 FTs in new `MySeaSpreadFormTest`: combobox renders 6 options + 2 dividers w. correct labels, default is Situation/Action/Outcome (hidden input value + visible current-label span + cross's data-spread-shape), picking Celtic Cross flips data-spread-shape to six-card + reveals crown/lay/cross, form col carries DECKS swatches + LOCK HAND + DEL + reversal-% caption. Combobox `<li>` options are inside `aria-expanded='false'` listbox → use `get_attribute("textContent")` not `.text` (which returns "" for Selenium-hidden elements).
- 7 ITs in new `MySeaSpreadFormTemplateTest`: default_spread + reversals_pct context keys, all 6 options + both labels render, 2 dividers render w. expected text, default option carries aria-selected="true", cross's initial data-spread-shape="three-card", form col DECKS + buttons + reversal hint render.
Tests: 32/32 FT green across test_bill_my_sign + test_game_my_sea; 1048/1048 IT/UT green in 52s.
Card-draw mechanics (clicking a deck swatch deposits a card into the next empty slot; LOCK HAND commits the draw) defer to iter 4 — this iter ships the spread-selection + layout-shape switch UI; the buttons are stubs (LOCK HAND starts disabled, DEL is a placeholder).
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,16 +66,19 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Picker phase — three-card cross w. sig pinned in core + #}
|
||||
{# cover (overlaid on sig) + leave (left) + loom (right). #}
|
||||
{# Crown / lay / cross from the gameroom's 6-position spread #}
|
||||
{# are forsaken in the solo flow per user-locked spec. Hidden #}
|
||||
{# until FREE DRAW click swaps data-phase to `picker` (see #}
|
||||
{# inline JS below); form col (spread dropdown / decks / #}
|
||||
{# LOCK HAND / DEL) lands in iter 3. #}
|
||||
{# 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`. #}
|
||||
<div class="my-sea-picker" style="display:none">
|
||||
<div class="sea-cards-col">
|
||||
<div class="sea-cross my-sea-cross">
|
||||
<div class="sea-cross my-sea-cross" data-spread-shape="three-card">
|
||||
<div class="sea-crucifix-cell sea-pos-crown">
|
||||
<div class="sea-card-slot sea-card-slot--empty"></div>
|
||||
</div>
|
||||
<div class="sea-crucifix-cell sea-pos-leave">
|
||||
<div class="sea-card-slot sea-card-slot--empty"></div>
|
||||
</div>
|
||||
@@ -88,13 +91,101 @@
|
||||
<div class="sea-pos-cover">
|
||||
<div class="sea-card-slot sea-card-slot--empty"></div>
|
||||
</div>
|
||||
<div class="sea-pos-cross">
|
||||
<div class="sea-card-slot sea-card-slot--empty sea-card-slot--crossing"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sea-crucifix-cell sea-pos-loom">
|
||||
<div class="sea-card-slot sea-card-slot--empty"></div>
|
||||
</div>
|
||||
<div class="sea-crucifix-cell sea-pos-lay">
|
||||
<div class="sea-card-slot sea-card-slot--empty"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Form col — SPREAD combobox + DECKS swatches + LOCK #}
|
||||
{# HAND / DEL. DRY w. gameroom `_sea_overlay.html`'s #}
|
||||
{# `.sea-form-col` shape; my-sea-specific differences: #}
|
||||
{# (a) 6 spread options under 2 section dividers, #}
|
||||
{# (b) default = situation-action-outcome (3-card), #}
|
||||
{# (c) no `.sea-modal-header` (the gateway IS the page). #}
|
||||
<div class="sea-form-col my-sea-form-col">
|
||||
<div class="sea-form-main">
|
||||
<div class="sea-field">
|
||||
<label for="id_sea_spread" id="id_sea_spread_label">Spread</label>
|
||||
<p class="sea-reversal-hint">{{ reversals_pct|default:25 }}% reversals</p>
|
||||
<input type="hidden" id="id_sea_spread" name="spread"
|
||||
value="{{ default_spread }}">
|
||||
<div class="sea-select"
|
||||
data-combobox
|
||||
data-combobox-target="id_sea_spread"
|
||||
role="combobox"
|
||||
aria-expanded="false"
|
||||
aria-haspopup="listbox"
|
||||
aria-labelledby="id_sea_spread_label"
|
||||
tabindex="0">
|
||||
<span class="sea-select-current">Situation, Action, Outcome</span>
|
||||
<span class="sea-select-arrow" aria-hidden="true">▾</span>
|
||||
<ul class="sea-select-list" role="listbox">
|
||||
<li role="presentation" class="sea-select-divider">3-card spreads</li>
|
||||
<li role="option" data-value="past-present-future" aria-selected="false">Past, Present, Future</li>
|
||||
<li role="option" data-value="situation-action-outcome" aria-selected="true">Situation, Action, Outcome</li>
|
||||
<li role="option" data-value="mind-body-spirit" aria-selected="false">Mind, Body, Spirit</li>
|
||||
<li role="option" data-value="desire-obstacle-solution" aria-selected="false">Desire, Obstacle, Solution</li>
|
||||
<li role="presentation" class="sea-select-divider">6-card spreads</li>
|
||||
<li role="option" data-value="waite-smith" aria-selected="false">Celtic Cross, Waite-Smith</li>
|
||||
<li role="option" data-value="escape-velocity" aria-selected="false">Celtic Cross, Escape Velocity</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sea-stacks">
|
||||
<span class="sea-stacks-label">DECKS</span>
|
||||
<div class="sea-deck-stack sea-deck-stack--gravity">
|
||||
<div class="sea-stack-face">
|
||||
<button class="btn btn-reveal sea-stack-ok" type="button">FLIP</button>
|
||||
</div>
|
||||
<span class="sea-stack-name">Gravity</span>
|
||||
</div>
|
||||
<div class="sea-deck-stack sea-deck-stack--levity">
|
||||
<div class="sea-stack-face">
|
||||
<button class="btn btn-reveal sea-stack-ok" type="button">FLIP</button>
|
||||
</div>
|
||||
<span class="sea-stack-name">Levity</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sea-form-actions">
|
||||
<button type="button" id="id_sea_lock_hand" class="btn btn-primary" disabled>
|
||||
LOCK HAND
|
||||
</button>
|
||||
<button type="button" id="id_sea_del" class="btn btn-danger">
|
||||
DEL
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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'];
|
||||
var hidden = document.getElementById('id_sea_spread');
|
||||
var cross = document.querySelector('.my-sea-cross');
|
||||
if (!hidden || !cross) return;
|
||||
function sync() {
|
||||
var shape = SIX_CARD_SPREADS.indexOf(hidden.value) >= 0
|
||||
? 'six-card' : 'three-card';
|
||||
cross.setAttribute('data-spread-shape', shape);
|
||||
}
|
||||
hidden.addEventListener('change', sync);
|
||||
sync();
|
||||
}());
|
||||
</script>
|
||||
|
||||
<script src="{% static 'apps/epic/room.js' %}"></script>
|
||||
<script>
|
||||
|
||||
Reference in New Issue
Block a user