Skip to content

Commit 59c5693

Browse files
authored
feat: update provider row after saving credentials (#8914)
1 parent 0a49318 commit 59c5693

2 files changed

Lines changed: 58 additions & 8 deletions

File tree

ui/goose2/src/features/settings/ui/ModelProviderRow.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ export function ModelProviderRow({
7878
const [setupOutput, setSetupOutput] = useState<SetupOutputLine[]>([]);
7979
const [setupError, setSetupError] = useState("");
8080
const [showSavedState, setShowSavedState] = useState(false);
81-
const [preserveSetupLayout, setPreserveSetupLayout] = useState(false);
8281
const setupLineCounter = useRef(0);
8382
const hasLoadedConfig = useRef(false);
8483
const shouldRestorePanelFocus = useRef(false);
@@ -170,7 +169,6 @@ export function ModelProviderRow({
170169
setEditingKey(null);
171170
setError("");
172171
setShowSavedState(false);
173-
setPreserveSetupLayout(false);
174172

175173
const unlisten = await onModelSetupOutput(provider.id, appendSetupOutput);
176174

@@ -195,7 +193,6 @@ export function ModelProviderRow({
195193
setExpanded((current) => {
196194
if (current) {
197195
setShowSavedState(false);
198-
setPreserveSetupLayout(false);
199196
}
200197
return !current;
201198
});
@@ -295,8 +292,7 @@ export function ModelProviderRow({
295292
})),
296293
);
297294
await loadConfig();
298-
setShowSavedState(true);
299-
setPreserveSetupLayout(true);
295+
setShowSavedState(false);
300296
} catch (nextError) {
301297
setError(
302298
nextError instanceof Error ? nextError.message : "Failed to save",
@@ -312,7 +308,6 @@ export function ModelProviderRow({
312308
setEditingKey(null);
313309
setError("");
314310
setShowSavedState(false);
315-
setPreserveSetupLayout(false);
316311
} catch (nextError) {
317312
setError(
318313
nextError instanceof Error ? nextError.message : "Failed to remove",
@@ -403,7 +398,7 @@ export function ModelProviderRow({
403398
);
404399
}
405400

406-
if (hasFields && isConnected && !preserveSetupLayout) {
401+
if (hasFields && isConnected) {
407402
return (
408403
<ConnectedFieldsPanel
409404
panelRef={panelRef}

ui/goose2/src/features/settings/ui/__tests__/ModelProviderRow.test.tsx

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { render, screen, waitFor } from "@testing-library/react";
22
import userEvent from "@testing-library/user-event";
3-
import type { ComponentType } from "react";
3+
import { useState, type ComponentType } from "react";
44
import { beforeEach, describe, expect, it, vi } from "vitest";
55
import { getModelProviders } from "@/features/providers/providerCatalog";
66
import { ModelProviderRow } from "../ModelProviderRow";
@@ -110,4 +110,59 @@ describe("ModelProviderRow", () => {
110110

111111
expect(screen.getByText(/model refresh failed/i)).toBeInTheDocument();
112112
});
113+
114+
it("switches from setup save to connected controls after first configuration", async () => {
115+
const user = userEvent.setup();
116+
let saved = false;
117+
118+
function SetupSaveRow() {
119+
const [status, setStatus] = useState<"connected" | "not_configured">(
120+
"not_configured",
121+
);
122+
123+
return (
124+
<ModelProviderRow
125+
provider={modelProvider("google", status)}
126+
onGetConfig={async () =>
127+
saved
128+
? [
129+
{
130+
key: "GOOGLE_API_KEY",
131+
value: null,
132+
isSet: true,
133+
isSecret: true,
134+
required: true,
135+
},
136+
]
137+
: []
138+
}
139+
onSaveFields={async () => {
140+
saved = true;
141+
setStatus("connected");
142+
}}
143+
onRemoveConfig={onRemoveConfig}
144+
onCompleteNativeSetup={onCompleteNativeSetup}
145+
/>
146+
);
147+
}
148+
149+
render(<SetupSaveRow />);
150+
151+
await user.click(screen.getByRole("button", { name: /google gemini/i }));
152+
await user.type(
153+
await screen.findByPlaceholderText(/paste your api key/i),
154+
"google-token",
155+
);
156+
await user.click(screen.getByRole("button", { name: /^save$/i }));
157+
158+
await waitFor(() =>
159+
expect(
160+
screen.getByRole("button", { name: /disconnect/i }),
161+
).toBeInTheDocument(),
162+
);
163+
expect(screen.getByRole("button", { name: /edit/i })).toBeInTheDocument();
164+
expect(
165+
screen.queryByRole("button", { name: /saved/i }),
166+
).not.toBeInTheDocument();
167+
});
113168
});

0 commit comments

Comments
 (0)