Add one level of submenus to disclosure dropdowns [1/4]

🧩 About this stack (submenu support for disclosure dropdowns)

This is a stack of 4 merge requests. Each one builds on the previous branch, so they are small and reviewable in isolation, but together they deliver one feature: submenus for disclosure dropdowns.

# Merge request Adds Target branch
1 (this MR) Actions submenus The submenu foundation + action rows main
2 Selection submenus Rows that pick a value (listbox) MR 1
3 Search in selection submenus Search box inside the listbox submenu MR 2
4 Documentation Pajamas guidance, no code MR 3

👀 Just want to see/test the end result?

The full experience lands incrementally in Storybook, under Base → Dropdowns → Disclosure Dropdown:

  • This MR (1): story NestedDropdown — hover/click a row to reveal a submenu of links and actions.
  • MR 2 adds story NestedListboxSubmenu (choose a value, single + multi-select).
  • MR 3 extends NestedListboxSubmenu with a searchable submenu.

Review order: read them bottom-to-top for context, but they merge top-to-bottom (1 → 2 → 3 → 4). Merging this MR retargets MR 2 onto main automatically.

What does this MR do?

Adds the foundation for one level of submenus in disclosure dropdowns, plus the first submenu variant: action submenus (rows that reveal more links or actions).

  • submenu_mixin.js holds behaviour shared by all submenu variants: positioning, hover intent, small-viewport handling, open/close, and registration with the owning dropdown.
  • GlDisclosureDropdownItemNested renders a row that reveals more links or actions. Give an item a children array, directly or via items.
  • Depth is capped at one level. itemsValidator rejects deeper trees and GlDisclosureDropdown logs a warning explaining the rule, rather than silently truncating.

Shared files touched (worth extra review)

  • base_dropdown.vue learns to treat nested panels as part of the dropdown for close/containment purposes.
  • disclosure_dropdown.vue gains the submenu registry (one open at a time) plus focus delegation: focusNextItem now resolves an event target to its owning row via closest, and focusItem focuses the row's button when the row itself isn't focusable.

Screenshots or screen recordings

Test in Storybook: Base → Dropdowns → Disclosure Dropdown → NestedDropdown.

Verification

  • Tests: 24 new for the component (plus depth-validator and warning coverage); 423 in the suite for this branch; full package suite: 4213 passing.
  • Lint: clean.
  • Axe: 0 violations (submenus open + closed).
  • Exports GlDisclosureDropdownItemNested.

⚠️ Known CI failure

Visual baselines. The new NestedDropdown story has no baseline yet, so the visual test job will fail until baselines are regenerated (or the story is tagged skip-visual-test, as WithDynamicWrapperText does). This is expected and tracked, not a regression.

Related to #3274. Supersedes the earlier single-MR attempt !6053 (closed).

Edited by Austin Regnery

Merge request reports

Loading
Loading