mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
fix(site): preserve running spinner on agents sidebar row hover (#22452)
When hovering over a running/pending chat in the agents sidebar, the spinning status icon was being replaced by the expand/collapse chevron button. This was disorienting because the spinner conveys important "in progress" state. ## Changes **`AgentsSidebar.tsx`**: - Added `group/icon` scoped hover group to the icon container div - When a chat is executing (`pending`/`running`), the chevron toggle only appears on hover of the icon area itself, not the entire row - Non-executing chats retain the original whole-row hover behavior (no UX change) **`AgentsSidebar.stories.tsx`**: - Added `RunningChatPreservesSpinner` story verifying the spinner is present and the toggle button starts invisible for running chats with children Co-authored-by: Coder <coder@users.noreply.github.com>
This commit is contained in:
@@ -197,6 +197,49 @@ export const ExpandCollapse: Story = {
|
||||
},
|
||||
};
|
||||
|
||||
export const RunningChatPreservesSpinner: Story = {
|
||||
args: {
|
||||
chats: [
|
||||
buildChat({
|
||||
id: "root-running",
|
||||
title: "Running root agent",
|
||||
status: "running",
|
||||
}),
|
||||
buildChat({
|
||||
id: "child-of-running",
|
||||
title: "Child of running",
|
||||
parent_chat_id: "root-running",
|
||||
root_chat_id: "root-running",
|
||||
}),
|
||||
],
|
||||
},
|
||||
parameters: {
|
||||
reactRouter: reactRouterParameters({
|
||||
location: {
|
||||
path: "/agents/child-of-running",
|
||||
pathParams: { agentId: "child-of-running" },
|
||||
},
|
||||
routing: agentsRouting,
|
||||
}),
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
const canvas = within(canvasElement);
|
||||
|
||||
// The root chat is running and has children, so a spinning
|
||||
// Loader2Icon should be rendered inside the icon wrapper.
|
||||
const node = canvas.getByTestId("agents-tree-node-root-running");
|
||||
const spinner = node.querySelector(".animate-spin");
|
||||
await expect(spinner).toBeInTheDocument();
|
||||
|
||||
// The toggle button should exist (the node has children) but
|
||||
// must be invisible by default — it only appears on hover of
|
||||
// the icon area itself, not the whole row.
|
||||
const toggle = canvas.getByTestId("agents-tree-toggle-root-running");
|
||||
await expect(toggle).toBeInTheDocument();
|
||||
await expect(toggle.className).toMatch(/\binvisible\b/);
|
||||
},
|
||||
};
|
||||
|
||||
export const ActiveChatAncestryExpanded: Story = {
|
||||
args: {
|
||||
chats: [
|
||||
|
||||
@@ -327,6 +327,7 @@ const ChatTreeNode = memo<ChatTreeNodeProps>(({ chat, isChildNode }) => {
|
||||
}`;
|
||||
const isArchivingThisChat = isArchiving && archivingChatId === chat.id;
|
||||
const isExpanded = normalizedSearch ? true : (expandedById[chatID] ?? false);
|
||||
const isExecuting = chat.status === "pending" || chat.status === "running";
|
||||
|
||||
return (
|
||||
<div className="flex min-w-0 flex-col">
|
||||
@@ -340,11 +341,21 @@ const ChatTreeNode = memo<ChatTreeNodeProps>(({ chat, isChildNode }) => {
|
||||
"before:absolute before:-left-2.5 before:top-[17px] before:h-px before:w-2.5 before:bg-border-default/70",
|
||||
)}
|
||||
>
|
||||
<div className="relative mt-1.5 h-5 w-5 shrink-0">
|
||||
<div
|
||||
className={cn(
|
||||
"group/icon relative mt-1.5 h-5 w-5 shrink-0",
|
||||
hasChildren && "cursor-pointer",
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-5 w-5 items-center justify-center rounded-md",
|
||||
hasChildren && "[@media(hover:hover)]:group-hover:invisible",
|
||||
hasChildren &&
|
||||
!isExecuting &&
|
||||
"[@media(hover:hover)]:group-hover:invisible",
|
||||
hasChildren &&
|
||||
isExecuting &&
|
||||
"[@media(hover:hover)]:group-hover/icon:invisible",
|
||||
)}
|
||||
>
|
||||
<StatusIcon
|
||||
@@ -361,7 +372,12 @@ const ChatTreeNode = memo<ChatTreeNodeProps>(({ chat, isChildNode }) => {
|
||||
variant="subtle"
|
||||
size="icon"
|
||||
onClick={() => toggleExpanded(chatID)}
|
||||
className="absolute inset-0 invisible flex h-5 w-5 min-w-0 items-center justify-center rounded-md p-0 text-content-secondary/60 hover:text-content-primary [@media(hover:hover)]:group-hover:visible [&>svg]:size-3.5"
|
||||
className={cn(
|
||||
"absolute inset-0 invisible flex h-5 w-5 min-w-0 items-center justify-center rounded-md p-0 text-content-secondary/60 hover:text-content-primary [&>svg]:size-3.5",
|
||||
isExecuting
|
||||
? "[@media(hover:hover)]:group-hover/icon:visible"
|
||||
: "[@media(hover:hover)]:group-hover:visible",
|
||||
)}
|
||||
data-testid={`agents-tree-toggle-${chat.id}`}
|
||||
aria-label={isExpanded ? "Collapse" : "Expand"}
|
||||
aria-expanded={isExpanded}
|
||||
|
||||
Reference in New Issue
Block a user