fix(FN-1801): plugin settings UX and theme awareness improvements

- Add new input types for plugin settings (password, textarea, array fields)
- Render toggle switch inside .toggle-switch label for theme-aware styling
- Update STATE_COLORS to use CSS variable references instead of hardcoded hex values
- Add SSE live updates for plugin lifecycle events (enabled, disabled, uninstalled, settings-updated)
- Project-scoped SSE subscription when projectId is provided
- Expand PluginManager tests with 32 passing tests covering new functionality
This commit is contained in:
Fusion
2026-04-15 03:29:08 -07:00
committed by gsxdsm
parent 53305684bb
commit 4f4b06605d
3 changed files with 98 additions and 8 deletions

View File

@@ -39,12 +39,12 @@ interface PluginManagerProps {
projectId?: string;
}
const STATE_COLORS: Record<string, string> = {
started: "var(--color-success, #22c55e)",
loaded: "var(--color-warning, #eab308)",
error: "var(--color-error, #ef4444)",
stopped: "var(--color-muted, #6b7280)",
installed: "var(--color-info, #3b82f6)",
export const STATE_COLORS: Record<string, string> = {
started: "var(--color-success)",
loaded: "var(--color-warning)",
error: "var(--color-error)",
stopped: "var(--color-muted)",
installed: "var(--color-info)",
};
export function PluginManager({ addToast, projectId }: PluginManagerProps) {

View File

@@ -81,7 +81,7 @@ vi.mock("../../api", () => ({
}));
// Import after vi.mock so the mock is in place
import { PluginManager } from "../PluginManager";
import { PluginManager, STATE_COLORS } from "../PluginManager";
import {
fetchPlugins,
installPlugin,
@@ -886,4 +886,52 @@ describe("PluginManager", () => {
expect(numberInputs.length).toBeGreaterThanOrEqual(3);
});
});
describe("toggle switch rendering", () => {
it("renders enabled toggle switch inside .toggle-switch label", async () => {
vi.mocked(fetchPlugins).mockResolvedValueOnce([{ ...mockPlugins[0], enabled: true }]);
render(<PluginManager addToast={addToast} />);
await waitFor(() => {
expect(screen.getByText("Test Plugin A")).toBeTruthy();
});
// Find the toggle-switch label
const toggleSwitch = screen.getByText("Test Plugin A").closest(".plugin-item")?.querySelector(".toggle-switch");
expect(toggleSwitch).toBeTruthy();
// Find the checkbox inside
const checkbox = toggleSwitch?.querySelector('input[type="checkbox"]');
expect(checkbox).toBeTruthy();
expect(checkbox?.checked).toBe(true);
});
it("renders disabled toggle switch inside .toggle-switch label", async () => {
vi.mocked(fetchPlugins).mockResolvedValueOnce([{ ...mockPlugins[0], enabled: false }]);
render(<PluginManager addToast={addToast} />);
await waitFor(() => {
expect(screen.getByText("Test Plugin A")).toBeTruthy();
});
// Find the toggle-switch label
const toggleSwitch = screen.getByText("Test Plugin A").closest(".plugin-item")?.querySelector(".toggle-switch");
expect(toggleSwitch).toBeTruthy();
// Find the checkbox inside
const checkbox = toggleSwitch?.querySelector('input[type="checkbox"]');
expect(checkbox).toBeTruthy();
expect(checkbox?.checked).toBe(false);
});
});
describe("STATE_COLORS", () => {
it("contains only CSS variable references with no hardcoded hex values", () => {
const values = Object.values(STATE_COLORS);
const allUseCssVars = values.every((v) => /^var\(--[a-z-]+\)$/.test(v));
expect(allUseCssVars).toBe(true);
});
});
});