diff --git a/packages/app/e2e/app/home.spec.ts b/packages/app/e2e/app/home.spec.ts index a3cedf7cb6f..58c0dc3acaa 100644 --- a/packages/app/e2e/app/home.spec.ts +++ b/packages/app/e2e/app/home.spec.ts @@ -19,3 +19,42 @@ test("server picker dialog opens from home", async ({ page }) => { await expect(dialog).toBeVisible() await expect(dialog.getByRole("textbox").first()).toBeVisible() }) + +test("home hides desktop history and sidebar controls", async ({ page }) => { + await page.setViewportSize({ width: 1400, height: 900 }) + await page.goto("/") + + await expect(page.getByRole("button", { name: "Toggle sidebar" })).toHaveCount(0) + await expect(page.getByRole("button", { name: "Go back" })).toHaveCount(0) + await expect(page.getByRole("button", { name: "Go forward" })).toHaveCount(0) + await expect(page.getByRole("button", { name: "Toggle menu" })).toHaveCount(0) +}) + +test("home keeps the mobile menu available", async ({ page }) => { + await page.setViewportSize({ width: 430, height: 900 }) + await page.goto("/") + + const toggle = page.getByRole("button", { name: "Toggle menu" }).first() + await expect(toggle).toBeVisible() + await toggle.click() + + const nav = page.locator('[data-component="sidebar-nav-mobile"]') + await expect(nav).toBeVisible() + await expect.poll(async () => (await nav.boundingBox())?.width ?? 0).toBeLessThan(120) + await expect(nav.getByRole("button", { name: "Settings" })).toBeVisible() + await expect(nav.getByRole("button", { name: "Help" })).toBeVisible() + + await page.setViewportSize({ width: 1400, height: 900 }) + await expect(nav).toBeHidden() + + await page.setViewportSize({ width: 430, height: 900 }) + await expect(toggle).toBeVisible() + await expect(toggle).toHaveAttribute("aria-expanded", "false") + await expect(nav).toHaveClass(/-translate-x-full/) + + await toggle.click() + await expect(nav).toBeVisible() + + await nav.getByRole("button", { name: "Settings" }).click() + await expect(page.getByRole("dialog")).toBeVisible() +}) diff --git a/packages/app/src/components/titlebar.tsx b/packages/app/src/components/titlebar.tsx index 345903420c0..0b2ef4cd66e 100644 --- a/packages/app/src/components/titlebar.tsx +++ b/packages/app/src/components/titlebar.tsx @@ -58,6 +58,7 @@ export function Titlebar() { }) const path = () => `${location.pathname}${location.search}${location.hash}` + const home = createMemo(() => !params.dir) const creating = createMemo(() => { if (!params.dir) return false if (params.id) return false @@ -198,91 +199,93 @@ export function Titlebar() { /> -
- - - - +
diff --git a/packages/app/src/pages/layout.tsx b/packages/app/src/pages/layout.tsx index ab2687dcab9..c8b72481782 100644 --- a/packages/app/src/pages/layout.tsx +++ b/packages/app/src/pages/layout.tsx @@ -200,13 +200,20 @@ export default function Layout(props: ParentProps) { onMount(() => { const stop = () => setState("sizing", false) + const sync = () => { + if (!window.matchMedia("(min-width: 1280px)").matches) return + layout.mobileSidebar.hide() + } window.addEventListener("pointerup", stop) window.addEventListener("pointercancel", stop) window.addEventListener("blur", stop) + window.addEventListener("resize", sync) + sync() onCleanup(() => { window.removeEventListener("pointerup", stop) window.removeEventListener("pointercancel", stop) window.removeEventListener("blur", stop) + window.removeEventListener("resize", sync) }) }) @@ -2242,6 +2249,7 @@ export default function Layout(props: ParentProps) { layout.sidebar.opened()} + hasPanel={() => !!currentProject()} aimMove={aim.move} projects={projects} renderProject={(project) => ( @@ -2340,7 +2348,9 @@ export default function Layout(props: ParentProps) { aria-label={language.t("sidebar.nav.projectsAndSessions")} data-component="sidebar-nav-mobile" classList={{ - "@container fixed top-10 bottom-0 left-0 z-50 w-full max-w-[400px] overflow-hidden border-r border-border-weaker-base bg-background-base transition-transform duration-200 ease-out": true, + "@container fixed top-10 bottom-0 left-0 z-50 overflow-hidden border-r border-border-weaker-base bg-background-base transition-transform duration-200 ease-out": true, + "w-16": !currentProject(), + "w-full max-w-[400px]": !!currentProject(), "translate-x-0": layout.mobileSidebar.opened(), "-translate-x-full": !layout.mobileSidebar.opened(), }} diff --git a/packages/app/src/pages/layout/sidebar-shell.tsx b/packages/app/src/pages/layout/sidebar-shell.tsx index ca36af2a421..e38aaec6baa 100644 --- a/packages/app/src/pages/layout/sidebar-shell.tsx +++ b/packages/app/src/pages/layout/sidebar-shell.tsx @@ -15,6 +15,7 @@ import { type LocalProject } from "@/context/layout" export const SidebarContent = (props: { mobile?: boolean opened: Accessor + hasPanel?: Accessor aimMove: (event: MouseEvent) => void projects: Accessor renderProject: (project: LocalProject) => JSX.Element @@ -33,6 +34,7 @@ export const SidebarContent = (props: { renderPanel: () => JSX.Element }): JSX.Element => { const expanded = createMemo(() => !!props.mobile || props.opened()) + const hasPanel = createMemo(() => props.hasPanel?.() ?? true) const placement = () => (props.mobile ? "bottom" : "right") let panel: HTMLDivElement | undefined @@ -111,15 +113,17 @@ export const SidebarContent = (props: {
-
{ - panel = el - }} - classList={{ "flex-1 flex h-full min-h-0 min-w-0 overflow-hidden": true, "pointer-events-none": !expanded() }} - aria-hidden={!expanded()} - > - {props.renderPanel()} -
+ +
{ + panel = el + }} + classList={{ "flex-1 flex h-full min-h-0 min-w-0 overflow-hidden": true, "pointer-events-none": !expanded() }} + aria-hidden={!expanded()} + > + {props.renderPanel()} +
+
) }