All files / src/components/editable EnvironmentSelect.tsx

0% Statements 0/18
0% Branches 0/25
0% Functions 0/5
0% Lines 0/16

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                                                                                                                                                                                   
import { type JSX, type ReactNode } from "react";
import { EditableSelect } from "./EditableSelect.js";
import type { Environment } from "../../hooks/types.js";
import styles from "./EnvironmentSelect.module.scss";
 
/** Props for EnvironmentSelect. */
export interface EnvironmentSelectProps {
  /** Currently selected environment ID. */
  value: string;
  /** Called when the user selects a new environment. */
  onSave: (envId: string) => void;
  /** Available environments. */
  environments: Environment[];
  /** Whether to include a "None" option. */
  allowNone?: boolean;
  /** Unique field identifier for coordination with other editable fields. */
  fieldId?: string;
  /** Which field is currently being edited (parent coordination). */
  activeFieldId?: string | null; // eslint-disable-line @rushstack/no-new-null
  /** Callback to tell the parent which field is active. */
  onActivate?: (fieldId: string | null) => void; // eslint-disable-line @rushstack/no-new-null
  /** Placeholder text when no value is selected. */
  placeholder?: string;
  /** Accessible label. */
  ariaLabel?: string;
  /** Base test ID. */
  "data-testid"?: string;
}
 
/** Map environment status to a CSS class for the status dot. */
function envStatusClass(status: string): string {
  const s = status.toLowerCase();
  if (s === "ready" || s === "running" || s === "available" || s === "connected")
    return styles.envDotGreen;
  if (s === "provisioning" || s === "starting" || s === "pending" || s === "connecting")
    return styles.envDotYellow;
  if (s === "error" || s === "failed" || s === "disconnected") return styles.envDotRed;
  return styles.envDotGray;
}
 
/** Reusable environment selector with status dot display. Click-to-edit EditableSelect. */
export function EnvironmentSelect(props: EnvironmentSelectProps): JSX.Element {
  const {
    value,
    onSave,
    environments,
    allowNone = false,
    fieldId = "environment",
    activeFieldId,
    onActivate,
    placeholder = "No environment",
    ariaLabel = "Environment",
    "data-testid": testId,
  } = props;
 
  const selectedEnv = environments.find((e) => e.id === value);
 
  const options = [
    ...(allowNone ? [{ value: "", label: "None" }] : []),
    ...environments.map((env) => ({ value: env.id, label: env.displayName })),
  ];
 
  const renderDisplay = (): ReactNode | undefined => {
    if (selectedEnv) {
      return (
        <span className={styles.envRow}>
          <span className={`${styles.envDot} ${envStatusClass(selectedEnv.status)}`} />
          {selectedEnv.displayName}
        </span>
      );
    }
    return undefined;
  };
 
  return (
    <EditableSelect
      value={value}
      onSave={onSave}
      options={options}
      fieldId={fieldId}
      activeFieldId={activeFieldId}
      onActivate={onActivate}
      renderDisplay={renderDisplay}
      placeholder={placeholder}
      ariaLabel={ariaLabel}
      data-testid={testId}
    />
  );
}