molecule

ActionMenu

Dropdown menu for contextual actions, commonly used in table rows for row-level operations.

Overview

The ActionMenu component provides a dropdown menu for contextual actions. It's commonly used in table rows, cards, and other contexts where multiple actions need to be accessible without cluttering the UI. Supports keyboard navigation, item variants, and dividers for grouping actions.

import { ActionMenu, IconButton } from '@enara-health/ui-react';
import { MoreVertical, Eye, Edit, Trash } from 'lucide-react';

<ActionMenu
  trigger={<IconButton icon={<MoreVertical />} aria-label="Actions" />}
  items={[
    { id: 'view', icon: <Eye />, label: 'View Details', onClick: handleView },
    { id: 'edit', icon: <Edit />, label: 'Edit', onClick: handleEdit },
    { divider: true },
    { id: 'delete', icon: <Trash />, label: 'Delete', onClick: handleDelete, variant: 'danger' },
  ]}
/>

Note: ActionMenu is an interactive component. To see it in action, run nx dev @enara-health/ui-react and view the component in the dev preview.

Props

Prop Type Default Description
trigger ReactNode - Required. Element that triggers the menu (typically IconButton)
items ActionMenuItemOrDivider[] - Required. Array of menu items and dividers
position 'auto' | 'top' | 'bottom' 'auto' Preferred vertical position of the dropdown
align 'start' | 'end' 'end' Horizontal alignment relative to trigger

ActionMenuItem

Property Type Description
id string Unique identifier for the item
label string Text label for the item
icon ReactNode Icon displayed before the label
onClick () => void Click handler for the item
variant 'default' | 'danger' | 'success' Visual variant for semantic meaning
disabled boolean Whether the item is disabled

ActionMenuDivider

To add a divider between items, include an object with divider: true in the items array.

Variants

Item Variants

Menu items support visual variants for semantic actions:

default Standard menu items for neutral actions
danger Destructive actions like delete or remove
success Positive actions like approve or confirm
// Default variant (neutral)
{ id: 'edit', label: 'Edit', onClick: handleEdit }

// Danger variant for destructive actions
{ id: 'delete', label: 'Delete', onClick: handleDelete, variant: 'danger' }

// Success variant for positive actions
{ id: 'approve', label: 'Approve', onClick: handleApprove, variant: 'success' }

Alignment

Control how the dropdown menu aligns relative to the trigger button:

align="start"
Menu left edge aligns with trigger left edge
align="end" default
Menu right edge aligns with trigger right edge

Examples

Table Row Actions

Common pattern for table row action menus with icons.

<ActionMenu
  trigger={<IconButton icon={<MoreVertical />} aria-label="Row actions" />}
  items={[
    { id: 'view', icon: <Eye />, label: 'View Profile', onClick: handleView },
    { id: 'edit', icon: <Edit />, label: 'Edit User', onClick: handleEdit },
    { divider: true },
    { id: 'delete', icon: <Trash />, label: 'Delete User', onClick: handleDelete, variant: 'danger' },
  ]}
/>

Card Options

Options menu for card-based content.

<ActionMenu
  trigger={<IconButton icon={<MoreVertical />} size="sm" aria-label="Options" />}
  items={[
    { id: 'share', icon: <Share />, label: 'Share', onClick: handleShare },
    { id: 'duplicate', icon: <Copy />, label: 'Duplicate', onClick: handleDuplicate },
    { id: 'archive', icon: <Archive />, label: 'Archive', onClick: handleArchive },
  ]}
  align="end"
/>

With Dividers

Group related actions with dividers for better organization.

items={[
  // Primary actions
  { id: 'view', label: 'View', onClick: handleView },
  { id: 'edit', label: 'Edit', onClick: handleEdit },

  { divider: true },

  // Secondary actions
  { id: 'duplicate', label: 'Duplicate', onClick: handleDuplicate },
  { id: 'archive', label: 'Archive', onClick: handleArchive },

  { divider: true },

  // Destructive action (always last)
  { id: 'delete', label: 'Delete', onClick: handleDelete, variant: 'danger' },
]}

Disabled Items

Items can be disabled when actions aren't currently available.

items={[
  { id: 'view', label: 'View', onClick: handleView },
  { id: 'edit', label: 'Edit', onClick: handleEdit, disabled: true },
  { id: 'delete', label: 'Delete', onClick: handleDelete, disabled: true, variant: 'danger' },
]}

Approval Workflow

Using success and danger variants for approval actions.

<ActionMenu
  trigger={<IconButton icon={<MoreHorizontal />} aria-label="Review actions" />}
  items={[
    { id: 'approve', icon: <Check />, label: 'Approve', onClick: handleApprove, variant: 'success' },
    { id: 'reject', icon: <X />, label: 'Reject', onClick: handleReject, variant: 'danger' },
    { divider: true },
    { id: 'comment', icon: <MessageSquare />, label: 'Add Comment', onClick: handleComment },
  ]}
/>

Accessibility

  • Role: Menu uses role="menu" with items using role="menuitem".
  • Keyboard Navigation: Arrow Up/Down to navigate, Enter/Space to select, Escape to close, Home/End for first/last item.
  • Focus Management: Focus is trapped in the menu when open and returns to trigger on close.
  • ARIA Attributes: Trigger has aria-haspopup="menu" and aria-expanded.
  • WCAG Compliance: Meets AA standards for interactive menus.

Best Practices

  • Use IconButton with aria-label for icon-only triggers
  • Keep primary actions visible - use menus for secondary actions
  • Group related actions with dividers
  • Use danger variant for destructive actions like delete
  • Place destructive actions at the bottom, separated by a divider
  • Limit menu items to 7 or fewer - consider alternative UI for many actions
  • Provide clear, concise labels for each action