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

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

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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69                                                    196x                                   217x                   217x                            
import type { ButtonHTMLAttributes, ReactNode } from 'react';
 
/**
 * Reusable button. Styling lives in globals.css (`.site-button*`) so the
 * interactive states are CSS-driven; this component only composes class names.
 *
 * Variants: `ghost`, `outline` (secondary), `brand` (primary).
 * Sizes: `sm` 32px, `md` 44px (default), `lg` 52px.
 *
 * Two forms:
 *   - Text: pass `children`, optionally with an `iconLeft` / `iconRight` (20px).
 *   - Icon-only: pass `iconOnly` and a single icon as `children`. Square; always
 *     supply an `aria-label` since there's no visible text.
 */
type ButtonVariant = 'ghost' | 'outline' | 'brand';
type ButtonSize = 'sm' | 'md' | 'lg';
 
interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
  variant?: ButtonVariant;
  size?: ButtonSize;
  iconLeft?: ReactNode;
  iconRight?: ReactNode;
  iconOnly?: boolean;
}
 
function ButtonIcon({ children }: { children: ReactNode }) {
  return (
    <span className="site-button__icon" aria-hidden="true">
      {children}
    </span>
  );
}
 
export function Button({
  variant = 'ghost',
  size = 'md',
  iconLeft,
  iconRight,
  iconOnly = false,
  className,
  type = 'button',
  children,
  ...rest
}: ButtonProps) {
  const classes = [
    'site-button',
    `site-button--${variant}`,
    `site-button--${size}`,
    iconOnly ? 'site-button--icon' : '',
    className ?? '',
  ]
    .filter(Boolean)
    .join(' ');
 
  return (
    <button type={type} className={classes} {...rest}>
      {iconOnly ? (
        <ButtonIcon>{children}</ButtonIcon>
      ) : (
        <>
          {iconLeft && <ButtonIcon>{iconLeft}</ButtonIcon>}
          {children}
          {iconRight && <ButtonIcon>{iconRight}</ButtonIcon>}
        </>
      )}
    </button>
  );
}