Migrate filter bars to FilterSelect

Replace the bespoke SelectableFilter in all three bars with FilterSelect: enum
fields (status, type, ownership) pre-render their fixed options; model-backed
fields (game(s), platform, device) use the search endpoints with prefetch and
resolve only the selected ids to pill labels — dropping the per-page queries that
fetched every game/platform/device. filter_bar.js now reads filter-mode
SearchSelect widgets via readSearchSelect (data-included/excluded/modifier),
preserving the {value, excludes, modifier} JSON and id Number() coercion; the
redundant session game/device blocks are gone. Drop FilterBar's now-unused
platform_options param. Rebuild base.css for the inline filter-pill utilities and
update the bar tests to the new markup.

https://claude.ai/code/session_01XzhXvMvw42CQGc9kmin3GS
This commit is contained in:
Claude
2026-06-07 22:20:44 +00:00
committed by Lukáš Kucharczyk
parent a6532807cb
commit 1a206d719b
5 changed files with 198 additions and 139 deletions
+16 -17
View File
@@ -235,20 +235,20 @@ class TestGameFilterToQ:
class TestFilterBarRendering:
"""Tests for FilterBar with SelectableFilter widgets."""
"""Tests for FilterBar with FilterSelect widgets."""
def test_status_uses_selectable_filter(self):
html = str(FilterBar(platform_options=[]))
assert "data-selectable-filter" in html
def test_status_uses_filter_select(self):
html = str(FilterBar())
assert 'data-ss-mode="filter"' in html
assert 'data-name="status"' in html
def test_mastered_not_checked_by_default(self):
html = str(FilterBar(filter_json="", platform_options=[]))
html = str(FilterBar(filter_json=""))
assert 'checked="true"' not in html
def test_mastered_checked_when_filtered(self):
html = str(
FilterBar(
platform_options=[],
filter_json=json.dumps(
{"mastered": {"value": True, "modifier": "EQUALS"}}
),
@@ -259,7 +259,6 @@ class TestFilterBarRendering:
def test_status_prefilled(self):
html = str(
FilterBar(
platform_options=[],
filter_json=json.dumps(
{"status": {"value": ["f"], "modifier": "INCLUDES"}}
),
@@ -269,19 +268,19 @@ class TestFilterBarRendering:
assert "Finished" in html
def test_no_hx_get(self):
html = str(FilterBar(platform_options=[]))
html = str(FilterBar())
assert "hx-get" not in html
def test_platform_options_rendered(self):
html = str(FilterBar(platform_options=[(1, "Steam"), (2, "Switch")]))
assert "Steam" in html
assert "Switch" in html
def test_platform_uses_search_url(self):
"""Platform is model-backed: rows are fetched, not pre-rendered."""
html = str(FilterBar())
assert 'data-search-url="/api/platforms/search"' in html
def test_status_has_no_modifiers(self):
"""Non-nullable fields should not show (None) but MUST show (Any)."""
html = str(FilterBar(platform_options=[]))
status_start = html.find('data-selectable-filter="status"')
platform_start = html.find('data-selectable-filter="platform"')
html = str(FilterBar())
status_start = html.find('data-name="status"')
platform_start = html.find('data-name="platform"')
status_section = html[status_start:platform_start]
# Must have (Any) — always available
assert "(Any)" in status_section
@@ -290,8 +289,8 @@ class TestFilterBarRendering:
def test_platform_has_modifiers(self):
"""Nullable ForeignKey fields should show (Any)/(None)."""
html = str(FilterBar(platform_options=[(1, "Steam")]))
platform_start = html.find('data-selectable-filter="platform"')
html = str(FilterBar())
platform_start = html.find('data-name="platform"')
platform_section = html[platform_start:]
# Should have at least one modifier option
assert "(Any)" in platform_section or "(None)" in platform_section