mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-02 06:33:05 +00:00
fix(app): enlarge compact sidebar footer tap targets (#6044)
* fix(app): enlarge compact sidebar footer tap targets The compact drawer reused 28px desktop buttons, making footer actions hard to tap. Use 44px targets with larger glyphs in compact layouts while preserving desktop geometry. * test(app): wait for compact footer tap stability
This commit is contained in:
@@ -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<void> {
|
||||
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<void> {
|
||||
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<void> {
|
||||
const button = page.getByRole("button", { name: "Help and support", exact: true });
|
||||
await button.tap({ position: { x: 40, y: 40 } });
|
||||
}
|
||||
|
||||
@@ -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<typeof useShortcutKeys>;
|
||||
theme: SidebarTheme;
|
||||
buttonRef?: RefObject<View | null>;
|
||||
}) {
|
||||
const isCompact = useIsCompactFormFactor();
|
||||
const iconSize = isCompact ? theme.iconSize.xl : theme.iconSize.md;
|
||||
|
||||
return (
|
||||
<Tooltip delayDuration={300}>
|
||||
<TooltipTrigger asChild>
|
||||
<Pressable
|
||||
ref={buttonRef}
|
||||
style={styles.footerIconButton}
|
||||
style={styles.footerIconButton(isCompact)}
|
||||
testID={testID}
|
||||
nativeID={testID}
|
||||
collapsable={false}
|
||||
@@ -305,7 +309,7 @@ function FooterIconButton({
|
||||
>
|
||||
{({ hovered }) => (
|
||||
<Icon
|
||||
size={iconSize ?? theme.iconSize.md}
|
||||
size={iconSize + iconSizeAdjustment}
|
||||
color={hovered ? theme.colors.foreground : theme.colors.foregroundMuted}
|
||||
/>
|
||||
)}
|
||||
@@ -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}
|
||||
/>
|
||||
</HostPicker>
|
||||
@@ -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",
|
||||
|
||||
@@ -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() {
|
||||
<TooltipTrigger asChild>
|
||||
<View>
|
||||
<DropdownMenuTrigger
|
||||
style={styles.trigger}
|
||||
style={styles.trigger(isCompact)}
|
||||
testID="sidebar-help"
|
||||
accessibilityRole="button"
|
||||
accessibilityLabel={t("sidebar.help.trigger")}
|
||||
>
|
||||
{({ hovered }) => (
|
||||
<ThemedCircleHelp
|
||||
size={ICON_SIZE.md}
|
||||
size={isCompact ? ICON_SIZE.xl : ICON_SIZE.md}
|
||||
uniProps={hovered ? foregroundColorMapping : foregroundMutedColorMapping}
|
||||
/>
|
||||
)}
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user