From 9d2fbeb940c92c8ba38a19a2c04d193e9c9f0515 Mon Sep 17 00:00:00 2001 From: Igor Fedoronchuk Date: Tue, 20 Oct 2026 17:56:23 +0200 Subject: [PATCH] fix: open a title-bar dropdown under its own button MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The right-alignment guard added for title-bar dropdowns pinned the panel with `right: 0`, but `div.dropdown_menu` is unpositioned (AA leaves it `display: inline`) and the theme resets `#titlebar_right` to static — so the panel resolved against `#title_bar` instead and every menu opened at the bar's far right, under whichever action item happens to sit last. Only bars with a dropdown that is not the rightmost item showed it. Make the dropdown its own containing block, so `right: 0` means the trigger's right edge. inline-block over AA's `display: inline`: an inline box's containing block spans its text fragments, and the whitespace around the button in the markup pushed the panel a few pixels past it. The nipple is pinned right for the same reason - AA offsets the arrow from the panel's left edge by the button's centre, which lands it on empty bar once the panel is right-aligned. The dummy app's wide table now carries four action items, two of them dropdowns, one of those in the middle - the shape that reproduces it. --- .../stylesheets/wigu/active_admin_theme.scss | 35 +++++++++++++------ test/dummy/app/admin/wide_table.rb | 21 +++++++++++ 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/wigu/active_admin_theme.scss b/app/assets/stylesheets/wigu/active_admin_theme.scss index 071987f..7495d2a 100644 --- a/app/assets/stylesheets/wigu/active_admin_theme.scss +++ b/app/assets/stylesheets/wigu/active_admin_theme.scss @@ -768,17 +768,26 @@ body.active_admin { } } div.dropdown_menu { + // Containing block for the panel below, so `right: 0` means "the + // trigger's right edge". AA leaves this element unpositioned, which + // makes the panel resolve against #title_bar (sticky) and park at the + // bar's far right no matter which action item was clicked. inline-block + // over AA's `display: inline`: an inline box's containing block spans + // its text fragments, so the markup's whitespace around the button + // pushed the panel a few px past the button's edge. + position: relative; + display: inline-block; div.dropdown_menu_list_wrapper { - // Always right-align title-bar dropdowns, overriding AA's inline - // positioning. AA 3.x's _position() sets inline `left` OR `right` - // per open depending on a window-geometry branch and never clears - // the other, and the wrapper persists across opens — after a resize - // between opens both offsets accumulate and the width:auto wrapper - // stretches from the stale left coordinate to the right edge. - // Title-bar action items sit at the bar's right edge, so pinning - // right is also simply the correct alignment. Consumer apps used to - // carry exactly this pair as a local hotfix; the theme owns the bar - // now, so it owns the guard. + // Always right-align title-bar dropdowns under their own button, + // overriding AA's inline positioning. AA 3.x's _position() sets + // inline `left` OR `right` per open depending on a window-geometry + // branch and never clears the other, and the wrapper persists across + // opens — after a resize between opens both offsets accumulate and + // the width:auto wrapper stretches from the stale left coordinate to + // the right edge. Its `right` branch is wrong here anyway: it offsets + // from the window, not from the bar. Consumer apps used to carry + // exactly this pair as a local hotfix; the theme owns the bar now, so + // it owns the guard. left: auto !important; right: 0 !important; background-color: var(--aa-elevated); @@ -791,6 +800,12 @@ body.active_admin { // that is invisible whenever the panel fill is close to the page — // which is exactly what a light surface is. Outline it like the panel. div.dropdown_menu_nipple { + // The panel is pinned right (above) while AA still offsets the + // arrow from the panel's LEFT edge by the button's centre, which + // lands it on empty bar. Pin it right too, centred on the button's + // own caret (AA draws that 6px wide at right: 12px). + left: auto !important; + right: 9px !important; border-color: var(--aa-border) transparent; &:before, &:after { border-color: var(--aa-elevated) transparent; } } diff --git a/test/dummy/app/admin/wide_table.rb b/test/dummy/app/admin/wide_table.rb index 6f39e64..79b2c50 100644 --- a/test/dummy/app/admin/wide_table.rb +++ b/test/dummy/app/admin/wide_table.rb @@ -4,8 +4,29 @@ ActiveAdmin.register_page "Wide table" do menu priority: 4 + # A bar with several action items, including a dropdown that is NOT the last + # one: its panel has to open under its own button. While .dropdown_menu was + # unpositioned the panel resolved against #title_bar (sticky) instead and + # every menu parked at the bar's right edge, under the wrong button. + action_item(:edit_record) { link_to "Edit record", "#" } + + action_item(:actions_menu) do + dropdown_menu "Actions" do + item "First action", "#" + item "Second action", "#" + item "Third action", "#" + end + end + action_item(:new_record) { link_to "New record", "#" } + action_item(:more_menu) do + dropdown_menu "More" do + item "Export CSV", "#" + item "Export XML", "#" + end + end + content do # Locals, not constants: assignment inside this block is lexically # top-level (it lands on Object), and AA re-loads every app/admin file on