# All files / src/components/layout StatusBar.tsx

90.9% Statements 10/11
88.88% Branches 8/9
75% Functions 3/4
88.88% Lines 8/9

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
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124

    8x

    8x

    36x
    36x
    36x
    36x
    36x

    36x

     import type { JSX } from "react";
    import { Circle, Menu, PanelLeft } from "lucide-react";
    import type { ConnectionStatus, Environment, Session } from "../../hooks/types.js";
    import { ICON_LG, ICON_XS } from "../../utils/iconSize.js";
    import { HOME_URL, useAppNavigate } from "../../utils/navigation.js";
    import { assetUrl } from "../../utils/assetUrl.js";
    import { Tooltip } from "../display/Tooltip.js";
    import styles from "./StatusBar.module.scss";

    /** Human-readable label for each connection state. */
    const CONNECTION_LABEL: Record<ConnectionStatus, string> = {
      connected: "Connected",
      connecting: "Connecting...",
      disconnected: "Disconnected",
    };

    /** CSS class for the connection dot in each state. */
    const CONNECTION_DOT_CLASS: Record<ConnectionStatus, string> = {
      connected: styles.connected,
      connecting: styles.connecting,
      disconnected: styles.disconnected,
    };

    /** Props for the StatusBar component. */
    interface StatusBarProps {
      /** Current connection state of the event stream. */
      connectionStatus: ConnectionStatus;
      /** List of all environments. */
      environments: Environment[];
      /** List of all sessions. */
      sessions: Session[];
      /** Callback to toggle the mobile sidebar drawer. */
      onToggleSidebar?: () => void;
      /** Whether the sidebar drawer is currently open (for aria-expanded). */
      sidebarOpen?: boolean;
      /** Callback to toggle the mobile context-nav drawer. */
      onToggleContextNav?: () => void;
      /** Whether the context-nav drawer is currently open (for aria-expanded). */
      contextNavOpen?: boolean;
    }

    /** Top status bar showing connection state, environment counts, and active session count. */
    export function StatusBar({
      connectionStatus,
      environments,
      sessions,
      onToggleSidebar,
      sidebarOpen,
      onToggleContextNav,
      contextNavOpen,
    }: StatusBarProps): JSX.Element {
      const navigate = useAppNavigate();
      const totalEnvs = environments.length;
      const connectedEnvs = environments.filter((e) => e.status === "connected").length;
      const activeCount = sessions.filter((s) => ["running", "idle"].includes(s.status)).length;
      const label = CONNECTION_LABEL[connectionStatus];

      return (
        <div className={styles.container}>
          {(onToggleContextNav ?? onToggleSidebar) && (
            <div className={styles.drawerControls}>
              {onToggleContextNav && (
                <button
                  type="button"
                  className={styles.hamburger}
                  onClick={onToggleContextNav}
                  aria-label="Toggle contexts"
                  aria-expanded={contextNavOpen}
                  data-testid="statusbar-context-toggle"
                >
                  <PanelLeft size={ICON_LG} aria-hidden="true" />
                </button>
              )}
              {onToggleSidebar && (
                <button
                  type="button"
                  className={styles.hamburger}
                  onClick={onToggleSidebar}
                  aria-label="Toggle sidebar"
                  aria-expanded={sidebarOpen}
                >
                  <Menu size={ICON_LG} aria-hidden="true" />
                </button>
              )}
            </div>
          )}
          <Tooltip text="Home" placement="bottom">
            <button
              type="button"
              className={styles.brand}
              onClick={() => navigate(HOME_URL)}
              data-testid="statusbar-brand"
            >
              <img
                src={assetUrl("icon-192x192.png")}
                alt=""
                className={styles.brandLogo}
                aria-hidden="true"
                data-testid="statusbar-logo"
              />
              Grackle
            </button>
          </Tooltip>
          <div className={styles.info}>
            <span aria-label={label}>
              <span
                className={`${styles.connectionDot} ${CONNECTION_DOT_CLASS[connectionStatus]}`}
                aria-hidden="true"
              >
                <Circle size={ICON_XS} fill="currentColor" />
              </span>{" "}
              <span className={styles.connectionLabel} aria-hidden="true">
                {label}
              </span>
            </span>
            <span>
              {connectedEnvs}/{totalEnvs} env{totalEnvs !== 1 ? "s" : ""}
            </span>
            <span>{activeCount} active</span>
          </div>
        </div>
      );
    }