# All files / src/components/panels PluginsPanel.tsx

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

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

     import type { JSX } from "react";
    import type { PluginData } from "../../hooks/types.js";
    import styles from "./SettingsPanel.module.scss";

    /** Props for the PluginsPanel component. */
    export interface PluginsPanelProps {
      /** List of all known plugins. */
      plugins: PluginData[];
      /** Whether the plugin list is loading. */
      loading: boolean;
      /** Callback invoked when the user toggles a plugin's enabled state. */
      onSetPluginEnabled: (name: string, enabled: boolean) => void;
    }

    /** Settings panel for managing Grackle plugins. */
    export function PluginsPanel({
      plugins,
      loading,
      onSetPluginEnabled,
    }: PluginsPanelProps): JSX.Element {
      return (
        <div className={styles.container} data-testid="plugins-panel">
          <h2 className={styles.heading}>Plugins</h2>
          <div className={styles.section}>
            <p className={styles.sectionDescription}>
              Enable or disable optional Grackle plugins. A server restart is required for changes to
              take effect.
            </p>
            {loading && <p className={styles.emptyState}>Loading plugins...</p>}
            {!loading && plugins.length === 0 && <p className={styles.emptyState}>No plugins found.</p>}
            {!loading &&
              plugins.map((plugin) => {
                const pendingChange = plugin.enabled !== plugin.loaded;
                return (
                  <div
                    key={plugin.name}
                    className={styles.tokenRow}
                    data-testid={`plugin-row-${plugin.name}`}
                  >
                    <div style={{ flex: 1 }}>
                      <div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
                        <span className={styles.tokenName}>{plugin.name}</span>
                        {plugin.required && (
                          <span className={styles.tokenBadge} title="Required — cannot be disabled">
                            required
                          </span>
                        )}
                      </div>
                      <div className={styles.tokenTarget}>{plugin.description}</div>
                      {pendingChange && (
                        <div
                          style={{
                            fontSize: "var(--font-size-xs)",
                            color: "var(--accent-yellow)",
                            marginTop: "2px",
                          }}
                          data-testid={`plugin-restart-notice-${plugin.name}`}
                        >
                          Restart Grackle to apply changes
                        </div>
                      )}
                    </div>
                    <label
                      style={{
                        display: "flex",
                        alignItems: "center",
                        gap: "6px",
                        cursor: plugin.required ? "not-allowed" : "pointer",
                      }}
                      title={plugin.required ? "Core is required and cannot be disabled" : undefined}
                    >
                      <input
                        type="checkbox"
                        checked={plugin.enabled}
                        disabled={plugin.required}
                        onChange={(e) => onSetPluginEnabled(plugin.name, e.target.checked)}
                        data-testid={`plugin-toggle-${plugin.name}`}
                        aria-label={`Enable or disable ${plugin.name} plugin`}
                        style={{ accentColor: "var(--accent-green)", width: "16px", height: "16px" }}
                      />
                    </label>
                  </div>
                );
              })}
          </div>
        </div>
      );
    }