mirror of
https://github.com/OpenHands/OpenHands.git
synced 2026-10-07 14:58:39 +08:00
refactor: extract ConversationMobilePanelPage from conversation-main (#1376)
This commit is contained in:
@@ -0,0 +1,32 @@
|
||||
import { render, screen, fireEvent } from "@testing-library/react";
|
||||
import { describe, it, expect, vi } from "vitest";
|
||||
import { useConversationStore } from "#/stores/conversation-store";
|
||||
|
||||
// Stub the heavy tab subtree so the test focuses on this page's own behavior.
|
||||
vi.mock(
|
||||
"#/components/features/conversation/conversation-tabs/conversation-tabs",
|
||||
() => ({ ConversationTabs: () => <div data-testid="conversation-tabs" /> }),
|
||||
);
|
||||
vi.mock(
|
||||
"#/components/features/conversation/conversation-tabs/conversation-tab-content/conversation-tab-content",
|
||||
() => ({ ConversationTabContent: () => <div data-testid="tab-content" /> }),
|
||||
);
|
||||
|
||||
import { ConversationMobilePanelPage } from "#/components/features/conversation/conversation-main/conversation-mobile-panel-page";
|
||||
|
||||
describe("ConversationMobilePanelPage", () => {
|
||||
it("opens the right panel on mount", () => {
|
||||
render(<ConversationMobilePanelPage onNavigateBack={vi.fn()} />);
|
||||
|
||||
expect(useConversationStore.getState().isRightPanelShown).toBe(true);
|
||||
});
|
||||
|
||||
it("calls onNavigateBack when the back button is clicked", () => {
|
||||
const onNavigateBack = vi.fn();
|
||||
render(<ConversationMobilePanelPage onNavigateBack={onNavigateBack} />);
|
||||
|
||||
fireEvent.click(screen.getByTestId("conversation-mobile-panel-back"));
|
||||
|
||||
expect(onNavigateBack).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -30,6 +30,11 @@ vi.mock(
|
||||
"#/components/features/conversation/conversation-main/conversation-main",
|
||||
() => ({
|
||||
ConversationMain: () => <div data-testid="conversation-main" />,
|
||||
}),
|
||||
);
|
||||
vi.mock(
|
||||
"#/components/features/conversation/conversation-main/conversation-mobile-panel-page",
|
||||
() => ({
|
||||
ConversationMobilePanelPage: () => <div data-testid="conversation-panel" />,
|
||||
}),
|
||||
);
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import React from "react";
|
||||
import { ChevronLeft } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import { cn } from "#/utils/utils";
|
||||
import {
|
||||
mobileTopBarIconButtonClassName,
|
||||
mobileTopBarIconClassName,
|
||||
} from "#/utils/mobile-top-bar-icon-button-classes";
|
||||
import { ChatInterfaceWrapper } from "./chat-interface-wrapper";
|
||||
import { ConversationTabContent } from "../conversation-tabs/conversation-tab-content/conversation-tab-content";
|
||||
import { ConversationNameWithStatus } from "../conversation-name-with-status";
|
||||
@@ -133,65 +125,3 @@ export function ConversationMain() {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function ConversationMobilePanelPage({
|
||||
onNavigateBack,
|
||||
}: {
|
||||
onNavigateBack: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation("openhands");
|
||||
const { setIsRightPanelShown, setHasRightPanelToggled, setSelectedTab } =
|
||||
useConversationStore();
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
setIsRightPanelShown(true);
|
||||
setHasRightPanelToggled(true);
|
||||
const st = useConversationStore.getState();
|
||||
if (!st.selectedTab) {
|
||||
setSelectedTab("files");
|
||||
}
|
||||
return () => {
|
||||
setIsRightPanelShown(false);
|
||||
setHasRightPanelToggled(false);
|
||||
};
|
||||
}, [setIsRightPanelShown, setHasRightPanelToggled, setSelectedTab]);
|
||||
|
||||
const handleBack = () => {
|
||||
onNavigateBack();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col bg-[var(--oh-surface)]">
|
||||
<div
|
||||
data-testid="conversation-mobile-panel-top"
|
||||
className="flex h-10 min-h-10 shrink-0 items-center gap-1.5 border-b border-[var(--oh-border)] pl-2.5"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="conversation-mobile-panel-back"
|
||||
onClick={handleBack}
|
||||
aria-label={t(I18nKey.COMMON$BACK)}
|
||||
className={mobileTopBarIconButtonClassName}
|
||||
>
|
||||
<ChevronLeft
|
||||
size={20}
|
||||
className={mobileTopBarIconClassName}
|
||||
aria-hidden
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</button>
|
||||
<div className="flex min-h-0 min-w-0 flex-1 items-center self-stretch">
|
||||
<div
|
||||
data-testid="tabs-pane-header"
|
||||
className="flex h-full min-h-0 w-full min-w-0 flex-col justify-center"
|
||||
>
|
||||
<ConversationTabs variant="compact" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-0 flex-1 flex-col bg-[var(--oh-surface)]">
|
||||
<ConversationTabContent />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
import React from "react";
|
||||
import { ChevronLeft } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { I18nKey } from "#/i18n/declaration";
|
||||
import {
|
||||
mobileTopBarIconButtonClassName,
|
||||
mobileTopBarIconClassName,
|
||||
} from "#/utils/mobile-top-bar-icon-button-classes";
|
||||
import { useConversationStore } from "#/stores/conversation-store";
|
||||
import { ConversationTabContent } from "../conversation-tabs/conversation-tab-content/conversation-tab-content";
|
||||
import { ConversationTabs } from "../conversation-tabs/conversation-tabs";
|
||||
|
||||
export function ConversationMobilePanelPage({
|
||||
onNavigateBack,
|
||||
}: {
|
||||
onNavigateBack: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation("openhands");
|
||||
const { setIsRightPanelShown, setHasRightPanelToggled, setSelectedTab } =
|
||||
useConversationStore();
|
||||
|
||||
React.useLayoutEffect(() => {
|
||||
setIsRightPanelShown(true);
|
||||
setHasRightPanelToggled(true);
|
||||
const st = useConversationStore.getState();
|
||||
if (!st.selectedTab) {
|
||||
setSelectedTab("files");
|
||||
}
|
||||
return () => {
|
||||
setIsRightPanelShown(false);
|
||||
setHasRightPanelToggled(false);
|
||||
};
|
||||
}, [setIsRightPanelShown, setHasRightPanelToggled, setSelectedTab]);
|
||||
|
||||
const handleBack = () => {
|
||||
onNavigateBack();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full min-h-0 flex-col bg-[var(--oh-surface)]">
|
||||
<div
|
||||
data-testid="conversation-mobile-panel-top"
|
||||
className="flex h-10 min-h-10 shrink-0 items-center gap-1.5 border-b border-[var(--oh-border)] pl-2.5"
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
data-testid="conversation-mobile-panel-back"
|
||||
onClick={handleBack}
|
||||
aria-label={t(I18nKey.COMMON$BACK)}
|
||||
className={mobileTopBarIconButtonClassName}
|
||||
>
|
||||
<ChevronLeft
|
||||
size={20}
|
||||
className={mobileTopBarIconClassName}
|
||||
aria-hidden
|
||||
strokeWidth={2}
|
||||
/>
|
||||
</button>
|
||||
<div className="flex min-h-0 min-w-0 flex-1 items-center self-stretch">
|
||||
<div
|
||||
data-testid="tabs-pane-header"
|
||||
className="flex h-full min-h-0 w-full min-w-0 flex-col justify-center"
|
||||
>
|
||||
<ConversationTabs variant="compact" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex min-h-0 flex-1 flex-col bg-[var(--oh-surface)]">
|
||||
<ConversationTabContent />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -21,10 +21,8 @@ import { useTaskPolling } from "#/hooks/query/use-task-polling";
|
||||
|
||||
import { displayErrorToast } from "#/utils/custom-toast-handlers";
|
||||
import { useIsAuthed } from "#/hooks/query/use-is-authed";
|
||||
import {
|
||||
ConversationMain,
|
||||
ConversationMobilePanelPage,
|
||||
} from "#/components/features/conversation/conversation-main/conversation-main";
|
||||
import { ConversationMain } from "#/components/features/conversation/conversation-main/conversation-main";
|
||||
import { ConversationMobilePanelPage } from "#/components/features/conversation/conversation-main/conversation-mobile-panel-page";
|
||||
|
||||
import { WebSocketProviderWrapper } from "#/contexts/websocket-provider-wrapper";
|
||||
import { useErrorMessageStore } from "#/stores/error-message-store";
|
||||
|
||||
Reference in New Issue
Block a user