Add ServerSettingsModal group, revert form to controlled component
Revert ServerSettingsForm to a controlled component with expandedSections/onExpandedSectionsChange props. The form is a dumb component — accordion state belongs to its container. Create ServerSettingsModal group that manages: - Accordion expand/collapse state with ListToggle expand-all button - Add/remove/edit header pairs - Add/remove/edit metadata pairs - Connection mode, timeout, and OAuth changes - All changes flow through a single onSettingsChange callback Update ServerSettingsForm stories with useState render functions so accordion sections are interactive. Update UnconnectedView WithSettingsModal story to use the new ServerSettingsModal component. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
adb909daf6
commit
a6fc900271
2 changed files with 307 additions and 100 deletions
|
|
@ -1,7 +1,11 @@
|
|||
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 } from "./ServerSettingsForm";
|
||||
import {
|
||||
ServerSettingsForm,
|
||||
type ServerSettingsSection,
|
||||
} from "./ServerSettingsForm";
|
||||
|
||||
const defaultSettings: InspectorServerSettings = {
|
||||
connectionMode: "proxy",
|
||||
|
|
@ -11,72 +15,97 @@ 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,
|
||||
initialSections = ["connectionMode"],
|
||||
}: {
|
||||
settings: InspectorServerSettings;
|
||||
initialSections?: ServerSettingsSection[];
|
||||
}) {
|
||||
const [expandedSections, setExpandedSections] =
|
||||
useState<ServerSettingsSection[]>(initialSections);
|
||||
|
||||
return (
|
||||
<ServerSettingsForm
|
||||
settings={settings}
|
||||
expandedSections={expandedSections}
|
||||
onExpandedSectionsChange={setExpandedSections}
|
||||
{...defaultHandlers}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
const meta: Meta<typeof ServerSettingsForm> = {
|
||||
title: "Groups/ServerSettingsForm",
|
||||
component: ServerSettingsForm,
|
||||
args: {
|
||||
onConnectionModeChange: fn(),
|
||||
onAddHeader: fn(),
|
||||
onRemoveHeader: fn(),
|
||||
onHeaderChange: fn(),
|
||||
onAddMetadata: fn(),
|
||||
onRemoveMetadata: fn(),
|
||||
onMetadataChange: fn(),
|
||||
onTimeoutChange: fn(),
|
||||
onOAuthChange: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ServerSettingsForm>;
|
||||
|
||||
export const DefaultSettings: Story = {
|
||||
args: {
|
||||
settings: defaultSettings,
|
||||
},
|
||||
render: () => <InteractiveForm settings={defaultSettings} />,
|
||||
};
|
||||
|
||||
export const WithHeaders: Story = {
|
||||
args: {
|
||||
settings: {
|
||||
...defaultSettings,
|
||||
headers: [
|
||||
{ key: "Authorization", value: "Bearer token-abc-123" },
|
||||
{ key: "X-Custom-Header", value: "custom-value" },
|
||||
],
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<InteractiveForm
|
||||
settings={{
|
||||
...defaultSettings,
|
||||
headers: [
|
||||
{ key: "Authorization", value: "Bearer token-abc-123" },
|
||||
{ key: "X-Custom-Header", value: "custom-value" },
|
||||
],
|
||||
}}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
export const WithOAuth: Story = {
|
||||
args: {
|
||||
settings: {
|
||||
...defaultSettings,
|
||||
connectionMode: "direct",
|
||||
oauthClientId: "my-client-id",
|
||||
oauthClientSecret: "super-secret-value",
|
||||
oauthScopes: "read write admin",
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<InteractiveForm
|
||||
settings={{
|
||||
...defaultSettings,
|
||||
connectionMode: "direct",
|
||||
oauthClientId: "my-client-id",
|
||||
oauthClientSecret: "super-secret-value",
|
||||
oauthScopes: "read write admin",
|
||||
}}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
||||
export const AllConfigured: Story = {
|
||||
args: {
|
||||
settings: {
|
||||
connectionMode: "proxy",
|
||||
headers: [
|
||||
{ key: "Authorization", value: "Bearer token-abc-123" },
|
||||
{ key: "X-Request-Id", value: "req-456" },
|
||||
],
|
||||
metadata: [
|
||||
{ key: "userId", value: "user-789" },
|
||||
{ key: "sessionId", value: "session-012" },
|
||||
],
|
||||
connectionTimeout: 15000,
|
||||
requestTimeout: 45000,
|
||||
oauthClientId: "my-client-id",
|
||||
oauthClientSecret: "super-secret-value",
|
||||
oauthScopes: "read write",
|
||||
},
|
||||
},
|
||||
render: () => (
|
||||
<InteractiveForm
|
||||
settings={{
|
||||
connectionMode: "proxy",
|
||||
headers: [
|
||||
{ key: "Authorization", value: "Bearer token-abc-123" },
|
||||
{ key: "X-Request-Id", value: "req-456" },
|
||||
],
|
||||
metadata: [
|
||||
{ key: "userId", value: "user-789" },
|
||||
{ key: "sessionId", value: "session-012" },
|
||||
],
|
||||
connectionTimeout: 15000,
|
||||
requestTimeout: 45000,
|
||||
oauthClientId: "my-client-id",
|
||||
oauthClientSecret: "super-secret-value",
|
||||
oauthScopes: "read write",
|
||||
}}
|
||||
/>
|
||||
),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { useState } from "react";
|
||||
import {
|
||||
Accordion,
|
||||
ActionIcon,
|
||||
|
|
@ -21,7 +20,8 @@ export type ServerSettingsSection =
|
|||
|
||||
export interface ServerSettingsFormProps {
|
||||
settings: InspectorServerSettings;
|
||||
defaultExpandedSections?: ServerSettingsSection[];
|
||||
expandedSections: ServerSettingsSection[];
|
||||
onExpandedSectionsChange: (sections: ServerSettingsSection[]) => void;
|
||||
onConnectionModeChange: (mode: "proxy" | "direct") => void;
|
||||
onAddHeader: () => void;
|
||||
onRemoveHeader: (index: number) => void;
|
||||
|
|
@ -95,7 +95,8 @@ function KeyValueRows({
|
|||
|
||||
export function ServerSettingsForm({
|
||||
settings,
|
||||
defaultExpandedSections = ["connectionMode"],
|
||||
expandedSections,
|
||||
onExpandedSectionsChange,
|
||||
onConnectionModeChange,
|
||||
onAddHeader,
|
||||
onRemoveHeader,
|
||||
|
|
@ -106,10 +107,6 @@ export function ServerSettingsForm({
|
|||
onTimeoutChange,
|
||||
onOAuthChange,
|
||||
}: ServerSettingsFormProps) {
|
||||
const [expandedSections, setExpandedSections] = useState<
|
||||
ServerSettingsSection[]
|
||||
>(defaultExpandedSections);
|
||||
|
||||
const handleTimeoutChange = (field: string) => (value: number | string) => {
|
||||
const numValue =
|
||||
typeof value === "string" ? parseInt(value, 10) || 0 : value;
|
||||
|
|
@ -121,7 +118,7 @@ export function ServerSettingsForm({
|
|||
multiple
|
||||
value={expandedSections}
|
||||
onChange={(value) =>
|
||||
setExpandedSections(value as ServerSettingsSection[])
|
||||
onExpandedSectionsChange(value as ServerSettingsSection[])
|
||||
}
|
||||
variant="separated"
|
||||
>
|
||||
|
|
|
|||
Loading…
Reference in a new issue