# All files / src/components/editable EnvironmentSelect.tsx

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

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