diff --git a/app/components/DS/menu.html.erb b/app/components/DS/menu.html.erb index ed6490184..27c262a59 100644 --- a/app/components/DS/menu.html.erb +++ b/app/components/DS/menu.html.erb @@ -1,26 +1,16 @@ <%= tag.div data: { controller: "DS--menu", DS__menu_placement_value: placement, DS__menu_offset_value: offset, DS__menu_mobile_fullwidth_value: mobile_fullwidth, testid: testid } do %> <% if variant == :icon %> - <%= render DS::Button.new(variant: "icon", icon: icon_vertical ? "more-vertical" : "more-horizontal", data: { DS__menu_target: "button" }) %> + <%= render DS::Button.new(variant: "icon", icon: icon_vertical ? "more-vertical" : "more-horizontal", aria: { haspopup: "menu", expanded: "false", controls: menu_id }, data: { DS__menu_target: "button" }) %> <% elsif variant == :button %> <%= button %> - <% elsif variant == :avatar %> - <% end %> - - <%= render DS::Menu.new(variant: "button", no_padding: true) do |menu| %> - <% menu.with_button( + <%= render DS::Popover.new(variant: "button", no_padding: true) do |popover| %> + <% popover.with_button( id: "transaction-filters-button", type: "button", text: t(".filter"), @@ -26,7 +26,7 @@ icon: "list-filter" ) %> - <% menu.with_custom_content do %> + <% popover.with_custom_content do %> <%= render "transactions/searches/menu", form: form %> <% end %> <% end %> diff --git a/app/views/transactions/searches/_menu.html.erb b/app/views/transactions/searches/_menu.html.erb index db7370e30..c424b20ed 100644 --- a/app/views/transactions/searches/_menu.html.erb +++ b/app/views/transactions/searches/_menu.html.erb @@ -37,7 +37,7 @@
- <%= render DS::Button.new(text: t(".cancel"), type: "button", variant: "ghost", data: { action: "DS--menu#close" }) %> + <%= render DS::Button.new(text: t(".cancel"), type: "button", variant: "ghost", data: { action: "DS--popover#close" }) %> <%= render DS::Button.new(text: t(".apply"), type: :submit) %>
diff --git a/app/views/users/_user_menu.html.erb b/app/views/users/_user_menu.html.erb index ddb2bc561..6268defb9 100644 --- a/app/views/users/_user_menu.html.erb +++ b/app/views/users/_user_menu.html.erb @@ -3,19 +3,20 @@ <% intro_mode = local_assigns.fetch(:intro_mode, false) %>
- <%= render DS::Menu.new( - variant: "avatar", + <%# `DS::Popover` (not `DS::Menu`) because the panel includes a + decorative profile header alongside the action items — `role="menu"` + would restrict AT users to menuitem-only navigation and hide the + user-info block. %> + <%= render DS::Popover.new( + variant: intro_mode ? "icon" : "avatar", avatar_url: user.profile_image&.variant(:small)&.url, initials: user.initials, placement: placement, - offset: offset - ) do |menu| %> - <% if intro_mode %> - <% menu.with_button do %> - <%= render DS::Button.new(variant: "icon", icon: "settings", data: { DS__menu_target: "button" }) %> - <% end %> - <% end %> - <%= menu.with_header do %> + offset: offset, + icon: "settings", + aria_label: t(".aria_label", default: "Open account menu") + ) do |popover| %> + <%= popover.with_header do %>
<%= render "settings/user_avatar", avatar_url: user.profile_image&.variant(:small)&.url, initials: user.initials, lazy: true %> @@ -43,21 +44,27 @@ <% end %> <% end %> - <% menu.with_item( - variant: "link", - text: t(".settings"), - icon: "settings", - href: intro_mode ? settings_profile_path : accounts_path(return_to: request.fullpath) - ) %> - <% menu.with_item(variant: "link", text: t(".changelog"), icon: "box", href: changelog_path) %> + <% popover.with_custom_content do %> + <%# `roving: false` keeps these items in the normal Tab order. The parent + `DS::Popover` has no arrow-key roving handler and is not a `role="menu"` + container, so the default `tabindex="-1"` would skip every item. %> + <%= render DS::MenuItem.new( + variant: "link", + roving: false, + text: t(".settings"), + icon: "settings", + href: intro_mode ? settings_profile_path : accounts_path(return_to: request.fullpath) + ) %> + <%= render DS::MenuItem.new(variant: "link", roving: false, text: t(".changelog"), icon: "box", href: changelog_path) %> - <% if self_hosted? && !intro_mode %> - <% menu.with_item(variant: "link", text: t(".feedback"), icon: "megaphone", href: feedback_path) %> + <% if self_hosted? && !intro_mode %> + <%= render DS::MenuItem.new(variant: "link", roving: false, text: t(".feedback"), icon: "megaphone", href: feedback_path) %> + <% end %> + <%= render DS::MenuItem.new(variant: "link", roving: false, text: t(".contact"), icon: "message-square-more", href: "https://discord.gg/36ZGBsxYEK", target: "_blank", rel: "noopener noreferrer") %> + + <%= render DS::MenuItem.new(variant: "divider") %> + + <%= render DS::MenuItem.new(variant: "button", roving: false, text: t(".log_out"), icon: "log-out", href: session_path(Current.session), method: :delete) %> <% end %> - <% menu.with_item(variant: "link", text: t(".contact"), icon: "message-square-more", href: "https://discord.gg/36ZGBsxYEK", target: "_blank", rel: "noopener noreferrer") %> - - <% menu.with_item(variant: "divider") %> - - <% menu.with_item(variant: "button", text: t(".log_out"), icon: "log-out", href: session_path(Current.session), method: :delete) %> <% end %>
diff --git a/config/locales/views/components/en.yml b/config/locales/views/components/en.yml index 11d896222..e0dfca1ab 100644 --- a/config/locales/views/components/en.yml +++ b/config/locales/views/components/en.yml @@ -89,6 +89,8 @@ en: default_label: Preview dialog: close: Close + popover: + avatar_default_label: Open menu tooltip: trigger_label: More info link: diff --git a/config/locales/views/users/en.yml b/config/locales/views/users/en.yml index 56bb4992d..9537d57ba 100644 --- a/config/locales/views/users/en.yml +++ b/config/locales/views/users/en.yml @@ -21,6 +21,7 @@ en: member: Member super_admin: Super admin user_menu: + aria_label: Open account menu version: Version settings: Settings changelog: Changelog diff --git a/test/components/previews/menu_component_preview.rb b/test/components/previews/menu_component_preview.rb index 78049537b..cf5e1b8e9 100644 --- a/test/components/previews/menu_component_preview.rb +++ b/test/components/previews/menu_component_preview.rb @@ -12,33 +12,14 @@ class MenuComponentPreview < ViewComponent::Preview end end - def avatar - render DS::Menu.new(variant: "avatar") do |menu| - menu_contents(menu) - end - end - private def menu_contents(menu) - menu.with_header do - content_tag(:div, class: "p-3") do - content_tag(:h3, "Menu header", class: "font-medium text-primary") - end - end - menu.with_item(variant: "link", text: "Link", href: "#", icon: "plus") menu.with_item(variant: "button", text: "Action", href: "#", method: :post, icon: "circle") menu.with_item(variant: "button", text: "Action destructive", href: "#", method: :delete, icon: "circle") menu.with_item(variant: "divider") - menu.with_custom_content do - content_tag(:div, class: "p-4") do - safe_join([ - content_tag(:h3, "Custom content header", class: "font-medium text-primary"), - content_tag(:p, "Some custom content", class: "text-sm text-secondary") - ]) - end - end + menu.with_item(variant: "link", text: "Another link", href: "#", icon: "external-link") end end diff --git a/test/system/settings_test.rb b/test/system/settings_test.rb index 1107c2aed..96ec24dcc 100644 --- a/test/system/settings_test.rb +++ b/test/system/settings_test.rb @@ -104,7 +104,7 @@ class SettingsTest < ApplicationSystemTestCase def open_settings_from_sidebar user_menu = find("div[data-testid=user-menu]", match: :first, visible: :visible) within user_menu do - find("[data-DS--menu-target='button']", match: :first).click + find("[data-DS--popover-target='button']", match: :first).click click_link "Settings", match: :first end end