Fix v3 Songs List View favorite heart staying dim until re-search (#654)

Favoriting from the tree / "List View" flipped the glyph ♡→♥ but the
heart stayed grey until a re-search — reported macOS+Windows, open since
0.3.0 / 2026-06-25.

One shared wireCards() [data-fav] handler serves both the grid card and
the List-View row, but they render with different idle colours (grid
text-white, List View text-fb-textDim) and the handler only ever removed
the grid's text-white. So in List View text-fb-textDim lingered next to
the freshly-added text-fb-accent and won by CSS source order — the glyph
changed but the colour didn't, until a re-search re-rendered the row.

Each heart now declares its idle colour via a data-fav-idle attribute;
the handler swaps exactly that class (so only one colour class is ever
present) and writes the new state back onto the in-memory song model so a
re-render / virtualized-grid recycle agrees instead of reverting.

Tests: tests/js/v3_favorites_toggle.test.js. Full JS suite 810 tests; the
13 pre-existing unrelated failures are unchanged.


Claude-Session: https://claude.ai/code/session_01QbexxfTt8q2tAn436MqGWF

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
ChrisBeWithYou
2026-07-02 13:58:21 +02:00
committed by GitHub
co-authored by Claude Opus 4.8
parent fee85a14e7
commit 0d28886d46
3 changed files with 59 additions and 3 deletions
+48
View File
@@ -0,0 +1,48 @@
// Pins the v3 Songs favorite-toggle colour swap in static/v3/songs.js.
//
// One shared wireCards() [data-fav] handler serves BOTH the grid card and the
// tree / "List View" row, but the two render sites use different idle colours
// (grid = text-white, tree = text-fb-textDim). The handler used to toggle a
// hardcoded text-white, so in List View it never removed text-fb-textDim — the
// heart changed glyph (♡→♥) but stayed dim and only turned red after a re-search
// re-rendered the row (reported macOS+Windows, 0.3.0, open since 06-25). Each
// button now declares its idle colour via data-fav-idle and the handler swaps
// exactly that class, so only one colour class is ever present.
//
// Source-level only — same strategy as tests/js/v3_az_rail.test.js.
const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const SONGS_JS = path.join(__dirname, '..', '..', 'static', 'v3', 'songs.js');
const src = fs.readFileSync(SONGS_JS, 'utf8');
test('both fav render sites declare their idle colour via data-fav-idle', () => {
// Grid card heart idles white; the tree / List View heart idles dim.
assert.match(src, /data-fav data-fav-idle="text-white"/,
'the grid fav button must declare data-fav-idle="text-white"');
assert.match(src, /data-fav data-fav-idle="text-fb-textDim"/,
'the tree/List-View fav button must declare data-fav-idle="text-fb-textDim"');
});
test('the shared fav handler swaps the declared idle colour, not a hardcoded one', () => {
// Reads the idle colour off the clicked button …
assert.match(src, /getAttribute\('data-fav-idle'\)/,
'the fav handler must read the idle colour from the button');
// … toggles fb-accent (red) on favorite and restores the context idle colour off it.
assert.match(src, /classList\.toggle\('text-fb-accent',\s*d\.favorite\)/);
assert.match(src, /classList\.toggle\(\s*idle\s*,\s*!d\.favorite\s*\)/,
'the fav handler must restore the per-context idle colour (idle), not text-white');
// The grid-only hardcoded idle toggle that stranded text-fb-textDim is gone.
assert.doesNotMatch(src, /classList\.toggle\('text-white',\s*!d\.favorite\)/,
'the hardcoded text-white idle toggle must be removed');
});
test('the fav toggle keeps the in-memory song model in sync', () => {
// So a virtualized grid recycle / tree re-render renders the new state,
// not a stale favorite=false read from state.songsById.
assert.match(src, /song\.favorite\s*=\s*d\.favorite/,
'the fav handler must write the new favorite state back onto the song model');
});