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>
  );
}