Class: Primer::Alpha::ActionMenu

Inherits:
Component
  • Object
show all
Defined in:
app/components/primer/alpha/action_menu.rb,
app/components/primer/alpha/action_menu/list.rb

Overview

ActionMenu is used for actions, navigation, to display secondary options, or single/multi select lists. They appear when users interact with buttons, actions, or other controls.

The only allowed elements for the ‘Item` components are: `:a`, `:button`, and `:clipboard-copy`. The default is `:button`.

Defined Under Namespace

Classes: List

Constant Summary collapse

DEFAULT_PRELOAD =
false
DEFAULT_SELECT_VARIANT =
:none
SELECT_VARIANT_OPTIONS =
[
  :single,
  :multiple,
  DEFAULT_SELECT_VARIANT
].freeze

Constants inherited from Component

Component::INVALID_ARIA_LABEL_TAGS

Constants included from Status::Dsl

Status::Dsl::STATUSES

Constants included from ViewHelper

ViewHelper::HELPERS

Constants included from TestSelectorHelper

TestSelectorHelper::TEST_SELECTOR_TAG

Constants included from FetchOrFallbackHelper

FetchOrFallbackHelper::InvalidValueError

Constants included from Primer::AttributesHelper

Primer::AttributesHelper::PLURAL_ARIA_ATTRIBUTES, Primer::AttributesHelper::PLURAL_DATA_ATTRIBUTES

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Component

deprecated?, generate_id

Methods included from JoinStyleArgumentsHelper

#join_style_arguments

Methods included from TestSelectorHelper

#add_test_selector

Methods included from FetchOrFallbackHelper

#fetch_or_fallback, #fetch_or_fallback_boolean, #silence_deprecations?

Methods included from ClassNameHelper

#class_names

Methods included from Primer::AttributesHelper

#aria, #data, #merge_aria, #merge_data, #merge_prefixed_attribute_hashes

Constructor Details

#initialize(menu_id: self.class.generate_id, anchor_align: Primer::Alpha::Overlay::DEFAULT_ANCHOR_ALIGN, anchor_side: Primer::Alpha::Overlay::DEFAULT_ANCHOR_SIDE, size: Primer::Alpha::Overlay::DEFAULT_SIZE, src: nil, preload: DEFAULT_PRELOAD, dynamic_label: false, dynamic_label_prefix: nil, select_variant: DEFAULT_SELECT_VARIANT, form_arguments: {}, **system_arguments) ⇒ ActionMenu

Returns a new instance of ActionMenu.

Examples:

Default

<%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-0") do |c| %>
  <% c.with_show_button { "Menu" } %>
  <% c.with_item(tag: :a, href: "https://primer.style/design/") do %>
    Primer Design
  <% end %>
  <% c.with_item(tag: :button, type: "button", onclick: "() => {}") do %>
    Quote Reply
  <% end %>
  <% c.with_item(tag: :"clipboard-copy", value: "Text to copy") do %>
    Copy Text
  <% end %>
  <% c.with_item(href: "https://google.com", form_arguments: { name: "foo", value: "bar", method: :post }) do %>
    Submit form
  <% end %>
<% end %>

With caret

<%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-1") do |c| %>
  <% c.with_show_button do |button| %>
    <% button.with_trailing_action_icon(icon: "triangle-down") %>
    Menu
  <% end %>
  <% c.with_item(tag: :a, href: "https://primer.style/design/") do %>
    Primer Design
  <% end %>
  <% c.with_item(tag: :button, type: "button") do %>
    Quote Reply
  <% end %>
  <% c.with_item(tag: :"clipboard-copy", value: "Text to copy") do %>
    Copy Text
  <% end %>
<% end %>

With ‘IconButton` trigger

@description
  Set `icon:` to the octicon you want to use. Always provide an accessible name for the menu by setting `aria-label`.
@code
 <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-2") do |c| %>
   <% c.with_show_button(icon: :"kebab-horizontal", "aria-label": "Menu") %>
   <% c.with_item(tag: :a, href: "https://primer.style/design/") do %>
     Primer Design Link
   <% end %>
   <% c.with_item(tag: :button, type: "button") do %>
     Quote Reply
   <% end %>
   <% c.with_item(tag: :"clipboard-copy", value: "Text to copy") do %>
     Copy Text
   <% end %>
 <% end %>

With divider

<%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-3") do |c| %>
  <% c.with_show_button(icon: :"kebab-horizontal", "aria-label": "Menu") %>
  <% c.with_item(tag: :a, href: "https://primer.style/design/") do %>
    Primer Design Link
  <% end %>
  <% c.with_item(tag: :button, type: "button") do %>
    Quote Reply
  <% end %>
  <% c.with_divider %>
  <% c.with_item(tag: :"clipboard-copy", value: "Text to copy") do %>
    Copy Text
  <% end %>
<% end %>

With danger item

<%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-4") do |c| %>
  <% c.with_show_button(icon: :"kebab-horizontal", "aria-label": "Menu") %>
  <% c.with_item(tag: :a, href: "https://primer.style/design/") do %>
    Primer Design Link
  <% end %>
  <% c.with_item(tag: :button, type: "button") do %>
    Quote Reply
  <% end %>
  <% c.with_item(tag: :"clipboard-copy", value: "Text to copy") do %>
    Copy Text
  <% end %>
  <% c.with_item(tag: :button, type: "button", scheme: :danger) do %>
    Delete
  <% end %>
<% end %>

With center align

@description
  Align the menu to the center of the trigger button
@code
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-5", anchor_align: :center, anchor_side: :outside_top) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside top
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-6", anchor_align: :center, anchor_side: :outside_left) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside left
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-7", anchor_align: :center, anchor_side: :outside_right) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside right
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-8", anchor_align: :center, anchor_side: :outside_bottom) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside bottom
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>

With start align

@description
  Align the menu to the start of the trigger button
@code
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-9", anchor_align: :start, anchor_side: :outside_top) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside top
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-10", anchor_align: :start, anchor_side: :outside_left) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside left
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-11", anchor_align: :start, anchor_side: :outside_right) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside right
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-12", anchor_align: :start, anchor_side: :outside_bottom) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside bottom
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>

With end align

@description
  Align the menu to the end of the trigger button
@code
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-13", anchor_align: :end, anchor_side: :outside_top) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside top
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-14", anchor_align: :end, anchor_side: :outside_left) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside left
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-15", anchor_align: :end, anchor_side: :outside_right) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside right
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>
  <%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-16", anchor_align: :end, anchor_side: :outside_bottom) do |c| %>
    <% c.with_show_button do |button| %>
      <% button.with_trailing_action_icon(icon: "triangle-down") %>
      Outside bottom
    <% end %>
    <% c.with_item do %>
      Item 1 that does something
    <% end %>
    <% c.with_item do %>
      Item 2 that does another thing
    <% end %>
  <% end %>

With deferred menu content loaded with an ‘include-fragment`

<%= render Primer::Alpha::ActionMenu.new(menu_id: "my-action-menu-17", src: "/") do |c| %>
  <% c.with_show_button(icon: :"kebab-horizontal", "aria-label": "Menu") %>
<% end %>

Using a single-select ActionMenu as a form input

<%= form_with(url: action_menu_form_action_path) do |f| %>
  <%= render(Primer::Alpha::ActionMenu.new(select_variant: :single, dynamic_label: true, dynamic_label_prefix: "Strategy", form_arguments: { builder: f, name: "foo" })) do |menu| %>
    <% menu.with_show_button { "Strategy" } %>
    <% menu.with_item(label: "Fast forward", data: { value: "fast_forward" }) %>
    <% menu.with_item(label: "Recursive", data: { value: "recursive" }) %>
    <% menu.with_item(label: "Ours", data: { value: "ours" }) %>
    <% menu.with_item(label: "Resolve", data: { value: "resolve" }) %>
  <% end %>
<% end %>

Using a multi-select ActionMenu as a form input

<%= form_with(url: action_menu_form_action_path) do |f| %>
  <%= render(Primer::Alpha::ActionMenu.new(select_variant: :multiple, form_arguments: { builder: f, name: "foo" })) do |menu| %>
    <% menu.with_show_button { "Strategy" } %>
    <% menu.with_item(label: "Fast forward", data: { value: "fast_forward" }) %>
    <% menu.with_item(label: "Recursive", data: { value: "recursive" }) %>
    <% menu.with_item(label: "Ours", data: { value: "ours" }) %>
    <% menu.with_item(label: "Resolve", data: { value: "resolve" }) %>
  <% end %>
<% end %>

Parameters:

  • menu_id (String) (defaults to: self.class.generate_id)

    Id of the menu.

  • anchor_align (Symbol) (defaults to: Primer::Alpha::Overlay::DEFAULT_ANCHOR_ALIGN)

    <%= one_of(Primer::Alpha::Overlay::ANCHOR_ALIGN_OPTIONS) %>.

  • anchor_side (Symbol) (defaults to: Primer::Alpha::Overlay::DEFAULT_ANCHOR_SIDE)

    <%= one_of(Primer::Alpha::Overlay::ANCHOR_SIDE_OPTIONS) %>.

  • size (Symbol) (defaults to: Primer::Alpha::Overlay::DEFAULT_SIZE)

    <%= one_of(Primer::Alpha::Overlay::SIZE_OPTIONS) %>.

  • src (String) (defaults to: nil)

    Used with an ‘include-fragment` element to load menu content from the given source URL.

  • preload (Boolean) (defaults to: DEFAULT_PRELOAD)

    When true, and src is present, loads the ‘include-fragment` on trigger hover.

  • dynamic_label (Boolean) (defaults to: false)

    Whether or not to display the text of the currently selected item in the show button.

  • dynamic_label_prefix (String) (defaults to: nil)

    If provided, the prefix is prepended to the dynamic label and displayed in the show button.

  • select_variant (Symbol) (defaults to: DEFAULT_SELECT_VARIANT)

    <%= one_of(Primer::Alpha::ActionMenu::SELECT_VARIANT_OPTIONS) %>

  • form_arguments (Hash) (defaults to: {})

    Allows an ‘ActionMenu` to act as a select list in multi- and single-select modes. Pass the `builder:` and `name:` options to this hash. `builder:` should be an instance of `ActionView::Helpers::FormBuilder`, which are created by the standard Rails `#form_with` and `#form_for` helpers. The `name:` option is the desired name of the field that will be included in the params sent to the server on form submission.

  • system_arguments (Hash)

    <%= link_to_system_arguments_docs %>.



310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
# File 'app/components/primer/alpha/action_menu.rb', line 310

def initialize(
  menu_id: self.class.generate_id,
  anchor_align: Primer::Alpha::Overlay::DEFAULT_ANCHOR_ALIGN,
  anchor_side: Primer::Alpha::Overlay::DEFAULT_ANCHOR_SIDE,
  size: Primer::Alpha::Overlay::DEFAULT_SIZE,
  src: nil,
  preload: DEFAULT_PRELOAD,
  dynamic_label: false,
  dynamic_label_prefix: nil,
  select_variant: DEFAULT_SELECT_VARIANT,
  form_arguments: {},
  **system_arguments
)
  @menu_id = menu_id
  @src = src
  @preload = fetch_or_fallback_boolean(preload, DEFAULT_PRELOAD)
  @system_arguments = deny_tag_argument(**system_arguments)

  @system_arguments[:preload] = true if @src.present? && preload?

  select_variant = fetch_or_fallback(SELECT_VARIANT_OPTIONS, select_variant, DEFAULT_SELECT_VARIANT)

  @system_arguments[:tag] = :"action-menu"
  @system_arguments[:"data-select-variant"] = select_variant
  @system_arguments[:"data-dynamic-label"] = "" if dynamic_label
  @system_arguments[:"data-dynamic-label-prefix"] = dynamic_label_prefix if dynamic_label_prefix.present?

  @overlay = Primer::Alpha::Overlay.new(
    id: "#{@menu_id}-overlay",
    title: "Menu",
    visually_hide_title: true,
    anchor_align: anchor_align,
    anchor_side: anchor_side,
    size: size
  )

  @list = Primer::Alpha::ActionMenu::List.new(
    menu_id: @menu_id,
    select_variant: select_variant,
    form_arguments: form_arguments
  )
end

Instance Attribute Details

#listObject (readonly)

Returns the value of attribute list.



26
27
28
# File 'app/components/primer/alpha/action_menu.rb', line 26

def list
  @list
end

#preloadObject (readonly) Also known as: preload?

Returns the value of attribute preload.



26
27
28
# File 'app/components/primer/alpha/action_menu.rb', line 26

def preload
  @preload
end

Instance Method Details

#with_avatar_item(**system_arguments, &block) ⇒ Object

Adds an avatar item to the list. Avatar items are a convenient way to accessibly add an item with a leading avatar image.

Parameters:

  • src (String)

    The source url of the avatar image.

  • username (String)

    The username associated with the avatar.

  • full_name (String)

    Optional. The user’s full name.

  • full_name_scheme (Symbol)

    Optional. How to display the user’s full name.

  • avatar_arguments (Hash)

    Optional. The arguments accepted by <%= link_to_component(Primer::Beta::Avatar) %>.

  • system_arguments (Hash)

    The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Item) %>.



394
395
396
# File 'app/components/primer/alpha/action_menu.rb', line 394

def with_avatar_item(**system_arguments, &block)
  @list.with_avatar_item(**system_arguments, &block)
end

#with_divider(**system_arguments, &block) ⇒ Object

Adds a divider to the list.

Parameters:

  • system_arguments (Hash)

    The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList) %>‘s `divider` slot.



382
383
384
# File 'app/components/primer/alpha/action_menu.rb', line 382

def with_divider(**system_arguments, &block)
  @list.with_divider(**system_arguments, &block)
end

#with_item(**system_arguments, &block) ⇒ Object

Adds a new item to the list.

Parameters:

  • system_arguments (Hash)

    The arguments accepted by <%= link_to_component(Primer::Alpha::ActionList::Item) %>.



375
376
377
# File 'app/components/primer/alpha/action_menu.rb', line 375

def with_item(**system_arguments, &block)
  @list.with_item(**system_arguments, &block)
end

#with_show_button(**system_arguments, &block) ⇒ Object

Button to activate the menu.

Parameters:

  • system_arguments (Hash)

    The arguments accepted by <%= link_to_component(Primer::Alpha::Overlay) %>‘s `show_button` slot.



362
363
364
# File 'app/components/primer/alpha/action_menu.rb', line 362

def with_show_button(**system_arguments, &block)
  @overlay.with_show_button(**system_arguments, id: "#{@menu_id}-button", controls: "#{@menu_id}-list", &block)
end