mirror of
https://github.com/All-Hands-AI/OpenHands.git
synced 2026-04-29 03:00:45 -04:00
Compare commits
15 Commits
exp/ltl-an
...
openhands-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e5f482400 | ||
|
|
ac6101833b | ||
|
|
a07df54fe1 | ||
|
|
faa6b91530 | ||
|
|
74f72a4dc9 | ||
|
|
3df3a07a98 | ||
|
|
a53dd1773b | ||
|
|
b9a8b756ae | ||
|
|
ce2a7c4b1f | ||
|
|
b71623510c | ||
|
|
3ad6afbe08 | ||
|
|
f763ba827c | ||
|
|
aac04fe6ca | ||
|
|
8bd4ae2a77 | ||
|
|
ebce3c2fe4 |
35
frontend/__tests__/components/workspace-toggle.test.tsx
Normal file
35
frontend/__tests__/components/workspace-toggle.test.tsx
Normal file
@@ -0,0 +1,35 @@
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { ToggleWorkspaceIconButton } from "#/components/shared/buttons/toggle-workspace-icon-button";
|
||||
|
||||
describe("Workspace Toggle", () => {
|
||||
it("should render toggle button with correct icon and label", () => {
|
||||
const onClickMock = vi.fn();
|
||||
|
||||
// Test initial state (workspace visible)
|
||||
const { rerender } = render(
|
||||
<ToggleWorkspaceIconButton onClick={onClickMock} isHidden={false} />
|
||||
);
|
||||
|
||||
const button = screen.getByTestId("toggle");
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button).toHaveAttribute("aria-label", "Close workspace");
|
||||
|
||||
// Test hidden state
|
||||
rerender(
|
||||
<ToggleWorkspaceIconButton onClick={onClickMock} isHidden={true} />
|
||||
);
|
||||
expect(button).toHaveAttribute("aria-label", "Open workspace");
|
||||
});
|
||||
|
||||
it("should call onClick handler when clicked", () => {
|
||||
const onClickMock = vi.fn();
|
||||
render(
|
||||
<ToggleWorkspaceIconButton onClick={onClickMock} isHidden={false} />
|
||||
);
|
||||
|
||||
const button = screen.getByTestId("toggle");
|
||||
fireEvent.click(button);
|
||||
expect(onClickMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,46 @@
|
||||
import React from "react";
|
||||
import { render, screen } from "@testing-library/react";
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { ToggleWorkspaceIconButton } from "../toggle-workspace-icon-button";
|
||||
|
||||
describe("ToggleWorkspaceIconButton", () => {
|
||||
it("renders with correct dimensions and styling", () => {
|
||||
const mockOnClick = vi.fn();
|
||||
render(
|
||||
<ToggleWorkspaceIconButton onClick={mockOnClick} isHidden={false} />,
|
||||
);
|
||||
|
||||
const button = screen.getByTestId("toggle");
|
||||
expect(button).toBeInTheDocument();
|
||||
expect(button).toHaveClass("h-[100px] w-[20px]");
|
||||
expect(button).toHaveClass("bg-neutral-800");
|
||||
expect(button).toHaveClass("hover:bg-neutral-700");
|
||||
expect(button).toHaveClass("rounded-md");
|
||||
});
|
||||
|
||||
it("displays the correct icon based on isHidden prop", () => {
|
||||
const mockOnClick = vi.fn();
|
||||
|
||||
const { rerender } = render(
|
||||
<ToggleWorkspaceIconButton onClick={mockOnClick} isHidden={false} />,
|
||||
);
|
||||
expect(screen.getByLabelText("Close workspace")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("toggle")).toContainElement(
|
||||
screen.getByTestId("arrow-forward-icon"),
|
||||
);
|
||||
|
||||
rerender(<ToggleWorkspaceIconButton onClick={mockOnClick} isHidden />);
|
||||
expect(screen.getByLabelText("Open workspace")).toBeInTheDocument();
|
||||
expect(screen.getByTestId("toggle")).toContainElement(
|
||||
screen.getByTestId("arrow-back-icon"),
|
||||
);
|
||||
});
|
||||
|
||||
it("remains visible when workspace is collapsed", () => {
|
||||
const mockOnClick = vi.fn();
|
||||
render(<ToggleWorkspaceIconButton onClick={mockOnClick} isHidden />);
|
||||
|
||||
const button = screen.getByTestId("toggle");
|
||||
expect(button).toBeVisible();
|
||||
});
|
||||
});
|
||||
@@ -1,11 +1,12 @@
|
||||
import { Button } from "@nextui-org/react";
|
||||
import React, { MouseEventHandler, ReactElement } from "react";
|
||||
import React, { ReactElement } from "react";
|
||||
|
||||
export interface IconButtonProps {
|
||||
icon: ReactElement;
|
||||
onClick: MouseEventHandler<HTMLButtonElement>;
|
||||
onClick: () => void;
|
||||
ariaLabel: string;
|
||||
testId?: string;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function IconButton({
|
||||
@@ -13,13 +14,14 @@ export function IconButton({
|
||||
onClick,
|
||||
ariaLabel,
|
||||
testId = "",
|
||||
className = "",
|
||||
}: IconButtonProps): React.ReactElement {
|
||||
return (
|
||||
<Button
|
||||
type="button"
|
||||
variant="flat"
|
||||
onClick={onClick}
|
||||
className="cursor-pointer text-[12px] bg-transparent aspect-square px-0 min-w-[20px] h-[20px]"
|
||||
onPress={onClick}
|
||||
className={`cursor-pointer text-[12px] bg-transparent aspect-square px-0 min-w-[12px] h-[20px] ${className}`}
|
||||
aria-label={ariaLabel}
|
||||
data-testid={testId}
|
||||
>
|
||||
|
||||
@@ -14,20 +14,24 @@ export function ToggleWorkspaceIconButton({
|
||||
<IconButton
|
||||
icon={
|
||||
isHidden ? (
|
||||
<IoIosArrowForward
|
||||
size={20}
|
||||
<IoIosArrowBack
|
||||
size={10}
|
||||
className="text-neutral-400 hover:text-neutral-100 transition"
|
||||
data-testid="arrow-back-icon"
|
||||
/>
|
||||
) : (
|
||||
<IoIosArrowBack
|
||||
size={20}
|
||||
<IoIosArrowForward
|
||||
size={10}
|
||||
className="text-neutral-400 hover:text-neutral-100 transition"
|
||||
data-testid="arrow-forward-icon"
|
||||
/>
|
||||
)
|
||||
}
|
||||
testId="toggle"
|
||||
ariaLabel={isHidden ? "Open workspace" : "Close workspace"}
|
||||
title={isHidden ? "Open workspace" : "Close workspace"}
|
||||
onClick={onClick}
|
||||
className="h-[80px] w-[8px] bg-neutral-800 hover:bg-neutral-700 rounded-md"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import { Container } from "#/components/layout/container";
|
||||
import Security from "#/components/shared/modals/security/security";
|
||||
import { CountBadge } from "#/components/layout/count-badge";
|
||||
import { TerminalStatusLabel } from "#/components/features/terminal/terminal-status-label";
|
||||
import { ToggleWorkspaceIconButton } from "#/components/shared/buttons/toggle-workspace-icon-button";
|
||||
|
||||
function AppContent() {
|
||||
const { gitHubToken } = useAuth();
|
||||
@@ -62,6 +63,12 @@ function AppContent() {
|
||||
dispatch(clearJupyter());
|
||||
});
|
||||
|
||||
const [isWorkspaceHidden, setIsWorkspaceHidden] = React.useState(false);
|
||||
|
||||
const toggleWorkspace = React.useCallback(() => {
|
||||
setIsWorkspaceHidden((prev) => !prev);
|
||||
}, []);
|
||||
|
||||
const {
|
||||
isOpen: securityModalIsOpen,
|
||||
onOpen: onSecurityModalOpen,
|
||||
@@ -71,15 +78,31 @@ function AppContent() {
|
||||
return (
|
||||
<WsClientProvider ghToken={gitHubToken} conversationId={conversationId}>
|
||||
<EventHandler>
|
||||
<div className="flex flex-col h-full gap-3">
|
||||
<div className="flex h-full overflow-auto gap-3">
|
||||
<Container className="w-full md:w-[390px] max-h-full relative">
|
||||
<ChatInterface />
|
||||
</Container>
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex flex-grow overflow-hidden">
|
||||
<div
|
||||
className={`relative flex-grow mr-5 ${isWorkspaceHidden ? "w-full" : "md:w-[390px]"}`}
|
||||
>
|
||||
<Container className="h-full">
|
||||
<ChatInterface />
|
||||
</Container>
|
||||
<div
|
||||
className={`absolute top-1/2 -translate-y-1/2 ${isWorkspaceHidden ? "-right-4" : "-right-4"} z-10`}
|
||||
>
|
||||
<ToggleWorkspaceIconButton
|
||||
onClick={toggleWorkspace}
|
||||
isHidden={isWorkspaceHidden}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="hidden md:flex flex-col grow gap-3">
|
||||
<div
|
||||
className={`hidden md:flex flex-col flex-grow transition-all duration-300 ${
|
||||
isWorkspaceHidden ? "w-0 opacity-0 overflow-hidden" : ""
|
||||
}`}
|
||||
>
|
||||
<Container
|
||||
className="h-2/3"
|
||||
className="flex-grow"
|
||||
labels={[
|
||||
{ label: "Workspace", to: "", icon: <CodeIcon /> },
|
||||
{ label: "Jupyter", to: "jupyter", icon: <ListIcon /> },
|
||||
|
||||
@@ -100,6 +100,7 @@ reportlab = "*"
|
||||
[tool.coverage.run]
|
||||
concurrency = ["gevent"]
|
||||
|
||||
|
||||
[tool.poetry.group.runtime.dependencies]
|
||||
jupyterlab = "*"
|
||||
notebook = "*"
|
||||
@@ -129,6 +130,7 @@ ignore = ["D1"]
|
||||
[tool.ruff.lint.pydocstyle]
|
||||
convention = "google"
|
||||
|
||||
|
||||
[tool.poetry.group.evaluation.dependencies]
|
||||
streamlit = "*"
|
||||
whatthepatch = "*"
|
||||
|
||||
Reference in New Issue
Block a user