# All files / src/components/display SectionHeader.tsx

0% Statements 0/3
0% Branches 0/6
0% Functions 0/2
0% Lines 0/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
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82

     /**
     * SectionHeader -- shared uppercase section label with optional icon-button actions.
     *
     * Used atop every sidebar nav (Tasks, Environments, Schedules, Personas, Coordination)
     * to provide a consistent header strip. Actions (filter, group, sort, add, etc.) only
     * render when provided.
     *
     * @module
     */

    import { type JSX, type ReactNode } from "react";
    import { Tooltip } from "./Tooltip.js";
    import styles from "./SectionHeader.module.scss";

    /** A single action button rendered in the SectionHeader trailing slot. */
    export interface SectionHeaderAction {
      /** Unique key for React rendering. */
      key: string;
      /** Icon element (typically a lucide-react component). */
      icon: ReactNode;
      /** Tooltip text shown on hover. */
      tooltip: string;
      /** Accessible label for the button. */
      ariaLabel: string;
      /** Click handler. */
      onClick: () => void;
      /** Whether this action is currently active (renders with green accent). */
      active?: boolean;
      /** aria-pressed value for toggle buttons. */
      ariaPressed?: boolean;
      /** aria-haspopup value for buttons that open menus. */
      ariaHasPopup?: boolean;
      /** aria-expanded value for buttons that control a popup. */
      ariaExpanded?: boolean;
      /** Optional data-testid for the button. */
      testId?: string;
    }

    /** Props for the {@link SectionHeader} component. */
    export interface SectionHeaderProps {
      /** Section title text (rendered uppercase via section-label mixin). */
      title: string;
      /** Optional action buttons rendered in the trailing slot. Only renders the actions container when non-empty. */
      actions?: SectionHeaderAction[];
      /** Optional data-testid for the header root. */
      "data-testid"?: string;
    }

    /** Uppercase section label with optional trailing icon-button actions. */
    export function SectionHeader({
      title,
      actions,
      "data-testid": testId,
    }: SectionHeaderProps): JSX.Element {
      const hasActions = actions !== undefined && actions.length > 0;
      return (
        <div className={styles.header} data-testid={testId}>
          <span className={styles.title}>{title}</span>
          {hasActions && (
            <div className={styles.actions}>
              {actions.map((action) => (
                <Tooltip key={action.key} text={action.tooltip}>
                  <button
                    type="button"
                    className={`${styles.actionButton} ${action.active ? styles.actionActive : ""}`}
                    onClick={action.onClick}
                    aria-label={action.ariaLabel}
                    aria-pressed={action.ariaPressed}
                    aria-haspopup={action.ariaHasPopup}
                    aria-expanded={action.ariaExpanded}
                    data-testid={action.testId}
                  >
                    {action.icon}
                  </button>
                </Tooltip>
              ))}
            </div>
          )}
        </div>
      );
    }