note palette: swatch previews body palette, NVM reverts, OK saves sitewide; note_set_palette also saves user.palette — TDD

- note-page.js: body class swap on swatch click; 10s auto-revert timer; NVM reverts;
  .note-item--active persists border/glow while modal open; .previewing on swatch
- billboard/views.py: note_set_palette also saves user.palette via _unlocked_palettes_for_user
- _note.scss: .note-swatch-body gradient (palette vars cascade from parent palette-* class);
  .previewing state; .note-item--active; note-palette-modal tooltip glass;
  note-palette-confirm floats below modal (position:absolute, out of flow)
- my_notes.html: note-item__body wrapper; image-box right; swatch row OK buttons removed
- FTs: T2a URL fix (/recognition → /my-notes); T2b split into preview+persist & NVM tests;
  NoteSetPaletteViewTest.test_also_saves_user_palette IT

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-04-22 23:54:05 -04:00
parent e8687dc050
commit cd5252c185
7 changed files with 258 additions and 98 deletions

View File

@@ -156,7 +156,7 @@ class StargazerNoteFromDashboardTest(FunctionalTest):
# FYI navigates to Note page
fyi.click()
self.wait_for(
lambda: self.assertRegex(self.browser.current_url, r"/billboard/recognition")
lambda: self.assertRegex(self.browser.current_url, r"/billboard/my-notes")
)
# Note page: one Stargazer item
@@ -172,62 +172,106 @@ class StargazerNoteFromDashboardTest(FunctionalTest):
# ── T2b ──────────────────────────────────────────────────────────────────
def test_note_page_palette_modal_flow(self):
"""Note page palette modal: image-box opens modal, swatch preview,
body-click restores modal, OK raises confirm submenu, confirm sets palette."""
Note.objects.create(
user=self.gamer, slug="stargazer", earned_at=timezone.now(),
)
self.create_pre_authenticated_session("stargazer@test.io")
def _open_modal_and_click_bardo(self):
"""Helper: navigate to /billboard/my-notes/, open modal, click bardo swatch body.
Returns (modal, confirm_menu) after the confirm bar is visible."""
self.browser.get(self.live_server_url + "/billboard/my-notes/")
image_box = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".note-item__image-box")
)
# Clicking ? opens palette modal
image_box.click()
modal = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".note-palette-modal")
)
modal.find_element(By.CSS_SELECTOR, ".palette-bardo")
modal.find_element(By.CSS_SELECTOR, ".palette-sheol")
# Clicking a swatch body previews the palette and dismisses the modal
bardo_body = modal.find_element(By.CSS_SELECTOR, ".palette-bardo .note-swatch-body")
self.browser.execute_script(
"arguments[0].dispatchEvent(new MouseEvent('click', {bubbles: true}))",
bardo_body,
)
self.wait_for(lambda: self.assertFalse(
self.browser.find_elements(By.CSS_SELECTOR, ".note-palette-modal")
))
# Clicking elsewhere ends preview and restores the modal
self.browser.find_element(By.TAG_NAME, "body").click()
modal = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".note-palette-modal")
)
# Clicking OK on the swatch raises a confirmation submenu
ok_btn = modal.find_element(By.CSS_SELECTOR, ".palette-bardo .btn.btn-confirm")
ok_btn.click()
confirm_menu = self.wait_for(
lambda: self.browser.find_element(By.CSS_SELECTOR, ".note-palette-confirm")
)
return modal, confirm_menu
# Confirming sets palette, closes modal, replaces image-box with palette swatch
confirm_menu.find_element(By.CSS_SELECTOR, ".btn.btn-confirm").click()
def test_note_page_swatch_previews_palette_sitewide_and_ok_persists(self):
"""Clicking a swatch previews that palette on the whole body.
OK commits it — Note.palette and user.palette both saved — so it
survives navigation to a new page."""
Note.objects.create(
user=self.gamer, slug="stargazer", earned_at=timezone.now(),
)
self.create_pre_authenticated_session("stargazer@test.io")
modal, confirm = self._open_modal_and_click_bardo()
# Swatch click previews bardo on the whole body
self.wait_for(
lambda: self.assertIn(
"palette-bardo",
self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
)
)
# Modal still open
self.assertTrue(self.browser.find_elements(By.CSS_SELECTOR, ".note-palette-modal"))
# OK → modal closes, ? box replaced by bardo swatch
confirm.find_element(By.CSS_SELECTOR, ".btn.btn-confirm").click()
self.wait_for(lambda: self.assertFalse(
self.browser.find_elements(By.CSS_SELECTOR, ".note-palette-modal")
))
item = self.browser.find_element(By.CSS_SELECTOR, ".note-item")
self.assertTrue(
item.find_elements(By.CSS_SELECTOR, ".note-item__palette.palette-bardo")
self.assertTrue(item.find_elements(By.CSS_SELECTOR, ".note-item__palette.palette-bardo"))
self.assertFalse(item.find_elements(By.CSS_SELECTOR, ".note-item__image-box"))
# Navigate away — palette persists (user.palette was saved)
self.browser.get(self.live_server_url)
self.wait_for(
lambda: self.assertIn(
"palette-bardo",
self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
)
)
self.assertFalse(
item.find_elements(By.CSS_SELECTOR, ".note-item__image-box")
def test_note_swatch_nvm_reverts_body_palette(self):
"""NVM in the confirm bar reverts the sitewide body palette back to
what it was before the swatch was clicked."""
Note.objects.create(
user=self.gamer, slug="stargazer", earned_at=timezone.now(),
)
self.create_pre_authenticated_session("stargazer@test.io")
# Record the original palette before opening the modal
self.browser.get(self.live_server_url + "/billboard/my-notes/")
original_classes = self.browser.find_element(
By.TAG_NAME, "body"
).get_attribute("class")
original_palette = next(
(c for c in original_classes.split() if c.startswith("palette-")), None
)
modal, confirm = self._open_modal_and_click_bardo()
# Bardo is previewed
self.wait_for(
lambda: self.assertIn(
"palette-bardo",
self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
)
)
# NVM reverts
confirm.find_element(By.CSS_SELECTOR, ".btn.btn-cancel").click()
self.wait_for(
lambda: self.assertNotIn(
"palette-bardo",
self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
)
)
if original_palette:
self.assertIn(
original_palette,
self.browser.find_element(By.TAG_NAME, "body").get_attribute("class"),
)
# ── T2c ──────────────────────────────────────────────────────────────────