From 39ed8b0fec473b7424735519d8453d5ad8fc7daf Mon Sep 17 00:00:00 2001 From: cliffhall Date: Tue, 21 Apr 2026 18:00:05 -0400 Subject: [PATCH] Make ServerSettingsForm stories fully interactive Replace fn() stubs with useState handlers so add/remove/edit for headers, metadata, connection mode, timeouts, and OAuth all work interactively in Storybook. The InteractiveForm wrapper manages settings state the same way ServerSettingsModal does. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../ServerSettingsForm.stories.tsx | 80 ++++++++++++++----- 1 file changed, 60 insertions(+), 20 deletions(-) diff --git a/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.stories.tsx b/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.stories.tsx index 7c3939ba..d32473f1 100644 --- a/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.stories.tsx +++ b/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.stories.tsx @@ -1,7 +1,6 @@ import { useState } from "react"; import type { InspectorServerSettings } from "@inspector/core/mcp/types.js"; import type { Meta, StoryObj } from "@storybook/react-vite"; -import { fn } from "storybook/test"; import { ServerSettingsForm, type ServerSettingsSection, @@ -15,34 +14,75 @@ const defaultSettings: InspectorServerSettings = { requestTimeout: 60000, }; -const defaultHandlers = { - onConnectionModeChange: fn(), - onAddHeader: fn(), - onRemoveHeader: fn(), - onHeaderChange: fn(), - onAddMetadata: fn(), - onRemoveMetadata: fn(), - onMetadataChange: fn(), - onTimeoutChange: fn(), - onOAuthChange: fn(), -}; - function InteractiveForm({ - settings, + startSettings, initialSections = ["connectionMode"], }: { - settings: InspectorServerSettings; + startSettings: InspectorServerSettings; initialSections?: ServerSettingsSection[]; }) { const [expandedSections, setExpandedSections] = useState(initialSections); + const [settings, setSettings] = + useState(startSettings); return ( + setSettings((s) => ({ ...s, connectionMode: mode })) + } + onAddHeader={() => + setSettings((s) => ({ + ...s, + headers: [...s.headers, { key: "", value: "" }], + })) + } + onRemoveHeader={(index) => + setSettings((s) => ({ + ...s, + headers: s.headers.filter((_, i) => i !== index), + })) + } + onHeaderChange={(index, key, value) => + setSettings((s) => ({ + ...s, + headers: s.headers.map((h, i) => (i === index ? { key, value } : h)), + })) + } + onAddMetadata={() => + setSettings((s) => ({ + ...s, + metadata: [...s.metadata, { key: "", value: "" }], + })) + } + onRemoveMetadata={(index) => + setSettings((s) => ({ + ...s, + metadata: s.metadata.filter((_, i) => i !== index), + })) + } + onMetadataChange={(index, key, value) => + setSettings((s) => ({ + ...s, + metadata: s.metadata.map((m, i) => + i === index ? { key, value } : m, + ), + })) + } + onTimeoutChange={(field, value) => + setSettings((s) => ({ ...s, [field]: value })) + } + onOAuthChange={(field, value) => { + const fieldMap: Record = { + clientId: "oauthClientId", + clientSecret: "oauthClientSecret", + scopes: "oauthScopes", + }; + setSettings((s) => ({ ...s, [fieldMap[field] ?? field]: value })); + }} /> ); } @@ -56,13 +96,13 @@ export default meta; type Story = StoryObj; export const DefaultSettings: Story = { - render: () => , + render: () => , }; export const WithHeaders: Story = { render: () => ( ( (