A.4 cont.: deck back-image renders inside card-stack icon + kit-bag dialog Deck section adopts the icon + size+pattern polish — TDD. Three follow-up improvements after user browser-verified A.4's first cut: (1) image-equipped decks (Minchiate today, future Earthman) now render the deck's actual <deck-slug>-back.png as the card-stack icon's visible faces instead of the placeholder --priUser solid fill — feels like a real deck, not a generic stand-in. (2) The kit-bag dialog Deck section (#id_kit_bag_dialog .kit-bag-deck) gets the same new card-stack icon (was still showing the old fa-regular fa-id-badge), with (×2) tooltip decoration on polarized decks for consistency w. the gameboard applet. (3) Visual polish: icon bumped 1.5× (1.5rem → 2.25rem width; 2.4rem → 3.6rem height, 5:8 aspect preserved); SVG <pattern> switched from patternUnits=userSpaceOnUse (which painted the image at fixed user-space coordinates and let the rect slide out from under it on hover, reading as "low opacity" to the user) to patternUnits=objectBoundingBox + patternContentUnits=objectBoundingBox (transform-aware — image tracks the rect through rest-state offsets + hover fan-out). New DeckVariant.back_image_url property mirrors A.2's TarotCard.image_url pattern: returns full static-asset URL for <deck-slug>-back.png when has_card_images=True, else empty string. Template partial _deck_stack_icon.html extended w. conditional <defs><pattern> block that renders only when deck.has_card_images is true; each of the 3 card rects then carries an inline style="fill: url(#deck-back-<short_key>)" overriding the SCSS default fill: rgba(--priUser, 1) (inline style beats CSS, the only way to opt out of the cascade default per-element). When no deck is passed (kit-bag placeholder branch) or deck has no images (Earthman + RWS), the partial falls through to the placeholder fill — single template handles both modes. _kit_bag_panel.html Deck section: equipped-deck branch swaps <i class="fa-regular fa-id-badge"> for {% include _deck_stack_icon.html with deck=equipped_deck %} + adds (×2) span in --terUser for equipped_deck.is_polarized; placeholder branch swaps for the same include without deck= so the partial's conditional falls through. SCSS reorg: lifted the .deck-stack-icon base rules out of the #id_applet_game_kit nest (they were scoped to gameboard's Game Kit applet only) into top-level scope so the same SCSS applies in the kit-bag dialog context too. Hover/active/focus trigger selector list broadened to cover .deck-stack-icon itself + .token.deck-variant wrapper + .kit-bag-deck wrapper. 4 new ITs total: 2 in GameboardViewTest (image-equipped Minchiate's <pattern> defines + inline fill style on all 3 rects + asset URL ref; non-image Earthman has NEITHER pattern nor inline fill); 2 in dashboard.KitBagViewTest (kit-bag Deck section renders svg.deck-stack-icon + lacks fa-id-badge; polarized equipped deck tooltip carries .tt-x2 — element-presence assertion since literal "×2" character had encoding issues in the dashboard test file vs the gameboard one, which is fine since the template-side rendering of the literal × is exercised by the parent template). Tests: 4 new green; 1297/1297 IT+UT total green (69s; +4 from A.4's 1293). Visual verify pending: refresh /gameboard/ → Minchiate icon should show 3 stacked Minchiate card-backs at 1.5× size, fan out on hover w. back image tracking; refresh kit-bag dialog → same icon visible in Deck section w. (×2) on Earthman tooltip

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-25 01:01:05 -04:00
parent b9bb73db69
commit d26c45bf77
6 changed files with 181 additions and 58 deletions

View File

@@ -459,6 +459,41 @@ class GameboardViewTest(TestCase):
"Non-polarized RWS deck must not show (×2) in tooltip",
)
def test_image_equipped_deck_icon_uses_back_image_pattern(self):
"""Sprint A.4 follow-up — image-equipped decks (Minchiate today,
future Earthman) render the SVG card-stack icon w. an inline <pattern>
referencing the deck's <deck-slug>-back.png + inline style `fill:
url(#deck-back-<short_key>)` on each rect so the actual card-back
renders instead of the placeholder `--priUser` solid fill."""
from apps.epic.models import DeckVariant
minchiate = DeckVariant.objects.get(slug="minchiate-fiorentine-1860-1890")
self.user.unlocked_decks.add(minchiate)
response = self.client.get("/gameboard/")
import lxml.html
parsed = lxml.html.fromstring(response.content)
deck = parsed.cssselect("#id_game_kit #id_kit_minchiate_deck")[0]
html = lxml.html.tostring(deck, encoding="unicode")
self.assertIn("deck-back-minchiate", html,
"Image-equipped deck's SVG must define a back-image <pattern>")
self.assertIn("minchiate-fiorentine-1860-1890-back.png", html,
"Pattern must reference the deck's back-image asset URL")
self.assertGreaterEqual(
html.count("fill: url(#deck-back-minchiate)"), 3,
"Each of the 3 card rects must override its fill via inline style",
)
def test_nonimage_deck_icon_has_no_back_pattern(self):
"""Earthman (has_card_images=False until its art lands) renders the
placeholder fill — NO <pattern> defs, no inline-style fill override,
rects fall through to the SCSS default `--priUser` solid fill."""
deck = self.parsed.cssselect("#id_game_kit #id_kit_earthman_deck")[0]
import lxml.html
html = lxml.html.tostring(deck, encoding="unicode")
self.assertNotIn("deck-back-earthman", html,
"Non-image deck must not define a back-image <pattern>")
self.assertNotIn("fill: url(#deck-back-", html,
"Non-image deck rects must use the SCSS default fill")
class GameboardDeckInUseTest(TestCase):
"""Sprint 2: game kit applet renders in-use state for a deck assigned to an active seat."""