Collapse ServerSettingsForm into accordion and add subtle ListToggle variant

Wraps every section of ServerSettingsForm (Connection Mode, Custom
Headers, Request Metadata, Timeouts, OAuth Settings) in a controlled
Mantine Accordion. The accordion is driven by new required
`expandedSections` / `onExpandedSectionsChange` props so the parent
owns expand/collapse state — consistent with the dumb-component
principle.

Adds a `variant?: "default" | "subtle"` prop to ListToggle. In subtle
mode it renders as an ActionIcon with `variant="subtle"`, `color="gray"`,
and a 16px icon so it visually matches Mantine's CloseButton in modal
headers (transparent until hover).

Adds a WithSettingsModal story on UnconnectedView that presents the
form inside a Mantine Modal with a custom three-column header: subtle
ListToggle on the left (expands/collapses all sections at once),
"Server Settings" Title centered, CloseButton on the right.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-04-05 09:36:14 -04:00
commit 8483b98c37

View file

@ -1,7 +1,22 @@
import { useState } from "react";
import type { Meta, StoryObj } from "@storybook/react-vite";
import { Box, CloseButton, Group, Modal, Title } from "@mantine/core";
import { fn } from "storybook/test";
import { UnconnectedView } from "./UnconnectedView.js";
import { ServerListScreen } from "../../screens/ServerListScreen/ServerListScreen";
import {
ServerSettingsForm,
type ServerSettingsSection,
} from "../../groups/ServerSettingsForm/ServerSettingsForm";
const ALL_SETTINGS_SECTIONS: ServerSettingsSection[] = [
"connectionMode",
"headers",
"metadata",
"timeouts",
"oauth",
];
import { ListToggle } from "../../elements/ListToggle/ListToggle";
import type { ServerCardProps } from "../../groups/ServerCard/ServerCard";
const meta: Meta<typeof UnconnectedView> = {
@ -93,6 +108,88 @@ export const WithServers: Story = {
},
};
function SettingsModalStory() {
const [expandedSections, setExpandedSections] = useState<
ServerSettingsSection[]
>(["connectionMode"]);
const allExpanded = expandedSections.length === ALL_SETTINGS_SECTIONS.length;
return (
<UnconnectedView onToggleTheme={fn()}>
<ServerListScreen
servers={[
{
name: "everything-server",
version: "1.0.0",
transport: "stdio",
connectionMode: "Via Proxy",
command: "npx -y @modelcontextprotocol/server-everything",
status: "disconnected",
canTestClientFeatures: false,
...makeServerCallbacks(),
},
]}
onAddManually={fn()}
onImportConfig={fn()}
onImportServerJson={fn()}
/>
<Modal
opened
onClose={fn()}
withCloseButton={false}
title={
<Group justify="space-between" wrap="nowrap" w="100%">
<ListToggle
compact={allExpanded}
variant="subtle"
onToggle={() =>
setExpandedSections(allExpanded ? [] : ALL_SETTINGS_SECTIONS)
}
/>
<Title order={4} ta="center" style={{ flex: 1 }}>
Server Settings
</Title>
<Box>
<CloseButton onClick={fn()} />
</Box>
</Group>
}
size="lg"
centered
styles={{ title: { flex: 1 } }}
>
<ServerSettingsForm
connectionMode="proxy"
headers={[
{ key: "Authorization", value: "Bearer token-abc-123" },
{ key: "X-Request-Id", value: "req-456" },
]}
metadata={[{ key: "userId", value: "user-789" }]}
connectionTimeout={30000}
requestTimeout={60000}
oauthClientId="my-client-id"
oauthClientSecret="super-secret-value"
oauthScopes="read write"
expandedSections={expandedSections}
onExpandedSectionsChange={setExpandedSections}
onConnectionModeChange={fn()}
onAddHeader={fn()}
onRemoveHeader={fn()}
onHeaderChange={fn()}
onAddMetadata={fn()}
onRemoveMetadata={fn()}
onMetadataChange={fn()}
onTimeoutChange={fn()}
onOAuthChange={fn()}
/>
</Modal>
</UnconnectedView>
);
}
export const WithSettingsModal: Story = {
render: () => <SettingsModalStory />,
};
export const ManyServers: Story = {
args: {
children: (