sky.html: snap-binary aperture scroll (wheel ↔ form, full aperture each); SAVE SKY animates scrollTop back to 0 — TDD

post-save the .sky-page aperture flips into scroll-snap-y-mandatory mode: wheel-col & form-col each fill the aperture & carry scroll-snap-align:start, so vertical scroll toggles between them rather than free-flowing through both. Modal-body uses display:contents so the cols become direct flex children of .sky-page (where min-height:100% resolves against the explicit aperture height); wheel-col's aspect-ratio/max-height caps are released under body.sky-saved so the section actually fills the aperture instead of clipping at 480px. SAVE SKY's success branch calls _scrollApertureToTop(), a 280ms RAF loop w. ease-out cubic so the user lands back on the wheel after confirming from the form section. New FT class MySkyApertureSnapScrollTest covers (T1) snap-type:y mandatory + scroll-snap-align:start on both cols, (T2) scrollTop returns to 0 after SAVE SKY click; both red before the SCSS+JS, green after. Snap behavior is gated on body.sky-saved (set by sky_view based on user.sky_chart_data) so the pre-save form-only flow is untouched.

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:
Disco DeDisco
2026-05-08 12:24:11 -04:00
parent 3beedc3f0a
commit 319b787109
4 changed files with 166 additions and 2 deletions

View File

@@ -372,6 +372,101 @@ class MySkyTimezoneRefreshTest(FunctionalTest):
))
class MySkyApertureSnapScrollTest(FunctionalTest):
"""Once sky data is saved, the .sky-page aperture is a snap-binary scroller
(wheel section + form section, each filling the aperture). Clicking SAVE
SKY animates the aperture back to the top (the wheel)."""
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 = "Baltimore, MD, US"
self.gamer.sky_birth_tz = "America/New_York"
self.gamer.sky_birth_lat = 39.2904
self.gamer.sky_birth_lon = -76.6122
self.gamer.sky_birth_dt = datetime(
1990, 6, 15, 12, 0, tzinfo=zoneinfo.ZoneInfo("UTC")
)
self.gamer.save()
self.sky_url = self.live_server_url + "/dashboard/sky/"
# ── T1 ───────────────────────────────────────────────────────────────────
def test_aperture_has_snap_y_mandatory_when_sky_saved(self):
"""When sky is saved, the .sky-page aperture has scroll-snap-type:y mandatory
and both wheel-col & form-col have scroll-snap-align:start. Without these
the layout is a free scroll instead of the binary wheel<->form toggle."""
self.browser.set_window_size(820, 520)
self.create_pre_authenticated_session("stargazer@test.io")
self.browser.get(self.sky_url)
self.wait_for(lambda: self.browser.find_element(By.CLASS_NAME, "sky-page"))
styles = self.browser.execute_script("""
const ap = document.querySelector('.sky-page');
const wheel = document.querySelector('.sky-page .sky-wheel-col');
const form = document.querySelector('.sky-page .sky-form-col');
return {
snapType: getComputedStyle(ap).scrollSnapType,
wheelAlign: getComputedStyle(wheel).scrollSnapAlign,
formAlign: getComputedStyle(form).scrollSnapAlign,
};
""")
self.assertIn("y", styles["snapType"])
self.assertIn("mandatory", styles["snapType"])
self.assertEqual(styles["wheelAlign"], "start")
self.assertEqual(styles["formAlign"], "start")
# ── T2 ───────────────────────────────────────────────────────────────────
def test_save_sky_scrolls_aperture_back_to_top(self):
"""Clicking SAVE SKY from the form section animates the aperture's
scrollTop back to 0 (the wheel)."""
self.browser.set_window_size(820, 520)
self.create_pre_authenticated_session("stargazer@test.io")
self.browser.get(self.sky_url)
self.wait_for(lambda: self.browser.find_element(By.CLASS_NAME, "sky-page"))
# Mock /sky/save so the click resolves without real server work
self.browser.execute_script("""
window._origFetch = window.fetch;
window.fetch = function(url, opts) {
if (typeof url === 'string' && url.includes('/sky/save')) {
return Promise.resolve({
ok: true,
json: () => Promise.resolve({saved: true}),
});
}
return window._origFetch(url, opts);
};
""")
# Scroll the aperture down to the form section
self.browser.execute_script(
"document.querySelector('.sky-page').scrollTop = 9999;"
)
self.wait_for(lambda: self.assertGreater(
self.browser.execute_script(
"return document.querySelector('.sky-page').scrollTop;"
),
10,
))
self.browser.find_element(By.ID, "id_sky_confirm").click()
# After save resolves, aperture scrollTop animates back to 0
self.wait_for(lambda: self.assertEqual(
self.browser.execute_script(
"return Math.round(document.querySelector('.sky-page').scrollTop);"
),
0,
))
class MySkyWheelConjunctionTest(FunctionalTest):
"""Tick lines, z-raise, and dual tooltip for conjunct planets."""