diff --git a/webview-ui/src/components/ui/__tests__/select-dropdown.test.tsx b/webview-ui/src/components/ui/__tests__/select-dropdown.test.tsx
index 5d65eaae98..328933852f 100644
--- a/webview-ui/src/components/ui/__tests__/select-dropdown.test.tsx
+++ b/webview-ui/src/components/ui/__tests__/select-dropdown.test.tsx
@@ -115,6 +115,21 @@ describe("SelectDropdown", () => {
expect(trigger.classList.toString()).toContain("custom-trigger-class")
})
+ it("ensures open state is controlled via props", () => {
+ // Test that the component accepts and uses the open state controlled prop
+ render()
+
+ // The component should render the dropdown root with correct props
+ const dropdown = screen.getByTestId("dropdown-root")
+ expect(dropdown).toBeInTheDocument()
+
+ // Verify trigger and content are rendered
+ const trigger = screen.getByTestId("dropdown-trigger")
+ const content = screen.getByTestId("dropdown-content")
+ expect(trigger).toBeInTheDocument()
+ expect(content).toBeInTheDocument()
+ })
+
// Tests for the new functionality
describe("Option types", () => {
it("renders separator options correctly", () => {
@@ -131,20 +146,6 @@ describe("SelectDropdown", () => {
expect(separators.length).toBe(1)
})
- it("renders string separator (backward compatibility) correctly", () => {
- const optionsWithStringSeparator = [
- { value: "option1", label: "Option 1" },
- { value: "sep-1", label: "────", disabled: true },
- { value: "option2", label: "Option 2" },
- ]
-
- render()
-
- // Check for separator
- const separators = screen.getAllByTestId("dropdown-separator")
- expect(separators.length).toBe(1)
- })
-
it("renders shortcut options correctly", () => {
const shortcutText = "Ctrl+K"
const optionsWithShortcut = [
diff --git a/webview-ui/src/components/ui/select-dropdown.tsx b/webview-ui/src/components/ui/select-dropdown.tsx
index b134894d25..775066732d 100644
--- a/webview-ui/src/components/ui/select-dropdown.tsx
+++ b/webview-ui/src/components/ui/select-dropdown.tsx
@@ -7,6 +7,7 @@ import {
DropdownMenuSeparator,
} from "./dropdown-menu"
import { cn } from "@/lib/utils"
+import { useEffect, useState } from "react"
// Constants for option types
export enum DropdownOptionType {
@@ -57,6 +58,19 @@ export const SelectDropdown = React.forwardRef {
+ // Track open state
+ const [open, setOpen] = React.useState(false)
+ const [portalContainer, setPortalContainer] = useState()
+
+ useEffect(() => {
+ // The dropdown menu uses a portal from @shadcn/ui which by default renders
+ // at the document root. This causes the menu to remain visible even when
+ // the parent ChatView component is hidden (during settings/history view).
+ // By moving the portal inside ChatView, the menu will properly hide when
+ // its parent is hidden.
+ setPortalContainer(document.getElementById("chat-view-portal") || undefined)
+ }, [])
+
// Find the selected option label
const selectedOption = options.find((option) => option.value === value)
const displayText = selectedOption?.label || placeholder || ""
@@ -69,13 +83,15 @@ export const SelectDropdown = React.forwardRef
+
setOpen(false)}
+ onInteractOutside={() => setOpen(false)}
+ container={portalContainer}
className={cn(
"bg-vscode-dropdown-background text-vscode-dropdown-foreground border border-vscode-dropdown-border z-50",
contentClassName,
)}>
{options.map((option, index) => {
// Handle separator type
- if (option.type === DropdownOptionType.SEPARATOR || option.label.includes("────")) {
+ if (option.type === DropdownOptionType.SEPARATOR) {
return
}