Compare commits

...

15 Commits

Author SHA1 Message Date
Graham Neubig
2e5f482400 Update button positioning 2024-12-31 06:27:59 +09:00
openhands
ac6101833b Fix expand/collapse button positioning and visibility issues 2024-12-30 09:17:36 +00:00
openhands
a07df54fe1 Adjust expand/collapse button size and positioning, update tests 2024-12-30 09:12:44 +00:00
openhands
faa6b91530 Restore margin between chat and workspace windows 2024-12-30 09:05:01 +00:00
openhands
74f72a4dc9 Fix chat window layout to fill entire available space 2024-12-30 08:50:21 +00:00
openhands
3df3a07a98 Fix ToggleWorkspaceIconButton test and update component positioning 2024-12-30 08:38:12 +00:00
openhands
a53dd1773b Fix ToggleWorkspaceIconButton issues: correct arrow direction and visibility 2024-12-30 08:30:06 +00:00
openhands
b9a8b756ae Revert changes to chat-suggestions.tsx and fix linting issues in route.tsx 2024-12-30 08:19:23 +00:00
openhands
ce2a7c4b1f Simplify PR: Revert unnecessary changes and focus on collapsible workspace feature 2024-12-30 08:12:28 +00:00
openhands
b71623510c Fix pr #5896: Fix issue #5894: Make it possible to collapse the right-hand side of the openhands screen 2024-12-30 04:59:14 +00:00
openhands
3ad6afbe08 Update frontend test workflow to clear cache and use npx tsc 2024-12-30 03:57:07 +00:00
openhands
f763ba827c Fix TypeScript error in toggle-workspace-icon-button.tsx 2024-12-30 03:51:55 +00:00
openhands
aac04fe6ca Fix pr #5896: Fix issue #5894: Make it possible to collapse the right-hand side of the openhands screen 2024-12-30 01:09:56 +00:00
openhands
8bd4ae2a77 Fix pr #5896: Fix issue #5894: Make it possible to collapse the right-hand side of the openhands screen 2024-12-29 05:35:02 +00:00
openhands
ebce3c2fe4 Fix issue #5894: Make it possible to collapse the right-hand side of the openhands screen 2024-12-29 04:56:12 +00:00
6 changed files with 127 additions and 15 deletions

View 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);
});
});

View File

@@ -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();
});
});

View File

@@ -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}
>

View File

@@ -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"
/>
);
}

View File

@@ -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 /> },

View File

@@ -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 = "*"