Skip to content

Commit 47b40dd

Browse files
NoahJenkinsclaude
andcommitted
fix: enable save button when profile is renamed
- Add setChangeDetected(true) when profile is renamed in SettingsView - Add comprehensive test coverage for profile rename change detection - Ensure unsaved changes dialog appears when navigating away after rename - Fix UI state consistency for profile management operations 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 63f47fb commit 47b40dd

File tree

2 files changed

+154
-3
lines changed

2 files changed

+154
-3
lines changed

webview-ui/src/components/settings/SettingsView.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,8 @@ const SettingsView = forwardRef<SettingsViewRef, SettingsViewProps>(({ onDone, t
609609
apiConfiguration,
610610
})
611611
prevApiConfigName.current = newName
612+
// Trigger change detection for profile rename
613+
setChangeDetected(true)
612614
}}
613615
onUpsertConfig={(configName: string) =>
614616
vscode.postMessage({

webview-ui/src/components/settings/__tests__/SettingsView.spec.tsx

Lines changed: 152 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,14 @@ vi.mock("@src/utils/vscode", () => ({ vscode: { postMessage: vi.fn() } }))
1010

1111
vi.mock("../ApiConfigManager", () => ({
1212
__esModule: true,
13-
default: ({ currentApiConfigName }: any) => (
13+
default: ({ currentApiConfigName, onRenameConfig }: any) => (
1414
<div data-testid="api-config-management">
1515
<span>Current config: {currentApiConfigName}</span>
16+
<button
17+
data-testid="rename-profile-button"
18+
onClick={() => onRenameConfig && onRenameConfig("oldProfile", "newProfile")}>
19+
Rename Profile
20+
</button>
1621
</div>
1722
),
1823
}))
@@ -135,8 +140,13 @@ vi.mock("@/components/ui", () => ({
135140
data-testid={dataTestId}
136141
/>
137142
),
138-
Button: ({ children, onClick, variant, className, "data-testid": dataTestId }: any) => (
139-
<button onClick={onClick} data-variant={variant} className={className} data-testid={dataTestId}>
143+
Button: ({ children, onClick, variant, className, disabled, "data-testid": dataTestId }: any) => (
144+
<button
145+
onClick={onClick}
146+
data-variant={variant}
147+
className={className}
148+
disabled={disabled}
149+
data-testid={dataTestId}>
140150
{children}
141151
</button>
142152
),
@@ -637,3 +647,142 @@ describe("SettingsView - Duplicate Commands", () => {
637647
)
638648
})
639649
})
650+
651+
describe("SettingsView - Profile Rename Change Detection", () => {
652+
beforeEach(() => {
653+
vi.clearAllMocks()
654+
})
655+
656+
it("enables save button when profile is renamed", () => {
657+
// Render settings view
658+
renderSettingsView()
659+
660+
// Rename a profile to trigger change detection
661+
const renameButton = screen.getByTestId("rename-profile-button")
662+
fireEvent.click(renameButton)
663+
664+
// Save button should now be enabled due to change detection
665+
const saveButton = screen.getByTestId("save-button")
666+
expect(saveButton).not.toBeDisabled()
667+
})
668+
669+
it("sends rename message to vscode when profile is renamed", () => {
670+
// Render settings view
671+
renderSettingsView()
672+
673+
// Rename a profile
674+
const renameButton = screen.getByTestId("rename-profile-button")
675+
fireEvent.click(renameButton)
676+
677+
// Verify that the rename message was sent to vscode
678+
expect(vscode.postMessage).toHaveBeenCalledWith(
679+
expect.objectContaining({
680+
type: "renameApiConfiguration",
681+
values: { oldName: "oldProfile", newName: "newProfile" },
682+
}),
683+
)
684+
})
685+
686+
it("saves changes when save button is clicked after profile rename", () => {
687+
// Render settings view
688+
renderSettingsView()
689+
690+
// Rename a profile
691+
const renameButton = screen.getByTestId("rename-profile-button")
692+
fireEvent.click(renameButton)
693+
694+
// Click save button
695+
const saveButton = screen.getByTestId("save-button")
696+
fireEvent.click(saveButton)
697+
698+
// Verify that all settings are saved, including the renamed configuration
699+
expect(vscode.postMessage).toHaveBeenCalledWith(
700+
expect.objectContaining({
701+
type: "upsertApiConfiguration",
702+
}),
703+
)
704+
})
705+
706+
it("disables save button after successful save following profile rename", () => {
707+
// Render settings view
708+
renderSettingsView()
709+
710+
// Rename a profile
711+
const renameButton = screen.getByTestId("rename-profile-button")
712+
fireEvent.click(renameButton)
713+
714+
// Verify save button is enabled
715+
const saveButton = screen.getByTestId("save-button")
716+
expect(saveButton).not.toBeDisabled()
717+
718+
// Click save button
719+
fireEvent.click(saveButton)
720+
721+
// Save button should be disabled again after save
722+
expect(saveButton).toBeDisabled()
723+
})
724+
725+
it("shows unsaved changes dialog when attempting to leave after profile rename", () => {
726+
// Render with activateTab helper
727+
const { onDone } = renderSettingsView()
728+
729+
// Rename a profile to create unsaved changes
730+
const renameButton = screen.getByTestId("rename-profile-button")
731+
fireEvent.click(renameButton)
732+
733+
// Try to leave by clicking Done
734+
const doneButton = screen.getByText("settings:common.done")
735+
fireEvent.click(doneButton)
736+
737+
// Should show unsaved changes dialog
738+
expect(screen.getByText("settings:unsavedChangesDialog.title")).toBeInTheDocument()
739+
expect(screen.getByText("settings:unsavedChangesDialog.description")).toBeInTheDocument()
740+
741+
// Verify onDone was not called yet
742+
expect(onDone).not.toHaveBeenCalled()
743+
})
744+
745+
it("allows leaving after discarding changes from profile rename", () => {
746+
// Render with activateTab helper
747+
const { onDone } = renderSettingsView()
748+
749+
// Rename a profile to create unsaved changes
750+
const renameButton = screen.getByTestId("rename-profile-button")
751+
fireEvent.click(renameButton)
752+
753+
// Try to leave by clicking Done
754+
const doneButton = screen.getByText("settings:common.done")
755+
fireEvent.click(doneButton)
756+
757+
// Click discard in the dialog
758+
const discardButton = screen.getByTestId("alert-dialog-action")
759+
fireEvent.click(discardButton)
760+
761+
// Verify onDone was called
762+
expect(onDone).toHaveBeenCalled()
763+
})
764+
765+
it("stays on page when canceling discard dialog after profile rename", () => {
766+
// Render with activateTab helper
767+
const { onDone } = renderSettingsView()
768+
769+
// Rename a profile to create unsaved changes
770+
const renameButton = screen.getByTestId("rename-profile-button")
771+
fireEvent.click(renameButton)
772+
773+
// Try to leave by clicking Done
774+
const doneButton = screen.getByText("settings:common.done")
775+
fireEvent.click(doneButton)
776+
777+
// Click cancel in the dialog
778+
const cancelButton = screen.getByTestId("alert-dialog-cancel")
779+
fireEvent.click(cancelButton)
780+
781+
// Verify onDone was not called
782+
expect(onDone).not.toHaveBeenCalled()
783+
784+
// Verify save button is still enabled (changes not discarded)
785+
const saveButton = screen.getByTestId("save-button")
786+
expect(saveButton).not.toBeDisabled()
787+
})
788+
})

0 commit comments

Comments
 (0)