refactor: extract ConversationMobilePanelPage from conversation-main (#1376)

This commit is contained in:
Hiep Le
2026-06-16 19:27:38 +00:00
committed by GitHub
parent 063158679b
commit d09346f8ef
5 changed files with 112 additions and 74 deletions
@@ -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>
);
}
@@ -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>
);
}
+2 -4
View File
@@ -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";