All files / packages/design-system-2-site/src/components/ui DropdownMenu.tsx

100% Statements 2/2
100% Branches 2/2
100% Functions 2/2
100% Lines 2/2

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49                                    4x                                           9x                
import type { ButtonHTMLAttributes, HTMLAttributes, ReactNode } from 'react';
 
/**
 * Dropdown menu primitives. Styling lives in globals.css (`.dropdown-menu*`);
 * these only compose markup and class names, so open / close / positioning
 * stays with the consumer.
 *
 *   <DropdownMenu>
 *     <DropdownItem icon={<Sun />} role="menuitemradio" aria-checked>Light</DropdownItem>
 *     <DropdownItem icon={<Sun />} trailing={<ChevronRight />}>Appearance</DropdownItem>
 *   </DropdownMenu>
 */
export function DropdownMenu({
  className,
  role = 'menu',
  children,
  ...rest
}: HTMLAttributes<HTMLDivElement>) {
  return (
    <div className={['dropdown-menu', className].filter(Boolean).join(' ')} role={role} {...rest}>
      {children}
    </div>
  );
}
 
interface DropdownItemProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  /** Leading icon, before the label. */
  icon?: ReactNode;
  /** Trailing element, e.g. a caret for submenu items. */
  trailing?: ReactNode;
}
 
export function DropdownItem({
  icon,
  trailing,
  className,
  type = 'button',
  children,
  ...rest
}: DropdownItemProps) {
  return (
    <button type={type} className={['dropdown-menu__item', className].filter(Boolean).join(' ')} {...rest}>
      {icon}
      <span className="dropdown-menu__label">{children}</span>
      {trailing}
    </button>
  );
}