From 1c29772d8f9433fbe96b05b9d3d4f1cd32053563 Mon Sep 17 00:00:00 2001 From: Daniel Riccio Date: Thu, 10 Jul 2025 13:53:12 -0500 Subject: [PATCH] fix: prevent auto-approve menu overflow with scrollable container - Add max-h-[400px] and overflow-y-auto Tailwind classes to expanded content - Ensures menu remains usable when all 10 toggles are displayed - Add tests to verify overflow behavior --- .../src/__tests__/AutoApproveMenu.spec.tsx | 133 ++++++++++++++++++ .../src/components/chat/AutoApproveMenu.tsx | 2 +- 2 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 webview-ui/src/__tests__/AutoApproveMenu.spec.tsx diff --git a/webview-ui/src/__tests__/AutoApproveMenu.spec.tsx b/webview-ui/src/__tests__/AutoApproveMenu.spec.tsx new file mode 100644 index 00000000000..a628bf674d2 --- /dev/null +++ b/webview-ui/src/__tests__/AutoApproveMenu.spec.tsx @@ -0,0 +1,133 @@ +import { render, screen, fireEvent } from "@/utils/test-utils" +import { describe, it, expect, vi, beforeEach } from "vitest" +import AutoApproveMenu from "../components/chat/AutoApproveMenu" + +// Mock vscode API +vi.mock("@src/utils/vscode", () => ({ + vscode: { + postMessage: vi.fn(), + }, +})) + +// Mock window.postMessage +const mockPostMessage = vi.fn() +window.postMessage = mockPostMessage + +// Mock useExtensionState +const mockUseExtensionState = vi.fn() + +vi.mock("@src/context/ExtensionStateContext", () => ({ + useExtensionState: () => mockUseExtensionState(), + ExtensionStateContextProvider: ({ children }: { children: React.ReactNode }) => <>{children}, +})) + +// Mock translation hook +vi.mock("@src/i18n/TranslationContext", () => ({ + useAppTranslation: () => ({ + t: (key: string) => key, + }), +})) + +const createMockExtensionState = (overrides = {}) => ({ + autoApprovalEnabled: false, + setAutoApprovalEnabled: vi.fn(), + alwaysAllowReadOnly: false, + alwaysAllowWrite: false, + alwaysAllowExecute: false, + alwaysAllowBrowser: false, + alwaysAllowMcp: false, + alwaysAllowModeSwitch: false, + alwaysAllowSubtasks: false, + alwaysApproveResubmit: false, + alwaysAllowFollowupQuestions: false, + alwaysAllowUpdateTodoList: false, + allowedMaxRequests: undefined, + setAlwaysAllowReadOnly: vi.fn(), + setAlwaysAllowWrite: vi.fn(), + setAlwaysAllowExecute: vi.fn(), + setAlwaysAllowBrowser: vi.fn(), + setAlwaysAllowMcp: vi.fn(), + setAlwaysAllowModeSwitch: vi.fn(), + setAlwaysAllowSubtasks: vi.fn(), + setAlwaysApproveResubmit: vi.fn(), + setAlwaysAllowFollowupQuestions: vi.fn(), + setAlwaysAllowUpdateTodoList: vi.fn(), + setAllowedMaxRequests: vi.fn(), + ...overrides, +}) + +describe("AutoApproveMenu", () => { + beforeEach(() => { + vi.clearAllMocks() + mockUseExtensionState.mockReturnValue(createMockExtensionState()) + }) + + it("renders without crashing", () => { + render() + expect(screen.getByText("chat:autoApprove.title")).toBeInTheDocument() + }) + + it("expands when clicked", () => { + render() + + // Initially, the expanded content should not be visible + const expandedContainer = document.querySelector(".flex.flex-col.gap-2.max-h-\\[400px\\].overflow-y-auto") + expect(expandedContainer).not.toBeInTheDocument() + + // Click to expand + const titleArea = screen.getByText("chat:autoApprove.title").parentElement?.parentElement + fireEvent.click(titleArea!) + + // Now the expanded content should be visible + const expandedContent = document.querySelector(".flex.flex-col.gap-2.max-h-\\[400px\\].overflow-y-auto") + expect(expandedContent).toBeInTheDocument() + }) + + it("expanded content has max-height and overflow-y auto to prevent overflow", () => { + render() + + // Click to expand + const titleArea = screen.getByText("chat:autoApprove.title").parentElement?.parentElement + fireEvent.click(titleArea!) + + // Find the expanded content container + const expandedContent = document.querySelector(".flex.flex-col.gap-2.max-h-\\[400px\\].overflow-y-auto") + + // Check that it exists and has the correct classes + expect(expandedContent).toBeInTheDocument() + expect(expandedContent).toHaveClass("max-h-[400px]", "overflow-y-auto") + }) + + it("collapses when clicked again", () => { + render() + + // Click to expand + const titleArea = screen.getByText("chat:autoApprove.title").parentElement?.parentElement + fireEvent.click(titleArea!) + + // Verify expanded + const expandedContent = document.querySelector(".flex.flex-col.gap-2.max-h-\\[400px\\].overflow-y-auto") + expect(expandedContent).toBeInTheDocument() + + // Click to collapse + fireEvent.click(titleArea!) + + // Verify collapsed + const collapsedContent = document.querySelector(".flex.flex-col.gap-2.max-h-\\[400px\\].overflow-y-auto") + expect(collapsedContent).not.toBeInTheDocument() + }) + + it("displays enabled actions list when toggles are enabled", () => { + mockUseExtensionState.mockReturnValue( + createMockExtensionState({ + alwaysAllowReadOnly: true, + alwaysAllowWrite: true, + }), + ) + + render() + + // Should show the enabled actions instead of "none" + expect(screen.queryByText("chat:autoApprove.none")).not.toBeInTheDocument() + }) +}) diff --git a/webview-ui/src/components/chat/AutoApproveMenu.tsx b/webview-ui/src/components/chat/AutoApproveMenu.tsx index ae363a7b63b..811968d4ec4 100644 --- a/webview-ui/src/components/chat/AutoApproveMenu.tsx +++ b/webview-ui/src/components/chat/AutoApproveMenu.tsx @@ -201,7 +201,7 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { {isExpanded && ( -
+