From 8a240217394bb9a5ce90981f75d19677c7ca37f6 Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Thu, 16 Apr 2026 14:40:52 -0400 Subject: [PATCH] =?UTF-8?q?MY=20SKY:=20full-page=20layout=20polish=20?= =?UTF-8?q?=E2=80=94=20aperture=20pinning,=20wheel-above-form,=20centred?= =?UTF-8?q?=20wheel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - sky_view passes page_class="page-sky" so the footer pins correctly - _natus.scss: page-sky aperture block (mirrors page-wallet pattern); sky-page stacks wheel above form via flex order + page-level scroll; wheel col uses aspect-ratio:1/1 so it takes natural square size without compressing to fit the form - natus-wheel.js: _layout() sets viewBox + preserveAspectRatio="xMidYMid meet" so the wheel is always centred inside the SVG element regardless of its aspect ratio (fixes left-alignment in the dashboard applet) Co-Authored-By: Claude Sonnet 4.6 --- src/apps/dashboard/views.py | 1 + .../static/apps/gameboard/natus-wheel.js | 4 + src/functional_tests/test_my_sky.py | 82 +++++++++++++++++++ src/static_src/scss/_natus.scss | 81 ++++++++++++++++++ .../dashboard/_partials/_applet-my-sky.html | 19 +++++ 5 files changed, 187 insertions(+) diff --git a/src/apps/dashboard/views.py b/src/apps/dashboard/views.py index 08ea0e0..953d9aa 100644 --- a/src/apps/dashboard/views.py +++ b/src/apps/dashboard/views.py @@ -317,6 +317,7 @@ def sky_view(request): "saved_sky": request.user.sky_chart_data, "saved_birth_dt": request.user.sky_birth_dt, "saved_birth_place": request.user.sky_birth_place, + "page_class": "page-sky", }) diff --git a/src/apps/gameboard/static/apps/gameboard/natus-wheel.js b/src/apps/gameboard/static/apps/gameboard/natus-wheel.js index 15f455f..d1766ba 100644 --- a/src/apps/gameboard/static/apps/gameboard/natus-wheel.js +++ b/src/apps/gameboard/static/apps/gameboard/natus-wheel.js @@ -118,6 +118,10 @@ const NatusWheel = (() => { const size = Math.min(rect.width || 400, rect.height || 400); _cx = size / 2; _cy = size / 2; + // viewBox pins the coordinate system to size×size; preserveAspectRatio + // centres it inside the SVG element regardless of its aspect ratio. + svgEl.setAttribute('viewBox', `0 0 ${size} ${size}`); + svgEl.setAttribute('preserveAspectRatio', 'xMidYMid meet'); _r = size * 0.46; // leave a small margin R = { diff --git a/src/functional_tests/test_my_sky.py b/src/functional_tests/test_my_sky.py index 00cced9..9c246b2 100644 --- a/src/functional_tests/test_my_sky.py +++ b/src/functional_tests/test_my_sky.py @@ -5,6 +5,7 @@ natus (natal chart) interface where the user can save their personal sky to their account (stored on the User model, independent of any game room). """ +from selenium.webdriver.common.action_chains import ActionChains from selenium.webdriver.common.by import By from apps.applets.models import Applet @@ -13,6 +14,35 @@ from apps.lyric.models import User from .base import FunctionalTest +# Minimal chart fixture — matches the NatusWheel data shape. +_CHART_FIXTURE = { + "planets": { + "Sun": {"sign": "Pisces", "degree": 340.0, "retrograde": False}, + "Moon": {"sign": "Gemini", "degree": 72.0, "retrograde": False}, + "Mercury": {"sign": "Aquarius", "degree": 310.0, "retrograde": False}, + "Venus": {"sign": "Aries", "degree": 10.0, "retrograde": False}, + "Mars": {"sign": "Capricorn", "degree": 280.0, "retrograde": False}, + "Jupiter": {"sign": "Cancer", "degree": 100.0, "retrograde": False}, + "Saturn": {"sign": "Capricorn", "degree": 290.0, "retrograde": True}, + "Uranus": {"sign": "Capricorn", "degree": 285.0, "retrograde": False}, + "Neptune": {"sign": "Capricorn", "degree": 283.0, "retrograde": False}, + "Pluto": {"sign": "Scorpio", "degree": 218.0, "retrograde": False}, + }, + "houses": { + "cusps": [10, 40, 70, 100, 130, 160, 190, 220, 250, 280, 310, 340], + "asc": 10.0, "mc": 100.0, + }, + "elements": {"Fire": 1, "Water": 2, "Stone": 4, "Air": 1, "Time": 0, "Space": 1}, + "aspects": [], + "distinctions": { + "1": 1, "2": 0, "3": 0, "4": 1, "5": 0, "6": 0, + "7": 0, "8": 0, "9": 0, "10": 4, "11": 0, "12": 0, + }, + "house_system": "O", + "timezone": "America/New_York", +} + + class MySkyAppletTest(FunctionalTest): """My Sky applet appears on the dashboard and links to the sky page.""" @@ -116,3 +146,55 @@ class MySkyLocalStorageTest(FunctionalTest): self.assertEqual(values["lon"], "-0.1278") self.assertEqual(values["place"], "London, UK") self.assertEqual(values["tz"], "Europe/London") + + +class MySkyAppletWheelTest(FunctionalTest): + """Saved natal chart renders as an interactive wheel inside the My Sky applet.""" + + def setUp(self): + super().setUp() + Applet.objects.get_or_create( + slug="my-sky", + defaults={"name": "My Sky", "grid_cols": 6, "grid_rows": 6, "context": "dashboard"}, + ) + self.gamer = User.objects.create(email="stargazer@test.io") + self.gamer.sky_chart_data = _CHART_FIXTURE + self.gamer.sky_birth_place = "Lindenwold, NJ, US" + self.gamer.save() + + # ── T3 ─────────────────────────────────────────────────────────────────── + + def test_saved_sky_wheel_renders_with_tooltips_in_applet(self): + """When the user has saved sky data, the natal wheel appears in the My Sky + applet with working element-ring and planet tooltips.""" + self.create_pre_authenticated_session("stargazer@test.io") + self.browser.get(self.live_server_url) + + # 1. Wheel SVG is drawn inside the applet + self.wait_for(lambda: self.assertTrue( + self.browser.find_element( + By.CSS_SELECTOR, "#id_applet_my_sky .nw-root" + ) + )) + + # 2. Hovering an element-ring slice shows the tooltip + slice_el = self.browser.find_element( + By.CSS_SELECTOR, "#id_applet_my_sky .nw-element-group" + ) + ActionChains(self.browser).move_to_element(slice_el).perform() + self.wait_for(lambda: self.assertEqual( + self.browser.find_element(By.ID, "id_natus_tooltip") + .value_of_css_property("display"), + "block", + )) + + # 3. Hovering a planet also shows the tooltip + planet_el = self.browser.find_element( + By.CSS_SELECTOR, "#id_applet_my_sky .nw-planet-group" + ) + ActionChains(self.browser).move_to_element(planet_el).perform() + self.wait_for(lambda: self.assertEqual( + self.browser.find_element(By.ID, "id_natus_tooltip") + .value_of_css_property("display"), + "block", + )) diff --git a/src/static_src/scss/_natus.scss b/src/static_src/scss/_natus.scss index 6604c7d..9d72709 100644 --- a/src/static_src/scss/_natus.scss +++ b/src/static_src/scss/_natus.scss @@ -507,6 +507,87 @@ body[class*="-light"] #id_natus_tooltip { .tt-title--pu { color: rgba(var(--priPu), 1); } } +// ── My Sky dashboard applet ─────────────────────────────────────────────────── + +#id_applet_my_sky { + display: flex; + flex-direction: column; + + h2 { flex-shrink: 0; } + + .natus-svg { + flex: 1; + min-height: 0; + max-width: none; + max-height: none; + align-self: center; + } +} + +// ── Sky full page (aperture + column layout) ────────────────────────────────── + +html:has(body.page-sky) { + overflow: hidden; +} + +body.page-sky { + overflow: hidden; + + .container { + overflow: hidden; + display: flex; + flex-direction: column; + flex: 1; + min-height: 0; + } + + .row { + flex-shrink: 0; + } +} + +// Sky page fills the aperture; its content can scroll past the bottom edge +.sky-page { + position: relative; + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + overflow-y: auto; +} + +// Stack wheel above form; allow body to grow past viewport (page scrolls, not body) +.sky-page .natus-modal-body { + flex-direction: column; + flex-shrink: 0; +} + +// Wheel takes its natural square size from its width — never shrinks for the form +.sky-page .natus-wheel-col { + order: -1; + flex: 0 0 auto; + width: 100%; + aspect-ratio: 1 / 1; + max-width: 480px; + max-height: 480px; + align-self: center; +} + +// Form col runs horizontally below the wheel (same compact pattern as narrow-portrait modal) +.sky-page .natus-form-col { + flex: 0 0 auto; + flex-direction: row; + align-items: flex-end; + border-right: none; + border-top: 0.1rem solid rgba(var(--terUser), 0.12); +} + +.sky-page .natus-form-main { + flex: 1; + min-width: 0; + overflow-y: visible; +} + // ── Sidebar z-index sink (landscape sidebars must go below backdrop) ─────────── @media (orientation: landscape) { diff --git a/src/templates/apps/dashboard/_partials/_applet-my-sky.html b/src/templates/apps/dashboard/_partials/_applet-my-sky.html index 4263480..bba6da5 100644 --- a/src/templates/apps/dashboard/_partials/_applet-my-sky.html +++ b/src/templates/apps/dashboard/_partials/_applet-my-sky.html @@ -1,6 +1,25 @@ +{% load static %}

My Sky

+ {% if request.user.sky_chart_data %} + + {{ request.user.sky_chart_data|json_script:"id_my_sky_data" }} + {% endif %}
+ +{% if request.user.sky_chart_data %} + + + + +{% endif %}