Browse Source

[Condense] Move condense button out of expanded task menu (#4093)

* [Condense] Move condense button out of expanded task menu

* tests

* fix test

* fix test again

* tailwind css
Canyon Robins 7 months ago
parent
commit
70e753ebb1

+ 1 - 8
webview-ui/src/components/chat/TaskActions.tsx

@@ -12,10 +12,9 @@ import { IconButton } from "./IconButton"
 interface TaskActionsProps {
 	item?: HistoryItem
 	buttonsDisabled: boolean
-	handleCondenseContext: (taskId: string) => void
 }
 
-export const TaskActions = ({ item, buttonsDisabled, handleCondenseContext }: TaskActionsProps) => {
+export const TaskActions = ({ item, buttonsDisabled }: TaskActionsProps) => {
 	const [deleteTaskId, setDeleteTaskId] = useState<string | null>(null)
 	const { t } = useTranslation()
 
@@ -29,12 +28,6 @@ export const TaskActions = ({ item, buttonsDisabled, handleCondenseContext }: Ta
 			/>
 			{!!item?.size && item.size > 0 && (
 				<>
-					<IconButton
-						iconClass="codicon-fold"
-						title={t("chat:task.condenseContext")}
-						disabled={buttonsDisabled}
-						onClick={() => handleCondenseContext(item.id)}
-					/>
 					<div className="flex items-center">
 						<IconButton
 							iconClass="codicon-trash"

+ 11 - 13
webview-ui/src/components/chat/TaskHeader.tsx

@@ -19,6 +19,7 @@ import Thumbnails from "../common/Thumbnails"
 import { TaskActions } from "./TaskActions"
 import { ContextWindowProgress } from "./ContextWindowProgress"
 import { Mention } from "./Mention"
+import { IconButton } from "./IconButton"
 
 export interface TaskHeaderProps {
 	task: ClineMessage
@@ -97,7 +98,7 @@ const TaskHeader = ({
 				</div>
 				{/* Collapsed state: Track context and cost if we have any */}
 				{!isTaskExpanded && contextWindow > 0 && (
-					<div className={`w-full flex flex-row gap-1 h-auto`}>
+					<div className={`w-full flex flex-row items-center gap-1 h-auto`}>
 						<ContextWindowProgress
 							contextWindow={contextWindow}
 							contextTokens={contextTokens || 0}
@@ -107,6 +108,13 @@ const TaskHeader = ({
 									: undefined
 							}
 						/>
+						<IconButton
+							iconClass="codicon-fold"
+							title={t("chat:task.condenseContext")}
+							disabled={buttonsDisabled}
+							onClick={() => currentTaskItem && handleCondenseContext(currentTaskItem.id)}
+							className="shrink-0 min-h-[20px] min-w-[20px] p-[2px]"
+						/>
 						{!!totalCost && <VSCodeBadge>${totalCost.toFixed(2)}</VSCodeBadge>}
 					</div>
 				)}
@@ -169,13 +177,7 @@ const TaskHeader = ({
 										</span>
 									)}
 								</div>
-								{!totalCost && (
-									<TaskActions
-										item={currentTaskItem}
-										buttonsDisabled={buttonsDisabled}
-										handleCondenseContext={handleCondenseContext}
-									/>
-								)}
+								{!totalCost && <TaskActions item={currentTaskItem} buttonsDisabled={buttonsDisabled} />}
 							</div>
 
 							{doesModelSupportPromptCache &&
@@ -204,11 +206,7 @@ const TaskHeader = ({
 										<span className="font-bold">{t("chat:task.apiCost")}</span>
 										<span>${totalCost?.toFixed(2)}</span>
 									</div>
-									<TaskActions
-										item={currentTaskItem}
-										buttonsDisabled={buttonsDisabled}
-										handleCondenseContext={handleCondenseContext}
-									/>
+									<TaskActions item={currentTaskItem} buttonsDisabled={buttonsDisabled} />
 								</div>
 							)}
 						</div>

+ 35 - 2
webview-ui/src/components/chat/__tests__/TaskHeader.test.tsx

@@ -1,13 +1,25 @@
 // npx jest src/components/chat/__tests__/TaskHeader.test.tsx
 
 import React from "react"
-import { render, screen } from "@testing-library/react"
+import { render, screen, fireEvent } from "@testing-library/react"
 import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
 
 import type { ProviderSettings } from "@roo-code/types"
 
 import TaskHeader, { TaskHeaderProps } from "../TaskHeader"
 
+// Mock i18n
+jest.mock("react-i18next", () => ({
+	useTranslation: () => ({
+		t: (key: string) => key, // Simple mock that returns the key
+	}),
+	// Mock initReactI18next to prevent initialization errors in tests
+	initReactI18next: {
+		type: "3rdParty",
+		init: jest.fn(),
+	},
+}))
+
 // Mock the vscode API
 jest.mock("@/utils/vscode", () => ({
 	vscode: {
@@ -28,7 +40,7 @@ jest.mock("@src/context/ExtensionStateContext", () => ({
 			apiKey: "test-api-key", // Add relevant fields
 			apiModelId: "claude-3-opus-20240229", // Add relevant fields
 		} as ProviderSettings, // Optional: Add type assertion if ProviderSettings is imported
-		currentTaskItem: null,
+		currentTaskItem: { id: "test-task-id" },
 	}),
 }))
 
@@ -79,4 +91,25 @@ describe("TaskHeader", () => {
 		renderTaskHeader({ totalCost: NaN })
 		expect(screen.queryByText(/\$/)).not.toBeInTheDocument()
 	})
+
+	it("should render the condense context button", () => {
+		renderTaskHeader()
+		expect(screen.getByTitle("chat:task.condenseContext")).toBeInTheDocument()
+	})
+
+	it("should call handleCondenseContext when condense context button is clicked", () => {
+		const handleCondenseContext = jest.fn()
+		renderTaskHeader({ handleCondenseContext })
+		const condenseButton = screen.getByTitle("chat:task.condenseContext")
+		fireEvent.click(condenseButton)
+		expect(handleCondenseContext).toHaveBeenCalledWith("test-task-id")
+	})
+
+	it("should disable the condense context button when buttonsDisabled is true", () => {
+		const handleCondenseContext = jest.fn()
+		renderTaskHeader({ buttonsDisabled: true, handleCondenseContext })
+		const condenseButton = screen.getByTitle("chat:task.condenseContext")
+		fireEvent.click(condenseButton)
+		expect(handleCondenseContext).not.toHaveBeenCalled()
+	})
 })