From 74af41c8abec4b05d01bb54d077d1f37099f4f62 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 16 Jan 2026 12:46:25 +0100 Subject: [PATCH 1/7] feat: reorder sidebar --- lib/mv_web/components/layouts/sidebar.ex | 36 ++++++++++------------- priv/gettext/de/LC_MESSAGES/default.po | 32 +++++++++++++++----- priv/gettext/default.pot | 27 ++++++++++++----- priv/gettext/en/LC_MESSAGES/default.po | 37 +++++++++++++++++++----- 4 files changed, 90 insertions(+), 42 deletions(-) diff --git a/lib/mv_web/components/layouts/sidebar.ex b/lib/mv_web/components/layouts/sidebar.ex index 6f7e684..6e50b3c 100644 --- a/lib/mv_web/components/layouts/sidebar.ex +++ b/lib/mv_web/components/layouts/sidebar.ex @@ -75,30 +75,24 @@ defmodule MvWeb.Layouts.Sidebar do icon="hero-users" label={gettext("Members")} /> - <.menu_item - href={~p"/users"} - icon="hero-user-circle" - label={gettext("Users")} - /> - <.menu_item - href={~p"/custom_field_values"} - icon="hero-rectangle-group" - label={gettext("Custom Fields")} - /> - - <.menu_group - icon="hero-currency-dollar" - label={gettext("Contributions")} - > - <.menu_subitem href="/contribution_types" label={gettext("Contribution Types")} /> - <.menu_subitem href="/membership_fee_settings" label={gettext("Settings")} /> - <.menu_item - href={~p"/settings"} - icon="hero-cog-6-tooth" - label={gettext("Settings")} + href={~p"/membership_fee_types"} + icon="hero-currency-euro" + label={gettext("Fee Types")} /> + + + <.menu_group icon="hero-cog-6-tooth" label={gettext("Administration")}> + <.menu_subitem href={~p"/users"} icon="hero-user-circle" label={gettext("Users")} /> + <.menu_subitem href={~p"/admin/roles"} icon="hero-shield-check" label={gettext("Roles")} /> + <.menu_subitem + href={~p"/membership_fee_settings"} + icon="hero-currency-euro" + label={gettext("Fee Settings")} + /> + <.menu_subitem href={~p"/settings"} icon="hero-cog-6-tooth" label={gettext("Settings")} /> + """ end diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po index 33dff0f..6264697 100644 --- a/priv/gettext/de/LC_MESSAGES/default.po +++ b/priv/gettext/de/LC_MESSAGES/default.po @@ -631,7 +631,6 @@ msgstr "Benutzerdefinierter Feldwert erfolgreich %{action}" msgid "Please select a custom field first" msgstr "Bitte wähle zuerst ein Benutzerdefiniertes Feld" -#: lib/mv_web/components/layouts/sidebar.ex #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format @@ -915,7 +914,6 @@ msgstr "Beitragsart ändern" msgid "Contribution Start" msgstr "Beitragsbeginn" -#: lib/mv_web/components/layouts/sidebar.ex #: lib/mv_web/live/contribution_type_live/index.ex #, elixir-autogen, elixir-format, fuzzy msgid "Contribution Types" @@ -926,11 +924,6 @@ msgstr "Beitragsarten" msgid "Contribution type" msgstr "Beitragsart" -#: lib/mv_web/components/layouts/sidebar.ex -#, elixir-autogen, elixir-format, fuzzy -msgid "Contributions" -msgstr "Beiträge" - #: lib/mv_web/live/contribution_period_live/show.ex #, elixir-autogen, elixir-format, fuzzy msgid "Contributions for %{name}" @@ -2188,3 +2181,28 @@ msgstr "Mitglied wurde erfolgreich erstellt" #, elixir-autogen, elixir-format msgid "Member updated successfully" msgstr "Mitglied wurde erfolgreich aktualisiert" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Roles" +msgstr "Rollen" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Fee Settings" +msgstr "Beitragseinstellungen" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format +msgid "Fee Types" +msgstr "Beitragstypen" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format +msgid "Administration" +msgstr "Administration" + +#~ #: lib/mv_web/components/layouts/sidebar.ex +#~ #, elixir-autogen, elixir-format, fuzzy +#~ msgid "Contributions" +#~ msgstr "Beiträge" diff --git a/priv/gettext/default.pot b/priv/gettext/default.pot index 4124d86..b7e0ceb 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -632,7 +632,6 @@ msgstr "" msgid "Please select a custom field first" msgstr "" -#: lib/mv_web/components/layouts/sidebar.ex #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format @@ -916,7 +915,6 @@ msgstr "" msgid "Contribution Start" msgstr "" -#: lib/mv_web/components/layouts/sidebar.ex #: lib/mv_web/live/contribution_type_live/index.ex #, elixir-autogen, elixir-format msgid "Contribution Types" @@ -927,11 +925,6 @@ msgstr "" msgid "Contribution type" msgstr "" -#: lib/mv_web/components/layouts/sidebar.ex -#, elixir-autogen, elixir-format -msgid "Contributions" -msgstr "" - #: lib/mv_web/live/contribution_period_live/show.ex #, elixir-autogen, elixir-format msgid "Contributions for %{name}" @@ -2189,3 +2182,23 @@ msgstr "" #, elixir-autogen, elixir-format msgid "Member updated successfully" msgstr "" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format +msgid "Roles" +msgstr "" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format +msgid "Fee Settings" +msgstr "" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format +msgid "Fee Types" +msgstr "" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format +msgid "Administration" +msgstr "" diff --git a/priv/gettext/en/LC_MESSAGES/default.po b/priv/gettext/en/LC_MESSAGES/default.po index 50c5440..6001b60 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -632,7 +632,6 @@ msgstr "" msgid "Please select a custom field first" msgstr "" -#: lib/mv_web/components/layouts/sidebar.ex #: lib/mv_web/live/member_live/form.ex #: lib/mv_web/live/member_live/show.ex #, elixir-autogen, elixir-format, fuzzy @@ -916,7 +915,6 @@ msgstr "" msgid "Contribution Start" msgstr "" -#: lib/mv_web/components/layouts/sidebar.ex #: lib/mv_web/live/contribution_type_live/index.ex #, elixir-autogen, elixir-format msgid "Contribution Types" @@ -927,11 +925,6 @@ msgstr "" msgid "Contribution type" msgstr "" -#: lib/mv_web/components/layouts/sidebar.ex -#, elixir-autogen, elixir-format -msgid "Contributions" -msgstr "" - #: lib/mv_web/live/contribution_period_live/show.ex #, elixir-autogen, elixir-format msgid "Contributions for %{name}" @@ -2189,3 +2182,33 @@ msgstr "Member created successfully" #, elixir-autogen, elixir-format msgid "Member updated successfully" msgstr "Member updated successfully" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Roles" +msgstr "" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format, fuzzy +msgid "Fee Settings" +msgstr "" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format +msgid "Fee Types" +msgstr "" + +#: lib/mv_web/components/layouts/sidebar.ex +#, elixir-autogen, elixir-format +msgid "Administration" +msgstr "" + +#~ #: lib/mv_web/components/layouts/sidebar.ex +#~ #, elixir-autogen, elixir-format, fuzzy +#~ msgid "Admin" +#~ msgstr "" + +#~ #: lib/mv_web/components/layouts/sidebar.ex +#~ #, elixir-autogen, elixir-format +#~ msgid "Contributions" +#~ msgstr "" From 3381fd88dbf2f7bfccc8e1513f591a191c887be6 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 16 Jan 2026 12:47:23 +0100 Subject: [PATCH 2/7] test: increase test worker pool size --- config/test.exs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/test.exs b/config/test.exs index 326694e..45acaa4 100644 --- a/config/test.exs +++ b/config/test.exs @@ -12,7 +12,7 @@ config :mv, Mv.Repo, port: System.get_env("TEST_POSTGRES_PORT", "5000"), database: "mv_test#{System.get_env("MIX_TEST_PARTITION")}", pool: Ecto.Adapters.SQL.Sandbox, - pool_size: System.schedulers_online() * 2 + pool_size: System.schedulers_online() * 4 # We don't run a server during test. If one is required, # you can enable the server option below. From d6173571b5f0be4e42b718df6df45383af3a2069 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 16 Jan 2026 12:48:35 +0100 Subject: [PATCH 3/7] test: make tests more structural, less dependend on specific values --- .../components/layouts/sidebar_test.exs | 153 ++++++++++-------- 1 file changed, 84 insertions(+), 69 deletions(-) diff --git a/test/mv_web/components/layouts/sidebar_test.exs b/test/mv_web/components/layouts/sidebar_test.exs index 41bbbd7..460d85a 100644 --- a/test/mv_web/components/layouts/sidebar_test.exs +++ b/test/mv_web/components/layouts/sidebar_test.exs @@ -122,35 +122,33 @@ defmodule MvWeb.Layouts.SidebarTest do test "T2.2: does not render menu items when current_user is nil" do html = render_sidebar(guest_assigns()) - # Navigation links should not be rendered - refute html =~ ~s(href="/members") - refute html =~ ~s(href="/users") - refute html =~ ~s(href="/settings") - refute html =~ ~s(href="/contribution_types") + # Navigation menu should not be rendered + refute html =~ ~s(role="menubar") + refute html =~ ~s(role="menuitem") # Footer section should not be rendered - refute html =~ "locale-select" refute html =~ "theme-controller" + refute html =~ "locale-select" end test "T2.3: renders menu items when current_user is present" do html = render_sidebar(authenticated_assigns()) - # Check for Members link - assert html =~ ~s(href="/members") + # Check that menu structure exists + assert html =~ ~s(role="menubar") + assert html =~ ~s(role="menuitem") - # Check for Users link - assert html =~ ~s(href="/users") + # Check that top-level menu items exist (at least one) + # Count menu items with tooltips (top-level items have tooltips) + menu_item_count = html |> String.split("data-tip=") |> length() |> Kernel.-(1) + assert menu_item_count > 0, "Should have at least one top-level menu item" - # Check for Custom Fields link - assert html =~ ~s(href="/custom_field_values") + # Check that nested menu groups exist + assert html =~ " String.split(~s(role="menuitem")) |> length() |> Kernel.-(1) + data_tip_count = html |> String.split("data-tip=") |> length() |> Kernel.-(1) + + # There should be more menuitems than data-tips (nested items don't have data-tip) + assert menuitem_count > data_tip_count, + "Should have nested menu items (menuitems without data-tip)" end test "T3.3: renders nested menu with dropdown for collapsed state" do html = render_sidebar(authenticated_assigns()) - # Check for collapsed dropdown container + # Check for collapsed dropdown structure assert has_class?(html, "collapsed-menu-group") assert has_class?(html, "dropdown") assert has_class?(html, "dropdown-right") - - # Check for dropdown-content assert has_class?(html, "dropdown-content") - # Check for icon button - assert html =~ "hero-currency-dollar" + # Check that dropdown button has icon (any hero icon) + assert html =~ ~r/hero-\w+/ + + # Check ARIA attributes assert html =~ ~s(aria-haspopup="menu") end end @@ -414,17 +419,17 @@ defmodule MvWeb.Layouts.SidebarTest do test "T7.1: renders hero icons for menu items" do html = render_sidebar(authenticated_assigns()) - # Check for hero icons - assert html =~ "hero-users" - assert html =~ "hero-user-circle" - assert html =~ "hero-rectangle-group" - assert html =~ "hero-currency-dollar" - assert html =~ "hero-cog-6-tooth" + # Check that hero icons are present (pattern matching) + assert html =~ ~r/hero-\w+/ + + # Check that icons have aria-hidden + assert html =~ ~s(aria-hidden="true") + + # Check for specific structural icons (toggle, theme) that should always exist assert html =~ "hero-chevron-left" assert html =~ "hero-chevron-right" - - # Icons should have aria-hidden - assert html =~ ~s(aria-hidden="true") + assert html =~ "hero-sun" + assert html =~ "hero-moon" end test "T7.2: renders icons for theme toggle" do @@ -503,26 +508,24 @@ defmodule MvWeb.Layouts.SidebarTest do # Header section assert html =~ "Mila Logo" + assert html =~ ~s(src="/images/mila.svg") # Navigation section assert html =~ ~s(role="menubar") + assert html =~ ~s(id="main-sidebar") + + # Check that menu has items (at least one top-level item) + assert html =~ ~s(role="menuitem") + + # Check that nested menus exist + assert html =~ " String.split(~s(role="menuitem")) |> length() |> Kernel.-(1) + data_tip_count = html |> String.split("data-tip=") |> length() |> Kernel.-(1) + + # There should be more menuitems than data-tips (nested items don't have data-tip) + assert menuitem_count > data_tip_count, + "Should have nested menu items (menuitems without data-tip)" + + # Verify nested menu structure exists assert html =~ ~s(role="menu") end end From c3515b4105d61b1dc19a1898dbeb286271039498 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 16 Jan 2026 13:53:31 +0100 Subject: [PATCH 4/7] feat: adjust display of submenu --- assets/css/app.css | 25 ++++++++- lib/mv_web/components/layouts/sidebar.ex | 67 +++++++++++------------- 2 files changed, 54 insertions(+), 38 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index 97961ab..f709b98 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -181,6 +181,25 @@ padding-left: 14px; } +/* ============================================ + Menu Groups - Disable hover and active on expanded-menu-group header + ============================================ */ + +/* Disable all interactive effects on expanded-menu-group header (no href, not clickable) */ +.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a) { + pointer-events: none; + cursor: default; +} + +.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a):hover, +.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a):active, +.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a):focus { + background-color: transparent !important; + box-shadow: none !important; + cursor: default !important; + color: inherit; +} + /* ============================================ Elements Only Visible in Expanded State ============================================ */ @@ -217,7 +236,8 @@ - Menu has p-2 (8px), so links need 14px additional padding-left */ .sidebar .menu > li > a, -.sidebar .menu > li > button { +.sidebar .menu > li > button, +.sidebar .menu > div.collapsed-menu-group > a { @apply transition-all duration-300; padding-left: 14px; } @@ -226,7 +246,8 @@ - Remove gap so label (which is opacity-0 w-0) doesn't create space - Keep padding-left at 14px so icons stay centered under logo */ [data-sidebar-expanded="false"] .sidebar .menu > li > a, -[data-sidebar-expanded="false"] .sidebar .menu > li > button { +[data-sidebar-expanded="false"] .sidebar .menu > li > button, +[data-sidebar-expanded="false"] .sidebar .menu > div.collapsed-menu-group > a { @apply gap-0; padding-left: 14px; padding-right: 14px; /* Center icon horizontally in 64px sidebar */ diff --git a/lib/mv_web/components/layouts/sidebar.ex b/lib/mv_web/components/layouts/sidebar.ex index 6e50b3c..40c8bb6 100644 --- a/lib/mv_web/components/layouts/sidebar.ex +++ b/lib/mv_web/components/layouts/sidebar.ex @@ -81,7 +81,7 @@ defmodule MvWeb.Layouts.Sidebar do icon="hero-currency-euro" label={gettext("Fee Types")} /> - + <.menu_group icon="hero-cog-6-tooth" label={gettext("Administration")}> <.menu_subitem href={~p"/users"} icon="hero-user-circle" label={gettext("Users")} /> @@ -123,43 +123,38 @@ defmodule MvWeb.Layouts.Sidebar do defp menu_group(assigns) do ~H""" -
  • +
    + <.icon name={@icon} class="size-5 shrink-0" aria-hidden="true" /> + {@label}
    +
  • + + """ end From c86ae6aa9db8319e62676f340beb9f5cae4a83d0 Mon Sep 17 00:00:00 2001 From: Simon Date: Fri, 16 Jan 2026 14:17:15 +0100 Subject: [PATCH 5/7] fix: sidebar accessibility --- assets/css/app.css | 8 ++--- lib/mv_web/components/layouts/sidebar.ex | 16 +++++---- .../components/layouts/sidebar_test.exs | 34 +++++++++++-------- 3 files changed, 33 insertions(+), 25 deletions(-) diff --git a/assets/css/app.css b/assets/css/app.css index f709b98..7e6699d 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -186,14 +186,14 @@ ============================================ */ /* Disable all interactive effects on expanded-menu-group header (no href, not clickable) */ -.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a) { +.sidebar .menu > li.expanded-menu-group > div:not(a) { pointer-events: none; cursor: default; } -.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a):hover, -.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a):active, -.sidebar .menu > li.menu-group > .expanded-menu-group > div:not(a):focus { +.sidebar .menu > li.expanded-menu-group > div:not(a):hover, +.sidebar .menu > li.expanded-menu-group > div:not(a):active, +.sidebar .menu > li.expanded-menu-group > div:not(a):focus { background-color: transparent !important; box-shadow: none !important; cursor: default !important; diff --git a/lib/mv_web/components/layouts/sidebar.ex b/lib/mv_web/components/layouts/sidebar.ex index 40c8bb6..962e720 100644 --- a/lib/mv_web/components/layouts/sidebar.ex +++ b/lib/mv_web/components/layouts/sidebar.ex @@ -84,14 +84,13 @@ defmodule MvWeb.Layouts.Sidebar do <.menu_group icon="hero-cog-6-tooth" label={gettext("Administration")}> - <.menu_subitem href={~p"/users"} icon="hero-user-circle" label={gettext("Users")} /> - <.menu_subitem href={~p"/admin/roles"} icon="hero-shield-check" label={gettext("Roles")} /> + <.menu_subitem href={~p"/users"} label={gettext("Users")} /> + <.menu_subitem href={~p"/admin/roles"} label={gettext("Roles")} /> <.menu_subitem href={~p"/membership_fee_settings"} - icon="hero-currency-euro" label={gettext("Fee Settings")} /> - <.menu_subitem href={~p"/settings"} icon="hero-cog-6-tooth" label={gettext("Settings")} /> + <.menu_subitem href={~p"/settings"} label={gettext("Settings")} /> """ @@ -124,8 +123,12 @@ defmodule MvWeb.Layouts.Sidebar do defp menu_group(assigns) do ~H""" -
  • -
    +
  • +
    <.icon name={@icon} class="size-5 shrink-0" aria-hidden="true" /> {@label}
    @@ -147,7 +150,6 @@ defmodule MvWeb.Layouts.Sidebar do <.icon name={@icon} class="size-5 shrink-0" aria-hidden="true" />