Internalize accordion state in ServerSettingsForm

Move expandedSections/onExpandedSectionsChange from props to internal
useState. The accordion open/close is local display logic, not parent
state. Add optional defaultExpandedSections prop for initial state.

Simplify UnconnectedView WithSettingsModal story — no longer needs
external state management for accordion sections.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-04-21 17:00:25 -04:00
commit adb909daf6
3 changed files with 11 additions and 38 deletions

View file

@ -15,8 +15,6 @@ const meta: Meta<typeof ServerSettingsForm> = {
title: "Groups/ServerSettingsForm", title: "Groups/ServerSettingsForm",
component: ServerSettingsForm, component: ServerSettingsForm,
args: { args: {
expandedSections: ["connectionMode"],
onExpandedSectionsChange: fn(),
onConnectionModeChange: fn(), onConnectionModeChange: fn(),
onAddHeader: fn(), onAddHeader: fn(),
onRemoveHeader: fn(), onRemoveHeader: fn(),

View file

@ -1,3 +1,4 @@
import { useState } from "react";
import { import {
Accordion, Accordion,
ActionIcon, ActionIcon,
@ -20,8 +21,7 @@ export type ServerSettingsSection =
export interface ServerSettingsFormProps { export interface ServerSettingsFormProps {
settings: InspectorServerSettings; settings: InspectorServerSettings;
expandedSections: ServerSettingsSection[]; defaultExpandedSections?: ServerSettingsSection[];
onExpandedSectionsChange: (sections: ServerSettingsSection[]) => void;
onConnectionModeChange: (mode: "proxy" | "direct") => void; onConnectionModeChange: (mode: "proxy" | "direct") => void;
onAddHeader: () => void; onAddHeader: () => void;
onRemoveHeader: (index: number) => void; onRemoveHeader: (index: number) => void;
@ -95,8 +95,7 @@ function KeyValueRows({
export function ServerSettingsForm({ export function ServerSettingsForm({
settings, settings,
expandedSections, defaultExpandedSections = ["connectionMode"],
onExpandedSectionsChange,
onConnectionModeChange, onConnectionModeChange,
onAddHeader, onAddHeader,
onRemoveHeader, onRemoveHeader,
@ -107,6 +106,10 @@ export function ServerSettingsForm({
onTimeoutChange, onTimeoutChange,
onOAuthChange, onOAuthChange,
}: ServerSettingsFormProps) { }: ServerSettingsFormProps) {
const [expandedSections, setExpandedSections] = useState<
ServerSettingsSection[]
>(defaultExpandedSections);
const handleTimeoutChange = (field: string) => (value: number | string) => { const handleTimeoutChange = (field: string) => (value: number | string) => {
const numValue = const numValue =
typeof value === "string" ? parseInt(value, 10) || 0 : value; typeof value === "string" ? parseInt(value, 10) || 0 : value;
@ -118,7 +121,7 @@ export function ServerSettingsForm({
multiple multiple
value={expandedSections} value={expandedSections}
onChange={(value) => onChange={(value) =>
onExpandedSectionsChange(value as ServerSettingsSection[]) setExpandedSections(value as ServerSettingsSection[])
} }
variant="separated" variant="separated"
> >

View file

@ -1,22 +1,9 @@
import { useState } from "react";
import type { Meta, StoryObj } from "@storybook/react-vite"; import type { Meta, StoryObj } from "@storybook/react-vite";
import { Box, CloseButton, Group, Modal, Title } from "@mantine/core"; import { CloseButton, Group, Modal, Title } from "@mantine/core";
import { fn } from "storybook/test"; import { fn } from "storybook/test";
import { UnconnectedView } from "./UnconnectedView.js"; import { UnconnectedView } from "./UnconnectedView.js";
import { ServerListScreen } from "../../screens/ServerListScreen/ServerListScreen"; import { ServerListScreen } from "../../screens/ServerListScreen/ServerListScreen";
import { import { ServerSettingsForm } from "../../groups/ServerSettingsForm/ServerSettingsForm";
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"; import type { ServerCardProps } from "../../groups/ServerCard/ServerCard";
const meta: Meta<typeof UnconnectedView> = { const meta: Meta<typeof UnconnectedView> = {
@ -125,10 +112,6 @@ export const WithServers: Story = {
}; };
function SettingsModalStory() { function SettingsModalStory() {
const [expandedSections, setExpandedSections] = useState<
ServerSettingsSection[]
>(["connectionMode"]);
const allExpanded = expandedSections.length === ALL_SETTINGS_SECTIONS.length;
return ( return (
<UnconnectedView onToggleTheme={fn()}> <UnconnectedView onToggleTheme={fn()}>
<ServerListScreen <ServerListScreen
@ -149,19 +132,10 @@ function SettingsModalStory() {
withCloseButton={false} withCloseButton={false}
title={ title={
<Group justify="space-between" wrap="nowrap" w="100%"> <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 }}> <Title order={4} ta="center" style={{ flex: 1 }}>
Server Settings Server Settings
</Title> </Title>
<Box> <CloseButton onClick={fn()} />
<CloseButton onClick={fn()} />
</Box>
</Group> </Group>
} }
size="lg" size="lg"
@ -182,8 +156,6 @@ function SettingsModalStory() {
oauthClientSecret: "super-secret-value", oauthClientSecret: "super-secret-value",
oauthScopes: "read write", oauthScopes: "read write",
}} }}
expandedSections={expandedSections}
onExpandedSectionsChange={setExpandedSections}
onConnectionModeChange={fn()} onConnectionModeChange={fn()}
onAddHeader={fn()} onAddHeader={fn()}
onRemoveHeader={fn()} onRemoveHeader={fn()}