diff --git a/src/apps/dashboard/migrations/0003_seed_applets.py b/src/apps/dashboard/migrations/0003_seed_applets.py index 78c4666..ead2bcc 100644 --- a/src/apps/dashboard/migrations/0003_seed_applets.py +++ b/src/apps/dashboard/migrations/0003_seed_applets.py @@ -4,7 +4,7 @@ from django.db import migrations def seed_applets(apps, schema_editor): Applet = apps.get_model("dashboard", "Applet") Applet.objects.get_or_create(slug="username", defaults={"name": "Username"}) - Applet.objects.get_or_create(slug="theme-switcher", defaults={"name": "Theme Switcher"}) + Applet.objects.get_or_create(slug="palette", defaults={"name": "Palette"}) class Migration(migrations.Migration): diff --git a/src/apps/dashboard/tests/integrated/test_views.py b/src/apps/dashboard/tests/integrated/test_views.py index b24c357..3a06d7e 100644 --- a/src/apps/dashboard/tests/integrated/test_views.py +++ b/src/apps/dashboard/tests/integrated/test_views.py @@ -210,7 +210,11 @@ class ShareListTest(TestCase): f"/dashboard/list/{our_list.id}/share_list", data={"recipient": "nobody@example.com"}, ) - self.assertRedirects(response, f"/dashboard/list/{our_list.id}/") + self.assertRedirects( + response, + f"/dashboard/list/{our_list.id}/", + fetch_redirect_response=False, + ) def test_share_list_does_not_add_owner_as_recipient(self): owner = User.objects.create(email="owner@example.com") @@ -374,3 +378,27 @@ class ToggleAppletsViewTest(TestCase): HTTP_HX_REQUEST="true", ) self.assertEqual(response.status_code, 200) + + def test_htmx_post_renders_visible_applets_only(self): + response = self.client.post( + self.url, + {"applets": ["username"]}, + HTTP_HX_REQUEST="true", + ) + parsed = lxml.html.fromstring(response.content) + self.assertEqual(len(parsed.cssselect("#id_applet_username")), 1) + self.assertEqual(len(parsed.cssselect("#id_applet_palette")), 0) + +class AppletVisibilityContextTest(TestCase): + def setUp(self): + self.user = User.objects.create(email="disco@test.io") + self.client.force_login(self.user) + self.username_applet, _ = Applet.objects.get_or_create(slug="username", defaults={"name": "Username"}) + self.palette_applet, _ = Applet.objects.get_or_create(slug="palette", defaults={"name": "Palette"}) + UserApplet.objects.create(user=self.user, applet=self.palette_applet, visible=False) + + def test_dash_reflects_user_applet_visibility(self): + response = self.client.get("/") + applet_map = {entry["applet"].slug: entry["visible"] for entry in response.context["applets"]} + self.assertFalse(applet_map["palette"]) + self.assertTrue(applet_map["username"]) diff --git a/src/apps/dashboard/views.py b/src/apps/dashboard/views.py index 355684a..0090668 100644 --- a/src/apps/dashboard/views.py +++ b/src/apps/dashboard/views.py @@ -16,12 +16,18 @@ PALETTES = [ ] +def _applet_context(user): + ua_map = {ua.applet_id: ua.visible for ua in user.user_applets.all()} + return [ + {"applet": applet, "visible": ua_map.get(applet.pk, applet.default_visible)} + for applet in Applet.objects.all() + ] + def home_page(request): - return render( - request, "apps/dashboard/home.html", { - "form": ItemForm(), - "palettes": PALETTES, - }) + context = {"form": ItemForm(), "palettes": PALETTES} + if request.user.is_authenticated: + context["applets"] = _applet_context(request.user) + return render(request, "apps/dashboard/home.html", context) def new_list(request): form = ItemForm(data=request.POST) @@ -100,5 +106,8 @@ def toggle_applets(request): defaults={"visible": applet.slug in checked}, ) if request.headers.get("HX-Request"): - return HttpResponse("") + return render(request, "apps/dashboard/_partials/_applets.html", { + "applets": _applet_context(request.user), + "palettes": PALETTES, + }) return redirect("home") diff --git a/src/functional_tests/test_dashboard.py b/src/functional_tests/test_dashboard.py index 220e0e1..5698dac 100644 --- a/src/functional_tests/test_dashboard.py +++ b/src/functional_tests/test_dashboard.py @@ -3,9 +3,15 @@ from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from .base import FunctionalTest +from apps.dashboard.models import Applet class DashboardMaintenanceTest(FunctionalTest): + def setUp(self): + super().setUp() + Applet.objects.get_or_create(slug="username", defaults={"name": "Username"}) + Applet.objects.get_or_create(slug="palette", defaults={"name": "Palette"}) + def test_user_without_username_can_claim_unclaimed_username(self): # 1. Create a pre-authenticated session for discoman@example.com self.create_pre_authenticated_session("discoman@example.com") @@ -51,18 +57,20 @@ class DashboardMaintenanceTest(FunctionalTest): dash_gear.click() # 4. A menu appears; wait_for el w. id="id_applet_menu" self.wait_for( - lambda: self.browser.find_element(By.ID, "id_applet_menu") + lambda: self.assertTrue( + self.browser.find_element(By.ID, "id_applet_menu").is_displayed() + ) ) # 5. Find two checkboxes in menu, name="username" & name="palette"; assert both .is_selected() menu = self.browser.find_element(By.ID, "id_applet_menu") - username_cb = menu.find_element(By.NAME, "username") - palette_cb = menu.find_element(By.NAME, "palette") + username_cb = menu.find_element(By.CSS_SELECTOR, '[name="applets"][value="username"]') + palette_cb = menu.find_element(By.CSS_SELECTOR, '[name="applets"][value="palette"]') self.assertTrue(username_cb.is_selected()) self.assertTrue(palette_cb.is_selected()) # 6. Click palette box to uncheck it palette_cb.click() self.assertFalse(palette_cb.is_selected()) - self.browser.execute_script("window.__no_reload_marker = True") + self.browser.execute_script("window.__no_reload_marker = true") # 7. Submit the menu form via [type="submit"] btn inside menu menu.find_element(By.CSS_SELECTOR, '[type="submit"]').click() self.wait_for( @@ -82,6 +90,13 @@ class DashboardMaintenanceTest(FunctionalTest): self.browser.find_element(By.ID, "id_applet_username") # 10. Click gear again, find menu, find palette checkbox; assert now NOT selected dash_gear.click() + self.wait_for( + lambda: self.assertTrue( + self.browser.find_element(By.ID, "id_applet_menu").is_displayed() + ) + ) + menu = self.browser.find_element(By.ID, "id_applet_menu") + palette_cb = menu.find_element(By.CSS_SELECTOR, '[name="applets"][value="palette"]') self.assertFalse(palette_cb.is_selected()) # 11. Click it to re-check box; submit palette_cb.click() diff --git a/src/templates/apps/dashboard/_partials/_applets.html b/src/templates/apps/dashboard/_partials/_applets.html new file mode 100644 index 0000000..e455662 --- /dev/null +++ b/src/templates/apps/dashboard/_partials/_applets.html @@ -0,0 +1,61 @@ +{% load lyric_extras %} +
+ + + {% for entry in applets %} + {% if entry.visible %} + {% if entry.applet.slug == "username" %} +
+

{{ user|display_name }}

+
+
+ {% csrf_token %} + +
+
+
+ {% elif entry.applet.slug == "palette" %} +
+ {% for palette in palettes %} +
+
+ {% if not palette.locked %} +
+ {% csrf_token %} + +
+ {% else %} + × + {% endif %} +
+ {% endfor %} +
+ {% endif %} + {% endif %} + {% endfor %} +
\ No newline at end of file diff --git a/src/templates/apps/dashboard/home.html b/src/templates/apps/dashboard/home.html index de24dbd..173783e 100644 --- a/src/templates/apps/dashboard/home.html +++ b/src/templates/apps/dashboard/home.html @@ -15,29 +15,12 @@ {% block content %} {% if user.is_authenticated %} -
- {% for palette in palettes %} -
-
- {% if not palette.locked %} -
- {% csrf_token %} - -
- {% else %} - × - {% endif %} -
- {% endfor %} -
-
-

{{ user|display_name }}

-
-
- {% csrf_token %} - -
-
-
+ + {% include "apps/dashboard/_partials/_applets.html" %} {% endif %} {% endblock content %}