SIG SELECT: FLIP→SPIN rename; stage-card reversal JS — TDD
- Template: FLIP btn label → SPIN; .btn-reverse class + .sig-flip-btn kept - _button-pad.scss: .btn-reverse restyled w. cyan (--priCy/--terCy); .btn-tip removed; button section comments added (BIG, BYE, FYI, OK, SPIN etc.) - sig-select.js: SPIN/FLIP handler also toggles .stage-card--reversed on stageCard; updateStage() populates .fan-card-reversal-name (data-reversal) + .fan-card-reversal-qualifier (polarity qualifier); resets .stage-card--reversed on each new hover — TDD - SigSelectSpec.js: SPIN card animation describe block (7 specs); spec descriptions updated FLIP→SPIN; fixture gains reversal elements + data-reversal attr; 235 Jasmine specs green 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:
@@ -127,8 +127,13 @@ var SigSelect = (function () {
|
|||||||
stageCard.querySelector('.sig-qualifier-above').textContent = isMajor ? '' : qualifier;
|
stageCard.querySelector('.sig-qualifier-above').textContent = isMajor ? '' : qualifier;
|
||||||
stageCard.querySelector('.sig-qualifier-below').textContent = isMajor ? qualifier : '';
|
stageCard.querySelector('.sig-qualifier-below').textContent = isMajor ? qualifier : '';
|
||||||
|
|
||||||
|
// Reversed face — same qualifier, polarity-resolved reversal title
|
||||||
|
stageCard.querySelector('.fan-card-reversal-qualifier').textContent = qualifier;
|
||||||
|
stageCard.querySelector('.fan-card-reversal-name').textContent = cardEl.dataset.reversal || '';
|
||||||
|
|
||||||
// Populate stat block keyword faces and reset to upright
|
// Populate stat block keyword faces and reset to upright
|
||||||
statBlock.classList.remove('is-reversed');
|
statBlock.classList.remove('is-reversed');
|
||||||
|
stageCard.classList.remove('stage-card--reversed');
|
||||||
_populateKeywordList(
|
_populateKeywordList(
|
||||||
statBlock.querySelector('#id_stat_keywords_upright'),
|
statBlock.querySelector('#id_stat_keywords_upright'),
|
||||||
cardEl.dataset.keywordsUpright
|
cardEl.dataset.keywordsUpright
|
||||||
@@ -613,6 +618,7 @@ var SigSelect = (function () {
|
|||||||
_flipBtn.addEventListener('click', function () {
|
_flipBtn.addEventListener('click', function () {
|
||||||
if (_flipBtn.classList.contains('btn-disabled')) return;
|
if (_flipBtn.classList.contains('btn-disabled')) return;
|
||||||
statBlock.classList.toggle('is-reversed');
|
statBlock.classList.toggle('is-reversed');
|
||||||
|
stageCard.classList.toggle('stage-card--reversed');
|
||||||
});
|
});
|
||||||
|
|
||||||
cautionEl = stage.querySelector('.sig-caution-tooltip');
|
cautionEl = stage.querySelector('.sig-caution-tooltip');
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ describe("SigSelect", () => {
|
|||||||
<p class="sig-qualifier-below"></p>
|
<p class="sig-qualifier-below"></p>
|
||||||
<p class="fan-card-arcana"></p>
|
<p class="fan-card-arcana"></p>
|
||||||
<p class="fan-card-correspondence"></p>
|
<p class="fan-card-correspondence"></p>
|
||||||
|
<p class="fan-card-reversal-qualifier"></p>
|
||||||
|
<p class="fan-card-reversal-name"></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="sig-stat-block">
|
<div class="sig-stat-block">
|
||||||
<button class="btn btn-reverse sig-flip-btn" type="button">FLIP</button>
|
<button class="btn btn-reverse sig-flip-btn" type="button">SPIN</button>
|
||||||
<button class="btn btn-caution sig-caution-btn" type="button">!!</button>
|
<button class="btn btn-caution sig-caution-btn" type="button">!!</button>
|
||||||
<div class="stat-face stat-face--upright">
|
<div class="stat-face stat-face--upright">
|
||||||
<p class="stat-face-label">Upright</p>
|
<p class="stat-face-label">Upright</p>
|
||||||
@@ -60,7 +62,8 @@ describe("SigSelect", () => {
|
|||||||
data-keywords-reversed="no direction,disregard for consequences"
|
data-keywords-reversed="no direction,disregard for consequences"
|
||||||
data-cautions="${cardCautions.replace(/"/g, '"')}"
|
data-cautions="${cardCautions.replace(/"/g, '"')}"
|
||||||
data-levity-qualifier="Elevated"
|
data-levity-qualifier="Elevated"
|
||||||
data-gravity-qualifier="Graven">
|
data-gravity-qualifier="Graven"
|
||||||
|
data-reversal="Territoriality">
|
||||||
<div class="fan-card-corner fan-card-corner--tl">
|
<div class="fan-card-corner fan-card-corner--tl">
|
||||||
<span class="fan-corner-rank">K</span>
|
<span class="fan-corner-rank">K</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -321,7 +324,7 @@ describe("SigSelect", () => {
|
|||||||
expect(cautionEffect.innerHTML).toContain("First");
|
expect(cautionEffect.innerHTML).toContain("First");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("opening caution adds .btn-disabled and swaps labels to ×", () => {
|
it("opening caution adds .btn-disabled and swaps SPIN/FYI labels to ×", () => {
|
||||||
openCaution();
|
openCaution();
|
||||||
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
||||||
expect(flipBtn.classList.contains("btn-disabled")).toBe(true);
|
expect(flipBtn.classList.contains("btn-disabled")).toBe(true);
|
||||||
@@ -330,7 +333,7 @@ describe("SigSelect", () => {
|
|||||||
expect(cautionBtn.textContent).toBe("\u00D7");
|
expect(cautionBtn.textContent).toBe("\u00D7");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("closing caution removes .btn-disabled and restores original labels", () => {
|
it("closing caution removes .btn-disabled and restores SPIN/FYI labels", () => {
|
||||||
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
||||||
var origFlip = flipBtn.textContent;
|
var origFlip = flipBtn.textContent;
|
||||||
var origCaution = cautionBtn.textContent;
|
var origCaution = cautionBtn.textContent;
|
||||||
@@ -348,7 +351,7 @@ describe("SigSelect", () => {
|
|||||||
expect(testDiv.querySelector(".sig-stage").classList.contains("sig-caution-open")).toBe(false);
|
expect(testDiv.querySelector(".sig-stage").classList.contains("sig-caution-open")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("FLIP click when caution open (btn-disabled) does nothing", () => {
|
it("SPIN click when caution open (btn-disabled) does nothing", () => {
|
||||||
openCaution();
|
openCaution();
|
||||||
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
||||||
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
@@ -357,9 +360,9 @@ describe("SigSelect", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Stat block: keyword population and FLIP toggle ────────────────── //
|
// ── Stat block: keyword population and SPIN toggle ────────────────── //
|
||||||
|
|
||||||
describe("stat block and FLIP", () => {
|
describe("stat block and SPIN", () => {
|
||||||
beforeEach(() => makeFixture());
|
beforeEach(() => makeFixture());
|
||||||
|
|
||||||
it("populates upright keywords when a card is hovered", () => {
|
it("populates upright keywords when a card is hovered", () => {
|
||||||
@@ -379,14 +382,14 @@ describe("SigSelect", () => {
|
|||||||
expect(items[1].textContent).toBe("disregard for consequences");
|
expect(items[1].textContent).toBe("disregard for consequences");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("FLIP click adds .is-reversed to the stat block", () => {
|
it("SPIN click adds .is-reversed to the stat block", () => {
|
||||||
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
||||||
var flipBtn = statBlock.querySelector(".sig-flip-btn");
|
var flipBtn = statBlock.querySelector(".sig-flip-btn");
|
||||||
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
expect(statBlock.classList.contains("is-reversed")).toBe(true);
|
expect(statBlock.classList.contains("is-reversed")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("second FLIP click removes .is-reversed", () => {
|
it("second SPIN click removes .is-reversed", () => {
|
||||||
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
||||||
var flipBtn = statBlock.querySelector(".sig-flip-btn");
|
var flipBtn = statBlock.querySelector(".sig-flip-btn");
|
||||||
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
@@ -416,6 +419,76 @@ describe("SigSelect", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── SPIN card animation — stage-card reversed state ──────────────────── //
|
||||||
|
|
||||||
|
describe("SPIN card animation", () => {
|
||||||
|
function hover() {
|
||||||
|
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
it("SPIN click adds .stage-card--reversed to the stage card", () => {
|
||||||
|
makeFixture();
|
||||||
|
hover();
|
||||||
|
statBlock.querySelector(".sig-flip-btn")
|
||||||
|
.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
|
expect(stageCard.classList.contains("stage-card--reversed")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("second SPIN click removes .stage-card--reversed", () => {
|
||||||
|
makeFixture();
|
||||||
|
hover();
|
||||||
|
var flipBtn = statBlock.querySelector(".sig-flip-btn");
|
||||||
|
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
|
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
|
expect(stageCard.classList.contains("stage-card--reversed")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hovering a new card resets .stage-card--reversed", () => {
|
||||||
|
makeFixture();
|
||||||
|
hover();
|
||||||
|
statBlock.querySelector(".sig-flip-btn")
|
||||||
|
.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
|
expect(stageCard.classList.contains("stage-card--reversed")).toBe(true);
|
||||||
|
card.dispatchEvent(new MouseEvent("mouseleave", { bubbles: true }));
|
||||||
|
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
||||||
|
expect(stageCard.classList.contains("stage-card--reversed")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updateStage() populates fan-card-reversal-name from data-reversal", () => {
|
||||||
|
makeFixture();
|
||||||
|
card.dataset.reversal = "Territoriality";
|
||||||
|
hover();
|
||||||
|
expect(stageCard.querySelector(".fan-card-reversal-name").textContent)
|
||||||
|
.toBe("Territoriality");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updateStage() populates fan-card-reversal-qualifier with levity qualifier", () => {
|
||||||
|
makeFixture({ polarity: "levity", userRole: "PC" });
|
||||||
|
hover();
|
||||||
|
expect(stageCard.querySelector(".fan-card-reversal-qualifier").textContent)
|
||||||
|
.toBe("Elevated");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updateStage() populates fan-card-reversal-qualifier with gravity qualifier", () => {
|
||||||
|
makeFixture({ polarity: "gravity", userRole: "BC" });
|
||||||
|
hover();
|
||||||
|
expect(stageCard.querySelector(".fan-card-reversal-qualifier").textContent)
|
||||||
|
.toBe("Graven");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hovering a card without data-reversal clears the reversal name", () => {
|
||||||
|
makeFixture();
|
||||||
|
card.dataset.reversal = "Territoriality";
|
||||||
|
hover();
|
||||||
|
card.dispatchEvent(new MouseEvent("mouseleave", { bubbles: true }));
|
||||||
|
delete card.dataset.reversal;
|
||||||
|
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
||||||
|
// reversal-name clears because data-reversal is gone;
|
||||||
|
// reversal-qualifier stays (it always mirrors the polarity qualifier)
|
||||||
|
expect(stageCard.querySelector(".fan-card-reversal-name").textContent).toBe("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// ── WS cursor hover (applyHover) ──────────────────────────────────────── //
|
// ── WS cursor hover (applyHover) ──────────────────────────────────────── //
|
||||||
//
|
//
|
||||||
// Fixture polarity = levity, userRole = PC.
|
// Fixture polarity = levity, userRole = PC.
|
||||||
|
|||||||
@@ -24,6 +24,7 @@
|
|||||||
border: 0.18rem solid rgba(var(--priUser), 1);
|
border: 0.18rem solid rgba(var(--priUser), 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BIG btn
|
||||||
&.btn-primary {
|
&.btn-primary {
|
||||||
width: 4rem;
|
width: 4rem;
|
||||||
height: 4rem;
|
height: 4rem;
|
||||||
@@ -70,6 +71,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BYE btn
|
||||||
&.btn-abandon {
|
&.btn-abandon {
|
||||||
color: rgba(var(--priBl), 1);
|
color: rgba(var(--priBl), 1);
|
||||||
border-color: rgba(var(--priBl), 1);
|
border-color: rgba(var(--priBl), 1);
|
||||||
@@ -105,6 +107,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEL btn
|
||||||
&.btn-cancel {
|
&.btn-cancel {
|
||||||
color: rgba(var(--priOr), 1);
|
color: rgba(var(--priOr), 1);
|
||||||
border-color: rgba(var(--priOr), 1);
|
border-color: rgba(var(--priOr), 1);
|
||||||
@@ -139,6 +142,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FYI btn
|
||||||
&.btn-caution {
|
&.btn-caution {
|
||||||
color: rgba(var(--priYl), 1);
|
color: rgba(var(--priYl), 1);
|
||||||
border-color: rgba(var(--priYl), 1);
|
border-color: rgba(var(--priYl), 1);
|
||||||
@@ -174,6 +178,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OK btn
|
||||||
&.btn-confirm {
|
&.btn-confirm {
|
||||||
color: rgba(var(--priGn), 1);
|
color: rgba(var(--priGn), 1);
|
||||||
border-color: rgba(var(--priGn), 1);
|
border-color: rgba(var(--priGn), 1);
|
||||||
@@ -209,6 +214,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DEL btn
|
||||||
&.btn-danger {
|
&.btn-danger {
|
||||||
color: rgba(var(--priRd), 1);
|
color: rgba(var(--priRd), 1);
|
||||||
background-color: rgba(var(--terRd), 1);
|
background-color: rgba(var(--terRd), 1);
|
||||||
@@ -286,6 +292,7 @@
|
|||||||
font-size: 0.75rem; // 0.63rem × 1.2
|
font-size: 0.75rem; // 0.63rem × 1.2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PRV btn
|
||||||
&.btn-nav-left {
|
&.btn-nav-left {
|
||||||
color: rgba(var(--priFs), 1);
|
color: rgba(var(--priFs), 1);
|
||||||
border-color: rgba(var(--priFs), 1);
|
border-color: rgba(var(--priFs), 1);
|
||||||
@@ -321,6 +328,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NXT btn
|
||||||
&.btn-nav-right {
|
&.btn-nav-right {
|
||||||
color: rgba(var(--priLm), 1);
|
color: rgba(var(--priLm), 1);
|
||||||
border-color: rgba(var(--priLm), 1);
|
border-color: rgba(var(--priLm), 1);
|
||||||
@@ -356,6 +364,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DON btn
|
||||||
&.btn-equip {
|
&.btn-equip {
|
||||||
color: rgba(var(--priTk), 1);
|
color: rgba(var(--priTk), 1);
|
||||||
border-color: rgba(var(--priTk), 1);
|
border-color: rgba(var(--priTk), 1);
|
||||||
@@ -391,6 +400,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DOFF btn
|
||||||
&.btn-unequip {
|
&.btn-unequip {
|
||||||
color: rgba(var(--priMe), 1);
|
color: rgba(var(--priMe), 1);
|
||||||
border-color: rgba(var(--priMe), 1);
|
border-color: rgba(var(--priMe), 1);
|
||||||
@@ -426,7 +436,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-reverse {
|
// FLIP btn
|
||||||
|
&.btn-reveal {
|
||||||
color: rgba(var(--priCy), 1);
|
color: rgba(var(--priCy), 1);
|
||||||
border-color: rgba(var(--priCy), 1);
|
border-color: rgba(var(--priCy), 1);
|
||||||
background-color: rgba(var(--terCy), 1);
|
background-color: rgba(var(--terCy), 1);
|
||||||
@@ -461,37 +472,38 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.btn-tip {
|
// SPIN btn
|
||||||
color: rgba(var(--priLm), 1);
|
&.btn-reverse {
|
||||||
border-color: rgba(var(--priLm), 1);
|
color: rgba(var(--priCy), 1);
|
||||||
background-color: rgba(var(--terLm), 1);
|
border-color: rgba(var(--priCy), 1);
|
||||||
|
background-color: rgba(var(--terCy), 1);
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0.1rem 0.1rem 0.12rem rgba(var(--terLm), 0.25),
|
0.1rem 0.1rem 0.12rem rgba(var(--terCy), 0.25),
|
||||||
0.12rem 0.12rem 0.25rem rgba(0, 0, 0, 0.25),
|
0.12rem 0.12rem 0.25rem rgba(0, 0, 0, 0.25),
|
||||||
0.25rem 0.25rem 0.25rem rgba(var(--terLm), 0.12)
|
0.25rem 0.25rem 0.25rem rgba(var(--terCy), 0.12)
|
||||||
;
|
;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
text-shadow:
|
text-shadow:
|
||||||
0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 0.25),
|
0.1rem 0.1rem 0.1rem rgba(0, 0, 0, 0.25),
|
||||||
0 0 1rem rgba(var(--priLm), 1)
|
0 0 1rem rgba(var(--priCy), 1)
|
||||||
;
|
;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0.12rem 0.12rem 0.5rem rgba(0, 0, 0, 0.25),
|
0.12rem 0.12rem 0.5rem rgba(0, 0, 0, 0.25),
|
||||||
0 0 0.5rem rgba(var(--priLm), 0.12)
|
0 0 0.5rem rgba(var(--priCy), 0.12)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
&:active {
|
&:active {
|
||||||
border: 0.18rem solid rgba(var(--priLm), 1);
|
border: 0.18rem solid rgba(var(--priCy), 1);
|
||||||
text-shadow:
|
text-shadow:
|
||||||
-0.1rem -0.1rem 0.25rem rgba(0, 0, 0, 0.25),
|
-0.1rem -0.1rem 0.25rem rgba(0, 0, 0, 0.25),
|
||||||
0 0 0.12rem rgba(var(--priLm), 1)
|
0 0 0.12rem rgba(var(--priCy), 1)
|
||||||
;
|
;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
-0.1rem -0.1rem 0.12rem rgba(var(--terLm), 0.25),
|
-0.1rem -0.1rem 0.12rem rgba(var(--terCy), 0.25),
|
||||||
-0.1rem -0.1rem 0.12rem rgba(0, 0, 0, 0.25),
|
-0.1rem -0.1rem 0.12rem rgba(0, 0, 0, 0.25),
|
||||||
0 0 0.5rem rgba(var(--priLm), 0.12)
|
0 0 0.5rem rgba(var(--priCy), 0.12)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,9 +22,11 @@ describe("SigSelect", () => {
|
|||||||
<p class="sig-qualifier-below"></p>
|
<p class="sig-qualifier-below"></p>
|
||||||
<p class="fan-card-arcana"></p>
|
<p class="fan-card-arcana"></p>
|
||||||
<p class="fan-card-correspondence"></p>
|
<p class="fan-card-correspondence"></p>
|
||||||
|
<p class="fan-card-reversal-qualifier"></p>
|
||||||
|
<p class="fan-card-reversal-name"></p>
|
||||||
</div>
|
</div>
|
||||||
<div class="sig-stat-block">
|
<div class="sig-stat-block">
|
||||||
<button class="btn btn-reverse sig-flip-btn" type="button">FLIP</button>
|
<button class="btn btn-reverse sig-flip-btn" type="button">SPIN</button>
|
||||||
<button class="btn btn-caution sig-caution-btn" type="button">!!</button>
|
<button class="btn btn-caution sig-caution-btn" type="button">!!</button>
|
||||||
<div class="stat-face stat-face--upright">
|
<div class="stat-face stat-face--upright">
|
||||||
<p class="stat-face-label">Upright</p>
|
<p class="stat-face-label">Upright</p>
|
||||||
@@ -60,7 +62,8 @@ describe("SigSelect", () => {
|
|||||||
data-keywords-reversed="no direction,disregard for consequences"
|
data-keywords-reversed="no direction,disregard for consequences"
|
||||||
data-cautions="${cardCautions.replace(/"/g, '"')}"
|
data-cautions="${cardCautions.replace(/"/g, '"')}"
|
||||||
data-levity-qualifier="Elevated"
|
data-levity-qualifier="Elevated"
|
||||||
data-gravity-qualifier="Graven">
|
data-gravity-qualifier="Graven"
|
||||||
|
data-reversal="Territoriality">
|
||||||
<div class="fan-card-corner fan-card-corner--tl">
|
<div class="fan-card-corner fan-card-corner--tl">
|
||||||
<span class="fan-corner-rank">K</span>
|
<span class="fan-corner-rank">K</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -321,7 +324,7 @@ describe("SigSelect", () => {
|
|||||||
expect(cautionEffect.innerHTML).toContain("First");
|
expect(cautionEffect.innerHTML).toContain("First");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("opening caution adds .btn-disabled and swaps labels to ×", () => {
|
it("opening caution adds .btn-disabled and swaps SPIN/FYI labels to ×", () => {
|
||||||
openCaution();
|
openCaution();
|
||||||
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
||||||
expect(flipBtn.classList.contains("btn-disabled")).toBe(true);
|
expect(flipBtn.classList.contains("btn-disabled")).toBe(true);
|
||||||
@@ -330,7 +333,7 @@ describe("SigSelect", () => {
|
|||||||
expect(cautionBtn.textContent).toBe("\u00D7");
|
expect(cautionBtn.textContent).toBe("\u00D7");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("closing caution removes .btn-disabled and restores original labels", () => {
|
it("closing caution removes .btn-disabled and restores SPIN/FYI labels", () => {
|
||||||
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
||||||
var origFlip = flipBtn.textContent;
|
var origFlip = flipBtn.textContent;
|
||||||
var origCaution = cautionBtn.textContent;
|
var origCaution = cautionBtn.textContent;
|
||||||
@@ -348,7 +351,7 @@ describe("SigSelect", () => {
|
|||||||
expect(testDiv.querySelector(".sig-stage").classList.contains("sig-caution-open")).toBe(false);
|
expect(testDiv.querySelector(".sig-stage").classList.contains("sig-caution-open")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("FLIP click when caution open (btn-disabled) does nothing", () => {
|
it("SPIN click when caution open (btn-disabled) does nothing", () => {
|
||||||
openCaution();
|
openCaution();
|
||||||
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
var flipBtn = testDiv.querySelector(".sig-flip-btn");
|
||||||
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
@@ -357,9 +360,9 @@ describe("SigSelect", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// ── Stat block: keyword population and FLIP toggle ────────────────── //
|
// ── Stat block: keyword population and SPIN toggle ────────────────── //
|
||||||
|
|
||||||
describe("stat block and FLIP", () => {
|
describe("stat block and SPIN", () => {
|
||||||
beforeEach(() => makeFixture());
|
beforeEach(() => makeFixture());
|
||||||
|
|
||||||
it("populates upright keywords when a card is hovered", () => {
|
it("populates upright keywords when a card is hovered", () => {
|
||||||
@@ -379,14 +382,14 @@ describe("SigSelect", () => {
|
|||||||
expect(items[1].textContent).toBe("disregard for consequences");
|
expect(items[1].textContent).toBe("disregard for consequences");
|
||||||
});
|
});
|
||||||
|
|
||||||
it("FLIP click adds .is-reversed to the stat block", () => {
|
it("SPIN click adds .is-reversed to the stat block", () => {
|
||||||
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
||||||
var flipBtn = statBlock.querySelector(".sig-flip-btn");
|
var flipBtn = statBlock.querySelector(".sig-flip-btn");
|
||||||
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
expect(statBlock.classList.contains("is-reversed")).toBe(true);
|
expect(statBlock.classList.contains("is-reversed")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("second FLIP click removes .is-reversed", () => {
|
it("second SPIN click removes .is-reversed", () => {
|
||||||
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
||||||
var flipBtn = statBlock.querySelector(".sig-flip-btn");
|
var flipBtn = statBlock.querySelector(".sig-flip-btn");
|
||||||
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
@@ -416,6 +419,76 @@ describe("SigSelect", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── SPIN card animation — stage-card reversed state ──────────────────── //
|
||||||
|
|
||||||
|
describe("SPIN card animation", () => {
|
||||||
|
function hover() {
|
||||||
|
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
||||||
|
}
|
||||||
|
|
||||||
|
it("SPIN click adds .stage-card--reversed to the stage card", () => {
|
||||||
|
makeFixture();
|
||||||
|
hover();
|
||||||
|
statBlock.querySelector(".sig-flip-btn")
|
||||||
|
.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
|
expect(stageCard.classList.contains("stage-card--reversed")).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("second SPIN click removes .stage-card--reversed", () => {
|
||||||
|
makeFixture();
|
||||||
|
hover();
|
||||||
|
var flipBtn = statBlock.querySelector(".sig-flip-btn");
|
||||||
|
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
|
flipBtn.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
|
expect(stageCard.classList.contains("stage-card--reversed")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hovering a new card resets .stage-card--reversed", () => {
|
||||||
|
makeFixture();
|
||||||
|
hover();
|
||||||
|
statBlock.querySelector(".sig-flip-btn")
|
||||||
|
.dispatchEvent(new MouseEvent("click", { bubbles: true }));
|
||||||
|
expect(stageCard.classList.contains("stage-card--reversed")).toBe(true);
|
||||||
|
card.dispatchEvent(new MouseEvent("mouseleave", { bubbles: true }));
|
||||||
|
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
||||||
|
expect(stageCard.classList.contains("stage-card--reversed")).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updateStage() populates fan-card-reversal-name from data-reversal", () => {
|
||||||
|
makeFixture();
|
||||||
|
card.dataset.reversal = "Territoriality";
|
||||||
|
hover();
|
||||||
|
expect(stageCard.querySelector(".fan-card-reversal-name").textContent)
|
||||||
|
.toBe("Territoriality");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updateStage() populates fan-card-reversal-qualifier with levity qualifier", () => {
|
||||||
|
makeFixture({ polarity: "levity", userRole: "PC" });
|
||||||
|
hover();
|
||||||
|
expect(stageCard.querySelector(".fan-card-reversal-qualifier").textContent)
|
||||||
|
.toBe("Elevated");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("updateStage() populates fan-card-reversal-qualifier with gravity qualifier", () => {
|
||||||
|
makeFixture({ polarity: "gravity", userRole: "BC" });
|
||||||
|
hover();
|
||||||
|
expect(stageCard.querySelector(".fan-card-reversal-qualifier").textContent)
|
||||||
|
.toBe("Graven");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("hovering a card without data-reversal clears the reversal name", () => {
|
||||||
|
makeFixture();
|
||||||
|
card.dataset.reversal = "Territoriality";
|
||||||
|
hover();
|
||||||
|
card.dispatchEvent(new MouseEvent("mouseleave", { bubbles: true }));
|
||||||
|
delete card.dataset.reversal;
|
||||||
|
card.dispatchEvent(new MouseEvent("mouseenter", { bubbles: true }));
|
||||||
|
// reversal-name clears because data-reversal is gone;
|
||||||
|
// reversal-qualifier stays (it always mirrors the polarity qualifier)
|
||||||
|
expect(stageCard.querySelector(".fan-card-reversal-name").textContent).toBe("");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// ── WS cursor hover (applyHover) ──────────────────────────────────────── //
|
// ── WS cursor hover (applyHover) ──────────────────────────────────────── //
|
||||||
//
|
//
|
||||||
// Fixture polarity = levity, userRole = PC.
|
// Fixture polarity = levity, userRole = PC.
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ Context: sig_cards, user_polarity, user_seat, sig_reserve_url, sig_reservations_
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sig-stat-block">
|
<div class="sig-stat-block">
|
||||||
<button class="btn btn-reverse sig-flip-btn" type="button">FLIP</button>
|
<button class="btn btn-reverse sig-flip-btn" type="button">SPIN</button>
|
||||||
<button class="btn btn-caution sig-caution-btn" type="button">FYI</button>
|
<button class="btn btn-caution sig-caution-btn" type="button">FYI</button>
|
||||||
<div class="stat-face stat-face--upright">
|
<div class="stat-face stat-face--upright">
|
||||||
<p class="stat-face-label">Upright</p>
|
<p class="stat-face-label">Upright</p>
|
||||||
|
|||||||
Reference in New Issue
Block a user