mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): focus agents terminal on tab switch (#24677)
Fixes [CODAGT-221](https://linear.app/codercom/issue/CODAGT-221/terminal-selection-in-side-panel-does-not-focus-input). Verified manually that the fix automatically focuses the terminal input when the terminal tab is selected.
This commit is contained in:
@@ -275,15 +275,26 @@ export const WorkspaceTerminal = ({
|
||||
refit();
|
||||
}, [isVisible, refit]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!terminal || !isVisible || !autoFocus) {
|
||||
return;
|
||||
}
|
||||
|
||||
const frame = requestAnimationFrame(() => {
|
||||
terminal.focus();
|
||||
});
|
||||
|
||||
return () => {
|
||||
cancelAnimationFrame(frame);
|
||||
};
|
||||
}, [terminal, isVisible, autoFocus]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!terminal || !hasBeenVisible) {
|
||||
return;
|
||||
}
|
||||
|
||||
terminal.clear();
|
||||
if (autoFocus) {
|
||||
terminal.focus();
|
||||
}
|
||||
terminal.options.disableStdin = true;
|
||||
|
||||
if (loading) {
|
||||
@@ -454,7 +465,6 @@ export const WorkspaceTerminal = ({
|
||||
}, [
|
||||
hasBeenVisible,
|
||||
agentId,
|
||||
autoFocus,
|
||||
baseUrl,
|
||||
containerName,
|
||||
containerUser,
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
withAuthProvider,
|
||||
withDashboardProvider,
|
||||
withProxyProvider,
|
||||
withWebSocket,
|
||||
} from "#/testHelpers/storybook";
|
||||
import {
|
||||
AgentChatPageLoadingView,
|
||||
@@ -1207,3 +1208,63 @@ export const ScrollStableAfterEditTruncation: Story = {
|
||||
).toBeNull();
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Selecting the Terminal tab in the sidebar must move keyboard focus into
|
||||
* the terminal so typing goes there, not the chat input.
|
||||
*/
|
||||
export const TerminalFocusOnTabSwitch: Story = {
|
||||
parameters: {
|
||||
chromatic: { disableSnapshot: true },
|
||||
webSocket: { "/api/v2/workspaceagents/": [{ event: "message", data: "" }] },
|
||||
},
|
||||
decorators: [withWebSocket],
|
||||
render: () => (
|
||||
<StoryAgentChatPageView
|
||||
showSidebarPanel
|
||||
workspace={MockWorkspace}
|
||||
workspaceAgent={MockWorkspaceAgent}
|
||||
/>
|
||||
),
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
// The sidebar should open on the Git tab by default.
|
||||
const terminalTab = await canvas.findByRole("tab", { name: "Terminal" });
|
||||
|
||||
// 1. Click the Terminal tab.
|
||||
await userEvent.click(terminalTab);
|
||||
|
||||
// Wait for the terminal container to appear.
|
||||
const terminalContainer = await waitFor(() => {
|
||||
const el = canvas.getByTestId("agents-sidebar-terminal");
|
||||
expect(el).toBeVisible();
|
||||
return el;
|
||||
});
|
||||
|
||||
// The xterm focus target is a textarea inside the terminal container.
|
||||
await waitFor(
|
||||
() => {
|
||||
const textarea = terminalContainer.querySelector("textarea");
|
||||
expect(textarea).not.toBeNull();
|
||||
expect(document.activeElement).toBe(textarea);
|
||||
},
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
|
||||
// 2. Switch to Git, then back to Terminal.
|
||||
const gitTab = canvas.getByRole("tab", { name: "Git" });
|
||||
await userEvent.click(gitTab);
|
||||
await userEvent.click(terminalTab);
|
||||
|
||||
// Focus should return to the terminal textarea.
|
||||
await waitFor(
|
||||
() => {
|
||||
const textarea = terminalContainer.querySelector("textarea");
|
||||
expect(textarea).not.toBeNull();
|
||||
expect(document.activeElement).toBe(textarea);
|
||||
},
|
||||
{ timeout: 3000 },
|
||||
);
|
||||
},
|
||||
};
|
||||
|
||||
@@ -94,7 +94,6 @@ export const TerminalPanel: FC<TerminalPanelProps> = ({
|
||||
ref={terminalRef}
|
||||
agentId={workspaceAgent.id}
|
||||
operatingSystem={workspaceAgent.operating_system}
|
||||
autoFocus={false}
|
||||
isVisible={isVisible}
|
||||
onStatusChange={setConnectionStatus}
|
||||
onError={handleTerminalError}
|
||||
|
||||
Reference in New Issue
Block a user