Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions site/src/pages/AgentsPage/AgentChatPageView.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
AgentChatPageNotFoundView,
AgentChatPageView,
} from "./AgentChatPageView";
import type { ChatMessageInputRef } from "./components/AgentChatInput";
import type { ChatDetailError } from "./components/ChatConversation/chatError";
import { createChatStore } from "./components/ChatConversation/chatStore";
import { buildLongConversation } from "./components/ChatConversation/storyFixtures";
Expand Down Expand Up @@ -669,6 +670,43 @@ index abc1234..def5678 100644
},
};

const collapsePanelsSend = fn();

const CollapsingPanelsFocusStory: FC = () => {
const [showSidebarPanel, setShowSidebarPanel] = useState(true);
const chatInputRef = useRef<ChatMessageInputRef | null>(null);

return (
<StoryAgentChatPageView
isSidebarCollapsed
showSidebarPanel={showSidebarPanel}
onSetShowSidebarPanel={setShowSidebarPanel}
editing={{
chatInputRef,
handleSendFromInput: collapsePanelsSend,
}}
/>
);
};

/** Closing the right panel while the left sidebar is collapsed returns focus to chat. */
export const CollapsingAllSidePanelsFocusesChat: Story = {
render: () => <CollapsingPanelsFocusStory />,
beforeEach: () => {
collapsePanelsSend.mockClear();
},
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
await userEvent.click(canvas.getByRole("button", { name: "Toggle panel" }));

const composer = canvas.getByRole("textbox", { name: "Chat message" });
await expect(composer).toHaveFocus();

await userEvent.type(composer, "Send after collapsing{Enter}");
await expect(collapsePanelsSend).toHaveBeenCalled();
},
};

/** Left sidebar is collapsed. */
export const SidebarCollapsed: Story = {
render: () => <StoryAgentChatPageView isSidebarCollapsed />,
Expand Down
16 changes: 16 additions & 0 deletions site/src/pages/AgentsPage/AgentChatPageView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
type ReactNode,
type RefObject,
useEffect,
useRef,
useState,
} from "react";
import { useQueryClient } from "react-query";
Expand Down Expand Up @@ -424,6 +425,21 @@ export const AgentChatPageView: FC<AgentChatPageViewProps> = ({
};

const [isRightPanelExpanded, setIsRightPanelExpanded] = useState(false);
const previousPanelsRef = useRef({
isSidebarCollapsed,
showSidebarPanel,
});
useEffect(() => {
const previous = previousPanelsRef.current;
const didCollapseLeftSidebar =
!previous.isSidebarCollapsed && isSidebarCollapsed;
const didCloseRightPanel = previous.showSidebarPanel && !showSidebarPanel;
previousPanelsRef.current = { isSidebarCollapsed, showSidebarPanel };

if ((didCollapseLeftSidebar || didCloseRightPanel) && !showSidebarPanel) {
editing.chatInputRef.current?.focus();
}
}, [editing.chatInputRef, isSidebarCollapsed, showSidebarPanel]);
// The turn already running when the page loaded must not anchor the
// scroller, even if its prompt is older than the newest messages page and
// only renders after the user pages up. Message ids are allocated from one
Expand Down
Loading