mirror of
https://github.com/HKUDS/nanobot.git
synced 2026-06-15 07:14:08 +00:00
fix(webui): keep project heading singular
maintainer edit: render the Projects divider only before the first project group so Chats can sort between projects without duplicating the heading. Add middle and last ordering regression coverage.
This commit is contained in:
parent
a70871679c
commit
b2cabb2bd8
@ -175,6 +175,7 @@ export const ChatList = memo(function ChatList({
|
||||
const running = new Set(runningChatIds);
|
||||
const completed = new Set(completedChatIds);
|
||||
const compact = density === "compact";
|
||||
const firstProjectGroupIndex = limitedGroups.findIndex((group) => group.kind === "project");
|
||||
|
||||
return (
|
||||
<div className="h-full min-h-0 min-w-0 overflow-x-hidden overflow-y-auto overscroll-contain scrollbar-thin scrollbar-track-transparent">
|
||||
@ -192,12 +193,11 @@ export const ChatList = memo(function ChatList({
|
||||
|
||||
return (
|
||||
<section key={group.id} aria-label={group.label}>
|
||||
{group.kind === "project"
|
||||
&& limitedGroups[index - 1]?.kind !== "project" ? (
|
||||
<div className="px-2 pb-1 text-[12px] font-medium text-muted-foreground/65">
|
||||
{labels.projects}
|
||||
</div>
|
||||
) : null}
|
||||
{index === firstProjectGroupIndex ? (
|
||||
<div className="px-2 pb-1 text-[12px] font-medium text-muted-foreground/65">
|
||||
{labels.projects}
|
||||
</div>
|
||||
) : null}
|
||||
{group.kind === "project" ? (
|
||||
<ProjectGroupHeader
|
||||
label={group.label}
|
||||
|
||||
@ -312,4 +312,104 @@ describe("ChatList", () => {
|
||||
expect(chatsIdx).toBeLessThan(projBIdx);
|
||||
expect(within(allRegions[chatsIdx]).getByText("Recent chat")).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("keeps one Projects heading when Chats sorts between project groups", () => {
|
||||
const sessions = [
|
||||
session({
|
||||
chatId: "project-a",
|
||||
title: "Project A task",
|
||||
updatedAt: "2026-05-21T12:00:00Z",
|
||||
workspaceScope: {
|
||||
project_path: "/Users/me/project-a",
|
||||
project_name: "project-a",
|
||||
access_mode: "restricted",
|
||||
},
|
||||
}),
|
||||
session({
|
||||
chatId: "middle-chat",
|
||||
title: "Middle chat",
|
||||
updatedAt: "2026-05-21T11:00:00Z",
|
||||
}),
|
||||
session({
|
||||
chatId: "project-b",
|
||||
title: "Project B task",
|
||||
updatedAt: "2026-05-21T10:00:00Z",
|
||||
workspaceScope: {
|
||||
project_path: "/Users/me/project-b",
|
||||
project_name: "project-b",
|
||||
access_mode: "restricted",
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
render(
|
||||
<ChatList
|
||||
sessions={sessions}
|
||||
activeKey="websocket:middle-chat"
|
||||
onSelect={vi.fn()}
|
||||
onRequestDelete={vi.fn()}
|
||||
onTogglePin={vi.fn()}
|
||||
onRequestRename={vi.fn()}
|
||||
onToggleArchive={vi.fn()}
|
||||
showTimestamps
|
||||
/>,
|
||||
);
|
||||
|
||||
const regionNames = screen
|
||||
.getAllByRole("region")
|
||||
.map((r) => r.getAttribute("aria-label") ?? "");
|
||||
|
||||
expect(regionNames).toEqual(["project-a", "Chats", "project-b"]);
|
||||
expect(screen.getAllByText("Projects")).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("keeps Chats last when its latest conversation is older than all projects", () => {
|
||||
const sessions = [
|
||||
session({
|
||||
chatId: "project-a",
|
||||
title: "Project A task",
|
||||
updatedAt: "2026-05-21T12:00:00Z",
|
||||
workspaceScope: {
|
||||
project_path: "/Users/me/project-a",
|
||||
project_name: "project-a",
|
||||
access_mode: "restricted",
|
||||
},
|
||||
}),
|
||||
session({
|
||||
chatId: "project-b",
|
||||
title: "Project B task",
|
||||
updatedAt: "2026-05-21T11:00:00Z",
|
||||
workspaceScope: {
|
||||
project_path: "/Users/me/project-b",
|
||||
project_name: "project-b",
|
||||
access_mode: "restricted",
|
||||
},
|
||||
}),
|
||||
session({
|
||||
chatId: "old-chat",
|
||||
title: "Old chat",
|
||||
updatedAt: "2026-05-21T10:00:00Z",
|
||||
}),
|
||||
];
|
||||
|
||||
render(
|
||||
<ChatList
|
||||
sessions={sessions}
|
||||
activeKey="websocket:old-chat"
|
||||
onSelect={vi.fn()}
|
||||
onRequestDelete={vi.fn()}
|
||||
onTogglePin={vi.fn()}
|
||||
onRequestRename={vi.fn()}
|
||||
onToggleArchive={vi.fn()}
|
||||
showTimestamps
|
||||
/>,
|
||||
);
|
||||
|
||||
const regionNames = screen
|
||||
.getAllByRole("region")
|
||||
.map((r) => r.getAttribute("aria-label") ?? "");
|
||||
|
||||
expect(regionNames).toEqual(["project-a", "project-b", "Chats"]);
|
||||
expect(screen.getAllByText("Projects")).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user