diff --git a/assets/css/app.css b/assets/css/app.css index b754a08..97961ab 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -181,29 +181,6 @@ 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) - Using [role="group"] to increase specificity and avoid !important */ -.sidebar .menu > li.expanded-menu-group > div[role="group"]:not(a) { - pointer-events: none; - cursor: default; -} - -/* Higher specificity selector to override DaisyUI menu hover styles - DaisyUI uses :where() which has 0 specificity, but the compiled CSS might have higher specificity - Using [role="group"] attribute selector increases specificity without !important */ -.sidebar .menu > li.expanded-menu-group > div[role="group"]:not(a):hover, -.sidebar .menu > li.expanded-menu-group > div[role="group"]:not(a):active, -.sidebar .menu > li.expanded-menu-group > div[role="group"]:not(a):focus { - background-color: transparent; - box-shadow: none; - cursor: default; - color: inherit; -} - /* ============================================ Elements Only Visible in Expanded State ============================================ */ @@ -240,9 +217,7 @@ - Menu has p-2 (8px), so links need 14px additional padding-left */ .sidebar .menu > li > a, -.sidebar .menu > li > button, -.sidebar .menu > li.expanded-menu-group > div, -.sidebar .menu > div.collapsed-menu-group > button { +.sidebar .menu > li > button { @apply transition-all duration-300; padding-left: 14px; } @@ -251,17 +226,12 @@ - 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.expanded-menu-group > div, -[data-sidebar-expanded="false"] .sidebar .menu > div.collapsed-menu-group > button { +[data-sidebar-expanded="false"] .sidebar .menu > li > button { @apply gap-0; padding-left: 14px; padding-right: 14px; /* Center icon horizontally in 64px sidebar */ } - - - /* ============================================ Footer Button Alignment - Left Aligned in Collapsed State ============================================ */ diff --git a/config/test.exs b/config/test.exs index 45acaa4..326694e 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() * 4 + pool_size: System.schedulers_online() * 2 # We don't run a server during test. If one is required, # you can enable the server option below. diff --git a/lib/mv_web/components/layouts/sidebar.ex b/lib/mv_web/components/layouts/sidebar.ex index 33319d4..6f7e684 100644 --- a/lib/mv_web/components/layouts/sidebar.ex +++ b/lib/mv_web/components/layouts/sidebar.ex @@ -75,23 +75,30 @@ 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"/membership_fee_types"} - icon="hero-currency-euro" - label={gettext("Fee Types")} + href={~p"/settings"} + icon="hero-cog-6-tooth" + label={gettext("Settings")} /> - - - <.menu_group icon="hero-cog-6-tooth" label={gettext("Administration")}> - <.menu_subitem href={~p"/users"} label={gettext("Users")} /> - <.menu_subitem href={~p"/admin/roles"} label={gettext("Roles")} /> - <.menu_subitem - href={~p"/membership_fee_settings"} - label={gettext("Fee Settings")} - /> - <.menu_subitem href={~p"/settings"} label={gettext("Settings")} /> - """ end @@ -122,41 +129,43 @@ defmodule MvWeb.Layouts.Sidebar do defp menu_group(assigns) do ~H""" - -
  • -
    - <.icon name={@icon} class="size-5 shrink-0" aria-hidden="true" /> - {@label} +
  • - - """ end diff --git a/priv/gettext/de/LC_MESSAGES/default.po b/priv/gettext/de/LC_MESSAGES/default.po index 6264697..33dff0f 100644 --- a/priv/gettext/de/LC_MESSAGES/default.po +++ b/priv/gettext/de/LC_MESSAGES/default.po @@ -631,6 +631,7 @@ 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 @@ -914,6 +915,7 @@ 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" @@ -924,6 +926,11 @@ 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}" @@ -2181,28 +2188,3 @@ 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 b7e0ceb..4124d86 100644 --- a/priv/gettext/default.pot +++ b/priv/gettext/default.pot @@ -632,6 +632,7 @@ 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 @@ -915,6 +916,7 @@ 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" @@ -925,6 +927,11 @@ 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}" @@ -2182,23 +2189,3 @@ 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 6001b60..50c5440 100644 --- a/priv/gettext/en/LC_MESSAGES/default.po +++ b/priv/gettext/en/LC_MESSAGES/default.po @@ -632,6 +632,7 @@ 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 @@ -915,6 +916,7 @@ 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" @@ -925,6 +927,11 @@ 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}" @@ -2182,33 +2189,3 @@ 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 "" diff --git a/test/mv_web/components/layouts/sidebar_test.exs b/test/mv_web/components/layouts/sidebar_test.exs index 75727e3..41bbbd7 100644 --- a/test/mv_web/components/layouts/sidebar_test.exs +++ b/test/mv_web/components/layouts/sidebar_test.exs @@ -122,34 +122,35 @@ 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 menu should not be rendered - refute html =~ ~s(role="menubar") - refute html =~ ~s(role="menuitem") + # 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") # Footer section should not be rendered - refute html =~ "theme-controller" refute html =~ "locale-select" + refute html =~ "theme-controller" end test "T2.3: renders menu items when current_user is present" do html = render_sidebar(authenticated_assigns()) - # Check that menu structure exists - assert html =~ ~s(role="menubar") - assert html =~ ~s(role="menuitem") + # Check for Members link + assert html =~ ~s(href="/members") - # 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 Users link + assert html =~ ~s(href="/users") - # Check that nested menu groups exist - assert html =~ ~s(
  • ) - assert html =~ ~s(role="group") - assert has_class?(html, "expanded-menu-group") + # Check for Custom Fields link + assert html =~ ~s(href="/custom_field_values") - # Check that nested menu items exist - assert html =~ ~s(role="menu") + # Check for Contributions section + assert html =~ ~s(href="/contribution_types") + assert html =~ ~s(href="/membership_fee_settings") + + # Check for Settings link (placeholder) + assert html =~ ~s(href="/settings") end test "T2.4: renders sidebar with main-sidebar ID" do @@ -173,59 +174,51 @@ defmodule MvWeb.Layouts.SidebarTest do test "T3.1: renders flat menu items with icons and labels" do html = render_sidebar(authenticated_assigns()) - # Check that top-level menu items have structure - # Top-level items have tooltips + # Check for Members link with icon + assert html =~ ~s(href="/members") + assert html =~ "hero-users" + + # Check for Users link with icon + assert html =~ ~s(href="/users") + assert html =~ "hero-user-circle" + + # Check for Custom Fields link with icon + assert html =~ ~s(href="/custom_field_values") + assert html =~ "hero-rectangle-group" + + # Check for Settings link with icon + assert html =~ ~s(href="/settings") + assert html =~ "hero-cog-6-tooth" + + # Check for tooltips (data-tip attribute) assert html =~ "data-tip=" - assert has_class?(html, "tooltip") - assert has_class?(html, "tooltip-right") - - # Check that menu items have icons (hero-* classes) - assert html =~ ~r/hero-\w+/ - - # Check that menu items have labels - assert has_class?(html, "menu-label") - - # Check that menu items have links - assert html =~ ~s(role="menuitem") end test "T3.2: renders nested menu with details element for expanded state" do html = render_sidebar(authenticated_assigns()) - # Check for nested menu structure - assert html =~ ~s(
  • ) - assert html =~ ~s(role="group") - assert html =~ ~s(aria-label="Administration") + # Check for Contributions section structure with details + 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)" + # Check for contribution links + assert html =~ ~s(href="/contribution_types") + assert html =~ ~s(href="/membership_fee_settings") end test "T3.3: renders nested menu with dropdown for collapsed state" do html = render_sidebar(authenticated_assigns()) - # Check for collapsed dropdown structure + # Check for collapsed dropdown container 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 that dropdown button has icon (any hero icon) - assert html =~ ~r/hero-\w+/ - - # Check ARIA attributes + # Check for icon button + assert html =~ "hero-currency-dollar" assert html =~ ~s(aria-haspopup="menu") end end @@ -353,9 +346,8 @@ defmodule MvWeb.Layouts.SidebarTest do test "T5.4: nested menu has correct ARIA attributes" do html = render_sidebar(authenticated_assigns()) - # Expanded mode should have role="group" with aria-label - assert html =~ ~s(role="group") - assert html =~ ~s(aria-label="Administration") + # Details summary should have haspopup + assert html =~ ~s(aria-haspopup="true") # Dropdown button should have haspopup assert html =~ ~s(aria-haspopup="menu") @@ -422,17 +414,17 @@ defmodule MvWeb.Layouts.SidebarTest do test "T7.1: renders hero icons for menu items" do html = render_sidebar(authenticated_assigns()) - # 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 + # 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" assert html =~ "hero-chevron-left" assert html =~ "hero-chevron-right" - assert html =~ "hero-sun" - assert html =~ "hero-moon" + + # Icons should have aria-hidden + assert html =~ ~s(aria-hidden="true") end test "T7.2: renders icons for theme toggle" do @@ -511,25 +503,26 @@ 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 =~ ~s(
  • ) - assert html =~ ~s(role="group") # Footer section assert html =~ "theme-controller" - assert html =~ ~s(action="/set_locale") - # Check that critical navigation exists (at least /members) - assert html =~ ~s(href="/members"), "Critical /members route should exist" + # All expected links + expected_links = [ + "/members", + "/users", + "/custom_field_values", + "/contribution_types", + "/membership_fee_settings", + "/sign-out" + ] + + for link <- expected_links do + assert html =~ ~s(href="#{link}"), "Missing link: #{link}" + end end end @@ -628,10 +621,9 @@ defmodule MvWeb.Layouts.SidebarTest do test "renders expanded menu group" do html = render_sidebar(authenticated_assigns()) - # expanded-menu-group structure present - assert html =~ ~s(
  • ) - assert html =~ ~s(role="group") - assert html =~ ~s(aria-label="Administration") + # details/summary present + 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 + # Inner_block items rendered + assert html =~ ~s(href="/contribution_types") + assert html =~ ~s(href="/membership_fee_settings") assert html =~ ~s(role="menu") end end @@ -841,10 +821,9 @@ defmodule MvWeb.Layouts.SidebarTest do assert has_class?(html, "expanded-menu-group") assert has_class?(html, "collapsed-menu-group") - # Expanded menu group should have correct structure - # (CSS handles hover effects, but we verify structure) - assert html =~ ~s(
  • ) - assert html =~ ~s(role="group") + # Details element should not have duplicate hover classes + # (CSS handles this, but we verify structure) + assert html =~ "