Fix ServerSettingsForm stories to use useArgs for action logging

Use Storybook's useArgs hook from storybook/preview-api to update
args from within the render function. This wires fn() callbacks to
the Actions panel AND updates the UI state through Storybook's args
system. All form interactions (add/remove/edit headers/metadata,
accordion open/close, connection mode, timeouts, OAuth) now both
update the UI and log to the Actions panel.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-04-21 19:37:27 -04:00
commit e04befa4dd

View file

@ -1,9 +1,10 @@
import { useState } from "react";
import type { InspectorServerSettings } from "@inspector/core/mcp/types.js"; import type { InspectorServerSettings } from "@inspector/core/mcp/types.js";
import type { Meta, StoryObj } from "@storybook/react-vite"; import type { Meta, StoryObj } from "@storybook/react-vite";
import { fn } from "storybook/test";
import { useArgs } from "storybook/preview-api";
import { import {
ServerSettingsForm, ServerSettingsForm,
type ServerSettingsSection, type ServerSettingsFormProps,
} from "./ServerSettingsForm"; } from "./ServerSettingsForm";
const defaultSettings: InspectorServerSettings = { const defaultSettings: InspectorServerSettings = {
@ -14,74 +15,99 @@ const defaultSettings: InspectorServerSettings = {
requestTimeout: 60000, requestTimeout: 60000,
}; };
function InteractiveForm({ function InteractiveRender(args: ServerSettingsFormProps) {
startSettings, const [, updateArgs] = useArgs<ServerSettingsFormProps>();
initialSections = ["connectionMode"],
}: {
startSettings: InspectorServerSettings;
initialSections?: ServerSettingsSection[];
}) {
const [expandedSections, setExpandedSections] =
useState<ServerSettingsSection[]>(initialSections);
const [settings, setSettings] =
useState<InspectorServerSettings>(startSettings);
return ( return (
<ServerSettingsForm <ServerSettingsForm
settings={settings} {...args}
expandedSections={expandedSections} onExpandedSectionsChange={(sections) => {
onExpandedSectionsChange={setExpandedSections} args.onExpandedSectionsChange(sections);
onConnectionModeChange={(mode) => updateArgs({ expandedSections: sections });
setSettings((s) => ({ ...s, connectionMode: mode })) }}
} onConnectionModeChange={(mode) => {
onAddHeader={() => args.onConnectionModeChange(mode);
setSettings((s) => ({ updateArgs({
...s, settings: { ...args.settings, connectionMode: mode },
headers: [...s.headers, { key: "", value: "" }], });
})) }}
} onAddHeader={() => {
onRemoveHeader={(index) => args.onAddHeader();
setSettings((s) => ({ updateArgs({
...s, settings: {
headers: s.headers.filter((_, i) => i !== index), ...args.settings,
})) headers: [...args.settings.headers, { key: "", value: "" }],
} },
onHeaderChange={(index, key, value) => });
setSettings((s) => ({ }}
...s, onRemoveHeader={(index) => {
headers: s.headers.map((h, i) => (i === index ? { key, value } : h)), args.onRemoveHeader(index);
})) updateArgs({
} settings: {
onAddMetadata={() => ...args.settings,
setSettings((s) => ({ headers: args.settings.headers.filter((_, i) => i !== index),
...s, },
metadata: [...s.metadata, { key: "", value: "" }], });
})) }}
} onHeaderChange={(index, key, value) => {
onRemoveMetadata={(index) => args.onHeaderChange(index, key, value);
setSettings((s) => ({ updateArgs({
...s, settings: {
metadata: s.metadata.filter((_, i) => i !== index), ...args.settings,
})) headers: args.settings.headers.map((h, i) =>
} i === index ? { key, value } : h,
onMetadataChange={(index, key, value) => ),
setSettings((s) => ({ },
...s, });
metadata: s.metadata.map((m, i) => }}
i === index ? { key, value } : m, onAddMetadata={() => {
), args.onAddMetadata();
})) updateArgs({
} settings: {
onTimeoutChange={(field, value) => ...args.settings,
setSettings((s) => ({ ...s, [field]: value })) metadata: [...args.settings.metadata, { key: "", value: "" }],
} },
});
}}
onRemoveMetadata={(index) => {
args.onRemoveMetadata(index);
updateArgs({
settings: {
...args.settings,
metadata: args.settings.metadata.filter((_, i) => i !== index),
},
});
}}
onMetadataChange={(index, key, value) => {
args.onMetadataChange(index, key, value);
updateArgs({
settings: {
...args.settings,
metadata: args.settings.metadata.map((m, i) =>
i === index ? { key, value } : m,
),
},
});
}}
onTimeoutChange={(field, value) => {
args.onTimeoutChange(field, value);
updateArgs({
settings: { ...args.settings, [field]: value },
});
}}
onOAuthChange={(field, value) => { onOAuthChange={(field, value) => {
args.onOAuthChange(field, value);
const fieldMap: Record<string, string> = { const fieldMap: Record<string, string> = {
clientId: "oauthClientId", clientId: "oauthClientId",
clientSecret: "oauthClientSecret", clientSecret: "oauthClientSecret",
scopes: "oauthScopes", scopes: "oauthScopes",
}; };
setSettings((s) => ({ ...s, [fieldMap[field] ?? field]: value })); updateArgs({
settings: {
...args.settings,
[fieldMap[field] ?? field]: value,
},
});
}} }}
/> />
); );
@ -90,62 +116,72 @@ function InteractiveForm({
const meta: Meta<typeof ServerSettingsForm> = { const meta: Meta<typeof ServerSettingsForm> = {
title: "Groups/ServerSettingsForm", title: "Groups/ServerSettingsForm",
component: ServerSettingsForm, component: ServerSettingsForm,
render: InteractiveRender,
args: {
expandedSections: ["connectionMode"],
onExpandedSectionsChange: fn(),
onConnectionModeChange: fn(),
onAddHeader: fn(),
onRemoveHeader: fn(),
onHeaderChange: fn(),
onAddMetadata: fn(),
onRemoveMetadata: fn(),
onMetadataChange: fn(),
onTimeoutChange: fn(),
onOAuthChange: fn(),
},
}; };
export default meta; export default meta;
type Story = StoryObj<typeof ServerSettingsForm>; type Story = StoryObj<typeof ServerSettingsForm>;
export const DefaultSettings: Story = { export const DefaultSettings: Story = {
render: () => <InteractiveForm startSettings={defaultSettings} />, args: {
settings: defaultSettings,
},
}; };
export const WithHeaders: Story = { export const WithHeaders: Story = {
render: () => ( args: {
<InteractiveForm settings: {
startSettings={{ ...defaultSettings,
...defaultSettings, headers: [
headers: [ { key: "Authorization", value: "Bearer token-abc-123" },
{ key: "Authorization", value: "Bearer token-abc-123" }, { key: "X-Custom-Header", value: "custom-value" },
{ key: "X-Custom-Header", value: "custom-value" }, ],
], },
}} },
/>
),
}; };
export const WithOAuth: Story = { export const WithOAuth: Story = {
render: () => ( args: {
<InteractiveForm settings: {
startSettings={{ ...defaultSettings,
...defaultSettings, connectionMode: "direct",
connectionMode: "direct", oauthClientId: "my-client-id",
oauthClientId: "my-client-id", oauthClientSecret: "super-secret-value",
oauthClientSecret: "super-secret-value", oauthScopes: "read write admin",
oauthScopes: "read write admin", },
}} },
/>
),
}; };
export const AllConfigured: Story = { export const AllConfigured: Story = {
render: () => ( args: {
<InteractiveForm settings: {
startSettings={{ connectionMode: "proxy",
connectionMode: "proxy", headers: [
headers: [ { key: "Authorization", value: "Bearer token-abc-123" },
{ key: "Authorization", value: "Bearer token-abc-123" }, { key: "X-Request-Id", value: "req-456" },
{ key: "X-Request-Id", value: "req-456" }, ],
], metadata: [
metadata: [ { key: "userId", value: "user-789" },
{ key: "userId", value: "user-789" }, { key: "sessionId", value: "session-012" },
{ key: "sessionId", value: "session-012" }, ],
], connectionTimeout: 15000,
connectionTimeout: 15000, requestTimeout: 45000,
requestTimeout: 45000, oauthClientId: "my-client-id",
oauthClientId: "my-client-id", oauthClientSecret: "super-secret-value",
oauthClientSecret: "super-secret-value", oauthScopes: "read write",
oauthScopes: "read write", },
}} },
/>
),
}; };