Skip to content
Open
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
95 changes: 95 additions & 0 deletions examples/plugins/t3sidebar/src/SubagentsChip.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// @vitest-environment jsdom
import { afterEach, describe, expect, it } from "vitest";
import { cleanup, fireEvent, screen } from "@testing-library/react";
import { loadPluginApp, renderSlot } from "@get-bb/plugin-sdk/testing/app";
import type { PluginSidebarThread } from "@get-bb/plugin-sdk";

const app = await loadPluginApp(() => import("../app"));
const childrenChip = app.threadHeaderActions.find(
(slot) => slot.id === "children",
)!;

function thread(
overrides: Partial<PluginSidebarThread> = {},
): PluginSidebarThread {
return {
id: "thr_1",
projectId: "proj_1",
title: "A thread",
titleFallback: null,
parentThreadId: null,
sectionId: null,
originKind: null,
originPluginId: null,
providerId: "codex",
hasPendingInteraction: false,
activity: {
workflows: 0,
backgroundAgents: 0,
backgroundCommands: 0,
planMode: 0,
goals: 0,
},
indicator: "none",
indicatorLabel: null,
isUnread: false,
isPinned: false,
isArchived: false,
environment: null,
host: null,
createdAt: 100,
updatedAt: 100,
lastReadAt: 100,
latestAttentionAt: 100,
...overrides,
};
}

afterEach(cleanup);

describe("SubagentsChip child list", () => {
it("caps a long menu and makes the child list scrollable", () => {
renderSlot(
childrenChip,
{
threadId: "parent",
projectId: "proj_1",
isCompactViewport: true,
},
{
sidebarThreads: {
status: "ready",
threads: [
thread({ id: "parent", title: "Parent" }),
...Array.from({ length: 26 }, (_, index) =>
thread({
id: `child_${index}`,
title: `Child ${index + 1}`,
parentThreadId: "parent",
createdAt: index,
}),
),
],
projects: [{ id: "proj_1", name: "bb", isPersonal: false }],
},
},
);

fireEvent.click(screen.getByRole("button", { name: "26 child threads" }));

const menu = screen.getByRole("menu", { name: "Child threads" });
const list = menu.querySelector("ul");
expect(menu.classList.contains("flex")).toBe(true);
expect(
menu.classList.contains("max-h-[min(32rem,calc(100dvh-6rem))]"),
).toBe(true);
expect(list?.classList.contains("min-h-0")).toBe(true);
expect(list?.classList.contains("overflow-y-auto")).toBe(true);
expect(list?.classList.contains("overscroll-contain")).toBe(true);
expect(list?.classList.contains("touch-pan-y")).toBe(true);
expect(list?.classList.contains("[-webkit-overflow-scrolling:touch]")).toBe(
true,
);
expect(screen.getAllByRole("menuitem")).toHaveLength(26);
});
});
4 changes: 2 additions & 2 deletions examples/plugins/t3sidebar/src/SubagentsChip.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ export function SubagentsChip({
<div
role="menu"
aria-label="Child threads"
className="absolute right-0 top-9 z-50 w-80 overflow-hidden rounded-xl border border-border bg-popover shadow-lg"
className="absolute right-0 top-9 z-50 flex max-h-[min(32rem,calc(100dvh-6rem))] w-80 flex-col overflow-hidden rounded-xl border border-border bg-popover shadow-lg"
>
<div className="flex items-center gap-2 px-3 pb-1 pt-2.5">
<span className="text-xs font-semibold">Children</span>
<span className="ml-auto text-2xs text-muted-foreground">
{children.length}
</span>
</div>
<ul className="flex flex-col gap-px p-1.5 pt-0.5">
<ul className="flex min-h-0 touch-pan-y flex-col gap-px overflow-y-auto overscroll-contain p-1.5 pt-0.5 [-webkit-overflow-scrolling:touch]">
{children.map((child) => (
<li key={child.id} className="list-none">
<button
Expand Down
Loading