mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-11 05:11:49 +01:00
fix(ui): restore pinned sidebar links above More (#20369)
Restore the mixed pinned navigation list after Inbox and before More, retaining saved order, agent pins, drag controls, and unpin actions. Verified with focused tests, UI checks, browser interaction checks, and passing PR CI. Greptile: 5/5.
This commit is contained in:
1 parent
bf0220d34a
commit
38cc6b2a74
2 files changed
+8
-8
No files matched your search
@@ -105,13 +105,15 @@ describe("Coordinator pinned pages", () => {
|
||||
expect(reads).toBeLessThanOrEqual(4 * agents.length);
|
||||
expect(items[0].to).toBe("/chats/agent-499");
|
||||
});
|
||||
it("keeps mixed pins in stored order and prevents native link dragging", async () => {
|
||||
it("keeps mixed pins above More in stored order and prevents native link dragging", async () => {
|
||||
fixture.agents = [{ id: "cto", companyId: "company", name: "CTO", urlKey: "cto", role: "cto", status: "idle" } as Agent];
|
||||
const saved = ["agent-chat:company:cto", "agent-chat:another-company:hidden", "/artifacts", "/dashboard"];
|
||||
localStorage.setItem("paperclip.coordinator.pinned-nav.v1:reviewer", JSON.stringify(saved));
|
||||
await renderSidebar();
|
||||
const links = [...container.querySelectorAll<HTMLAnchorElement>(".design-sidebar-pins a")];
|
||||
const links = [...navigation().querySelectorAll<HTMLAnchorElement>("a")].slice(3);
|
||||
expect(links.map(link => link.textContent)).toEqual(["CTO", "Artifacts", "Dashboard"]);
|
||||
const more = navigation().querySelector('button[aria-label="More workspace pages"]')!;
|
||||
expect(links.at(-1)!.compareDocumentPosition(more) & Node.DOCUMENT_POSITION_FOLLOWING).not.toBe(0);
|
||||
expect(JSON.parse(localStorage.getItem("paperclip.coordinator.pinned-nav.v1:reviewer")!)).toEqual(saved);
|
||||
const nativeDrag = new Event("dragstart", { bubbles: true, cancelable: true });
|
||||
act(() => links[1].dispatchEvent(nativeDrag));
|
||||
@@ -140,7 +142,7 @@ describe("Coordinator pinned pages", () => {
|
||||
await flush();
|
||||
}
|
||||
|
||||
it.each([false, true])("restores and unpins pages in the Pinned section when collapsed is %s", async collapsed => {
|
||||
it.each([false, true])("keeps pins above More without a separate section, restores and unpins when collapsed is %s", async collapsed => {
|
||||
fixture.collapsed = collapsed;
|
||||
await renderSidebar();
|
||||
expect(pinnedPage("/projects")).toBeNull();
|
||||
@@ -150,9 +152,9 @@ describe("Coordinator pinned pages", () => {
|
||||
expect(document.querySelector('[role="dialog"]')).not.toBeNull();
|
||||
expect(pinnedPage("/projects")?.textContent).toBe("Projects");
|
||||
expect(navigation().querySelector('[aria-label="Pinned pages"]')).toBeNull();
|
||||
if (!collapsed) expect(navigation().querySelector('[data-slot="sidebar-group-label"]')?.textContent).toBe("Pinned");
|
||||
expect(navigation().querySelector('[data-slot="sidebar-group-label"]')).toBeNull();
|
||||
const more = navigation().querySelector('button[aria-label="More workspace pages"]')!;
|
||||
expect(more.compareDocumentPosition(pinnedPage("/projects")!) & Node.DOCUMENT_POSITION_FOLLOWING).not.toBe(0);
|
||||
expect(pinnedPage("/projects")!.compareDocumentPosition(more) & Node.DOCUMENT_POSITION_FOLLOWING).not.toBe(0);
|
||||
await act(async () => root.unmount());
|
||||
root = createRoot(container);
|
||||
await renderSidebar();
|
||||
|
||||
@@ -170,6 +170,7 @@ export function CoordinatorSidebar({ children }: { children?: ReactNode }) {
|
||||
className="absolute right-3 top-1/2 -translate-y-1/2 rounded-md p-1 text-muted-foreground opacity-0 transition-opacity group-hover/coordinator-new:opacity-100 group-focus-within/coordinator-new:opacity-100 pointer-coarse:opacity-100 hover:bg-sidebar-accent focus-visible:ring-2 focus-visible:ring-ring"><Plus className="size-4" /></button>}
|
||||
</div>
|
||||
<SidebarNavItem className="design-sidebar-item" to="/decisions" label="Inbox" icon={Inbox} badge={attentionCount} badgeLabel="decisions" />
|
||||
{orderedPins.length > 0 && <PinnedNavigationList key={`${companyId}:${userId}`} items={orderedPins} rail={rail} onReorder={pinnedNavigation.reorder} onUnpin={pinnedNavigation.toggle} />}
|
||||
</div>
|
||||
<Flyout open={moreOpen} onOpenChange={setMoreOpen}>
|
||||
<FlyoutTrigger asChild><button type="button" aria-label="More workspace pages" className="design-sidebar-item flex w-auto items-center gap-2.5 rounded-lg py-1.5 text-left transition-colors focus-visible:ring-2 focus-visible:ring-ring"><MoreHorizontal className="size-4 shrink-0" /><span className={rail ? SIDEBAR_RAIL_HIDDEN_LABEL : undefined}>More</span></button></FlyoutTrigger>
|
||||
@@ -192,9 +193,6 @@ export function CoordinatorSidebar({ children }: { children?: ReactNode }) {
|
||||
</FlyoutContent>
|
||||
</Flyout>
|
||||
</div>
|
||||
{orderedPins.length > 0 && <div className="design-sidebar-pins"><SidebarSection label="Pinned">
|
||||
<PinnedNavigationList key={`${companyId}:${userId}`} items={orderedPins} rail={rail} onReorder={pinnedNavigation.reorder} onUnpin={pinnedNavigation.toggle} />
|
||||
</SidebarSection></div>}
|
||||
{pinnedNavigation.error && <p role="alert" className="px-4 py-2 text-xs text-destructive">{pinnedNavigation.error}</p>}
|
||||
{!rail && session.isSuccess && recents.length > 0 && <div className="design-sidebar-recents"><SidebarSection label="Recents">
|
||||
<RecentNavigationList>
|
||||
|
||||
Reference in new issue
Block a user