mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 09:07:46 +02:00
Fix mobile sidebar section controls
Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
1 parent
0891c1c72f
commit
cc1628c13a
2 files changed
+49
-2
No files matched your search
@@ -6,12 +6,20 @@ import { createRoot } from "react-dom/client";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { SidebarSection } from "./SidebarSection";
|
||||
|
||||
const sidebarState = vi.hoisted(() => ({
|
||||
isMobile: false,
|
||||
}));
|
||||
|
||||
vi.mock("@/lib/router", () => ({
|
||||
Link: ({ children, to, ...props }: { children: ReactNode; to: string }) => (
|
||||
<a href={to} {...props}>{children}</a>
|
||||
),
|
||||
}));
|
||||
|
||||
vi.mock("../context/SidebarContext", () => ({
|
||||
useSidebar: () => sidebarState,
|
||||
}));
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(globalThis as any).IS_REACT_ACT_ENVIRONMENT = true;
|
||||
|
||||
@@ -43,6 +51,7 @@ describe("SidebarSection", () => {
|
||||
let root: ReturnType<typeof createRoot> | null;
|
||||
|
||||
beforeEach(() => {
|
||||
sidebarState.isMobile = false;
|
||||
container = document.createElement("div");
|
||||
document.body.appendChild(container);
|
||||
root = null;
|
||||
@@ -146,4 +155,36 @@ describe("SidebarSection", () => {
|
||||
});
|
||||
expect(onAction).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("keeps section header controls visible on mobile", async () => {
|
||||
sidebarState.isMobile = true;
|
||||
const currentRoot = createRoot(container);
|
||||
root = currentRoot;
|
||||
|
||||
await act(async () => {
|
||||
currentRoot.render(
|
||||
<SidebarSection
|
||||
label="Projects"
|
||||
collapsible={{ open: false, onOpenChange: vi.fn() }}
|
||||
menu={{
|
||||
ariaLabel: "Projects section actions",
|
||||
actions: [{ type: "item", label: "New project" }],
|
||||
}}
|
||||
>
|
||||
<a href="/projects">Projects</a>
|
||||
</SidebarSection>,
|
||||
);
|
||||
});
|
||||
await flushReact();
|
||||
|
||||
const projectsLabel = Array.from(container.querySelectorAll("span"))
|
||||
.find((element) => element.textContent === "Projects");
|
||||
const caret = projectsLabel?.previousElementSibling;
|
||||
const trigger = container.querySelector('button[aria-label="Projects section actions"]');
|
||||
|
||||
expect(caret?.getAttribute("class")).toContain("opacity-100");
|
||||
expect(caret?.getAttribute("class")).not.toContain("opacity-0");
|
||||
expect(trigger?.getAttribute("class")).toContain("opacity-100");
|
||||
expect(trigger?.getAttribute("class")).not.toContain("opacity-0");
|
||||
});
|
||||
});
|
||||
@@ -2,6 +2,7 @@ import { useState, type ComponentType, type ReactNode } from "react";
|
||||
import { Link } from "@/lib/router";
|
||||
import { ChevronRight, MoreHorizontal } from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { useSidebar } from "../context/SidebarContext";
|
||||
import {
|
||||
Collapsible,
|
||||
CollapsibleContent,
|
||||
@@ -59,11 +60,16 @@ function SidebarSectionHeader({
|
||||
label,
|
||||
menu,
|
||||
}: Pick<SidebarSectionProps, "collapsible" | "label" | "menu">) {
|
||||
const { isMobile } = useSidebar();
|
||||
const [menuOpen, setMenuOpen] = useState(false);
|
||||
const hasMenu = Boolean(menu && ((menu.actions?.length ?? 0) > 0 || (menu.radioChoices?.length ?? 0) > 0));
|
||||
const labelClassName = "text-[10px] font-medium uppercase tracking-widest font-mono text-muted-foreground/60";
|
||||
const headerControlVisibilityClassName = isMobile
|
||||
? "opacity-100"
|
||||
: "opacity-0 group-hover/sidebar-section:opacity-100 group-focus-within/sidebar-section:opacity-100";
|
||||
const caretClassName = cn(
|
||||
"h-3 w-3 shrink-0 text-muted-foreground/60 opacity-0 transition-all group-hover/sidebar-section:opacity-100 group-focus-within/sidebar-section:opacity-100",
|
||||
"h-3 w-3 shrink-0 text-muted-foreground/60 transition-all",
|
||||
headerControlVisibilityClassName,
|
||||
collapsible?.open && "rotate-90",
|
||||
menuOpen && "opacity-100",
|
||||
);
|
||||
@@ -97,7 +103,7 @@ function SidebarSectionHeader({
|
||||
size="icon-xs"
|
||||
className={cn(
|
||||
"h-5 w-5 text-muted-foreground/60 transition-opacity hover:text-foreground data-[state=open]:opacity-100",
|
||||
"opacity-0 group-hover/sidebar-section:opacity-100 group-focus-within/sidebar-section:opacity-100",
|
||||
headerControlVisibilityClassName,
|
||||
)}
|
||||
aria-label={menu?.ariaLabel ?? `${label} actions`}
|
||||
>
|
||||
|
||||
Reference in new issue
Block a user