All files / src/components/layout PageHeader.tsx

0% Statements 0/47
100% Branches 1/1
100% Functions 1/1
0% Lines 0/47

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 70 71 72 73 74 75 76 77 78 79                                                                                                                                                             
/**
 * PageHeader — standardized breadcrumb row with optional back-navigation and actions.
 *
 * @module
 */
 
import type { JSX, ReactNode } from "react";
import { ArrowLeft } from "lucide-react";
import { Link } from "react-router";
import { Breadcrumbs } from "../display/Breadcrumbs.js";
import type { BreadcrumbSegment } from "../../utils/breadcrumbs.js";
import { ICON_SM } from "../../utils/iconSize.js";
import styles from "./PageHeader.module.scss";
 
/** Props for {@link PageHeader}. */
export interface PageHeaderProps {
  /** Breadcrumb segments to render. */
  segments: BreadcrumbSegment[];
  /** Optional back-navigation label (e.g., "Home"). Renders ArrowLeft + label. */
  backLabel?: string;
  /** URL to navigate to when the back button is clicked. */
  backUrl?: string;
  /** Optional callback for back navigation (overrides backUrl if provided). */
  onNavigateBack?: () => void;
  /** Optional actions to render in the right slot. */
  actions?: ReactNode;
}
 
/** Standardized page header with breadcrumbs, optional back-navigation, and actions. */
export function PageHeader({
  segments,
  backLabel,
  backUrl,
  onNavigateBack,
  actions,
}: PageHeaderProps): JSX.Element {
  const showBack = Boolean(backUrl || onNavigateBack);
 
  return (
    <header className={styles.pageHeader} data-testid="page-header">
      {showBack && (
        <>
          {onNavigateBack ? (
            <button
              type="button"
              className={styles.backButton}
              onClick={onNavigateBack}
              aria-label={backLabel ? `Back to ${backLabel}` : "Back"}
              data-testid="page-header-back"
            >
              <ArrowLeft size={ICON_SM} />
              {backLabel && <span className={styles.backLabel}>{backLabel}</span>}
            </button>
          ) : backUrl ? (
            <Link
              to={backUrl}
              className={styles.backButton}
              aria-label={backLabel ? `Back to ${backLabel}` : "Back"}
              data-testid="page-header-back"
            >
              <ArrowLeft size={ICON_SM} />
              {backLabel && <span className={styles.backLabel}>{backLabel}</span>}
            </Link>
          ) : null}
          <span className={styles.backSeparator} aria-hidden="true">
            /
          </span>
        </>
      )}
      <Breadcrumbs segments={segments} />
      {actions && (
        <div className={styles.actions} data-testid="page-header-actions">
          {actions}
        </div>
      )}
    </header>
  );
}