diff --git a/packages/app/e2e/browser/sidebar-help.spec.ts b/packages/app/e2e/browser/sidebar-help.spec.ts index 11c7cc52b7..6d2ed6e96f 100644 --- a/packages/app/e2e/browser/sidebar-help.spec.ts +++ b/packages/app/e2e/browser/sidebar-help.spec.ts @@ -155,6 +155,18 @@ test("keeps diagnostics available from Settings after globalizing the sheet", as test.describe("compact sidebar help", () => { test.use({ viewport: { width: 390, height: 844 }, isMobile: true, hasTouch: true }); + test("footer buttons provide finger-sized targets and accept taps beyond the glyph", async ({ + page, + }) => { + await gotoAppShell(page); + await openCompactSidebar(page); + await expectFingerSizedFooterTargets(page); + await tapHelpNearTargetEdge(page); + await expect(page.getByTestId("sidebar-help-menu")).toBeVisible(); + await page.getByTestId("sidebar-help-diagnostics").click(); + await expectDiagnosticReport(page); + }); + test("offers diagnostics without advertising disabled keyboard shortcuts", async ({ page }) => { await gotoAppShell(page); await page.getByRole("button", { name: "Open menu", exact: true }).click(); @@ -165,3 +177,23 @@ test.describe("compact sidebar help", () => { await expectDiagnosticReport(page); }); }); + +async function openCompactSidebar(page: Page): Promise { + await page.getByRole("button", { name: "Open menu", exact: true }).click(); + await expect(page.getByTestId("sidebar-footer-bottom-line")).toBeVisible(); +} + +async function expectFingerSizedFooterTargets(page: Page): Promise { + const buttons = page.getByTestId("sidebar-footer-bottom-line").getByRole("button"); + await expect(buttons).toHaveCount(5); + for (const button of await buttons.all()) { + const bounds = await button.boundingBox(); + expect(bounds?.width).toBeGreaterThanOrEqual(44); + expect(bounds?.height).toBeGreaterThanOrEqual(44); + } +} + +async function tapHelpNearTargetEdge(page: Page): Promise { + const button = page.getByRole("button", { name: "Help and support", exact: true }); + await button.tap({ position: { x: 40, y: 40 } }); +} diff --git a/packages/app/src/components/left-sidebar.tsx b/packages/app/src/components/left-sidebar.tsx index 0737634a1b..a6def126b1 100644 --- a/packages/app/src/components/left-sidebar.tsx +++ b/packages/app/src/components/left-sidebar.tsx @@ -26,6 +26,7 @@ import { SidebarSeparator } from "@/components/sidebar/sidebar-separator"; import { SidebarNavRows } from "@/components/sidebar/sidebar-nav-rows"; import { SidebarHelpMenu } from "@/components/sidebar/sidebar-help-menu"; import { SidebarResizeHandle } from "@/components/sidebar-resize-handle"; +import { buttonControlHeight } from "@/components/ui/control-geometry"; import { Shortcut } from "@/components/ui/shortcut"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { HEADER_INNER_HEIGHT, useIsCompactFormFactor } from "@/constants/layout"; @@ -275,7 +276,7 @@ function FooterIconButton({ testID, label, icon: Icon, - iconSize, + iconSizeAdjustment = 0, shortcutKeys, theme, }: { @@ -284,17 +285,20 @@ function FooterIconButton({ label: string; icon: typeof FolderPlus; /** Only for a glyph that reads larger than the others at the same size. */ - iconSize?: number; + iconSizeAdjustment?: number; shortcutKeys?: ReturnType; theme: SidebarTheme; buttonRef?: RefObject; }) { + const isCompact = useIsCompactFormFactor(); + const iconSize = isCompact ? theme.iconSize.xl : theme.iconSize.md; + return ( {({ hovered }) => ( )} @@ -367,7 +371,7 @@ function SidebarHostPicker({ label={label} icon={Server} // Server's two boxes fill more of the square than the other glyphs. - iconSize={theme.iconSize.md - 1} + iconSizeAdjustment={-1} theme={theme} /> @@ -923,7 +927,7 @@ const styles = StyleSheet.create((theme) => ({ borderTopWidth: 1, borderTopColor: theme.colors.border, }, - // The icons' 28pt buttons sit edge to edge; their own inset spaces the glyphs. + // Buttons sit edge to edge; their own inset spaces the glyphs. sidebarFooter: { flexDirection: "row", alignItems: "center", @@ -940,14 +944,14 @@ const styles = StyleSheet.create((theme) => ({ paddingVertical: theme.spacing[1.5], gap: 2, }, - footerIconButton: { - width: 28, - height: 28, + footerIconButton: (isCompact: boolean) => ({ + width: isCompact ? buttonControlHeight.md : buttonControlHeight.xs, + height: isCompact ? buttonControlHeight.md : buttonControlHeight.xs, alignItems: "center", justifyContent: "center", paddingVertical: theme.spacing[1], paddingHorizontal: theme.spacing[1], - }, + }), tooltipRow: { flexDirection: "row", alignItems: "center", diff --git a/packages/app/src/components/sidebar/sidebar-help-menu.tsx b/packages/app/src/components/sidebar/sidebar-help-menu.tsx index d709b3d9ea..85acc8cf30 100644 --- a/packages/app/src/components/sidebar/sidebar-help-menu.tsx +++ b/packages/app/src/components/sidebar/sidebar-help-menu.tsx @@ -14,6 +14,8 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { buttonControlHeight } from "@/components/ui/control-geometry"; +import { useIsCompactFormFactor } from "@/constants/layout"; import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip"; import { useAppDiagnosticStore } from "@/diagnostics/store"; import { useKeyboardShortcutsAvailable } from "@/keyboard/availability"; @@ -77,6 +79,7 @@ function HostVersionHint({ host }: { host: HostProfile }) { } export function SidebarHelpMenu() { + const isCompact = useIsCompactFormFactor(); const { t } = useTranslation(); const shortcutsAvailable = useKeyboardShortcutsAvailable(); const openAppDiagnostic = useAppDiagnosticStore((state) => state.open); @@ -103,14 +106,14 @@ export function SidebarHelpMenu() { {({ hovered }) => ( )} @@ -181,14 +184,14 @@ export function SidebarHelpMenu() { } const styles = StyleSheet.create((theme) => ({ - trigger: { - width: 28, - height: 28, + trigger: (isCompact: boolean) => ({ + width: isCompact ? buttonControlHeight.md : buttonControlHeight.xs, + height: isCompact ? buttonControlHeight.md : buttonControlHeight.xs, alignItems: "center", justifyContent: "center", paddingVertical: theme.spacing[1], paddingHorizontal: theme.spacing[1], - }, + }), tooltipText: { fontSize: theme.fontSize.base, color: theme.colors.popoverForeground, diff --git a/packages/app/src/styles/theme.ts b/packages/app/src/styles/theme.ts index 04b1e8f6ae..d5c1678749 100644 --- a/packages/app/src/styles/theme.ts +++ b/packages/app/src/styles/theme.ts @@ -595,6 +595,7 @@ export const ICON_SIZE = { sm: 14, md: 16, lg: 20, + xl: 24, } as const; export const ICON_STROKE_WIDTH = 1.5;