diff --git a/components/email/__tests__/more-actions-menu.test.tsx b/components/email/__tests__/more-actions-menu.test.tsx index 1dd50c78f..33bb3d859 100644 --- a/components/email/__tests__/more-actions-menu.test.tsx +++ b/components/email/__tests__/more-actions-menu.test.tsx @@ -200,3 +200,14 @@ describe('phone More panel parity', () => { expect(within(menu).queryByRole('menuitem', { name: /keyboard_shortcuts/ })).toBeNull(); }); }); +describe('phone touch targets (MAIL-18)', () => { + it('draws the panel close at 44px and gives the subject star the touch hit area', () => { + renderViewer({ onToggleStar: vi.fn() }); + const close = screen.getByTestId('more-panel-close'); + expect(close.className).toContain('h-11'); + expect(close.className).toContain('w-11'); + const star = screen.getByTestId('subject-star'); + expect(star).toHaveAccessibleName('tooltips.star'); + expect(star.className).toContain('pointer-coarse:after:min-h-11'); + }); +}); diff --git a/components/email/email-viewer.tsx b/components/email/email-viewer.tsx index 889cf769a..9f1c29397 100644 --- a/components/email/email-viewer.tsx +++ b/components/email/email-viewer.tsx @@ -4562,16 +4562,16 @@ export function EmailViewer({ ) : ( {t('more_actions')} )} - +
{moreMenuSub === null && ( @@ -4808,16 +4808,18 @@ export function EmailViewer({ {/* Star inline with subject (top toolbar mode). Mountain View has it on the sender's line instead. */} {toolbarPosition === 'top' && !mvViewer && ( - + )} {isImportant && ( diff --git a/components/email/move-target-list.tsx b/components/email/move-target-list.tsx index b1b0932a3..cc99987a4 100644 --- a/components/email/move-target-list.tsx +++ b/components/email/move-target-list.tsx @@ -44,7 +44,7 @@ export function MoveTargetList({ tree, targetIds, onPick, variant = "menu" }: Mo const label = localizeMailboxName(node.role, node.name, (key) => tSidebar(`mailboxes.${key}`)); const rowClass = panel ? "w-full pe-4 py-2.5 min-h-[44px] text-sm text-start flex items-center gap-3" - : "w-full pe-3 py-1.5 text-sm text-start flex items-center gap-2"; + : "w-full pe-3 py-1.5 text-sm text-start flex items-center gap-2 pointer-coarse:min-h-11"; const icon = ; return (
diff --git a/components/email/tag-picker.tsx b/components/email/tag-picker.tsx index e367ebce6..253cfe65e 100644 --- a/components/email/tag-picker.tsx +++ b/components/email/tag-picker.tsx @@ -101,7 +101,7 @@ export function TagPicker({ const rowClass = cn( "w-full text-start flex items-center gap-2 hover:bg-muted cursor-pointer", - touch ? "px-4 py-2.5 min-h-[44px] text-sm gap-3" : "px-3 py-1.5 text-sm", + touch ? "px-4 py-2.5 min-h-[44px] text-sm gap-3" : "px-3 py-1.5 text-sm pointer-coarse:min-h-11", ); const dotClass = touch ? "w-3.5 h-3.5" : "w-3 h-3"; const checkClass = touch ? "w-4 h-4" : "w-3.5 h-3.5"; diff --git a/components/ui/__tests__/context-menu-touch.test.tsx b/components/ui/__tests__/context-menu-touch.test.tsx new file mode 100644 index 000000000..2a518c982 --- /dev/null +++ b/components/ui/__tests__/context-menu-touch.test.tsx @@ -0,0 +1,25 @@ +import { render, screen } from '@testing-library/react'; +import { describe, it, expect } from 'vitest'; +import { ContextMenu, ContextMenuSubMenu, ContextMenuItem } from '../context-menu'; + +// jsdom can't match `pointer: coarse`, so this checks the rule is on the rows: +// 44px on a touch screen (R7), the desktop's own height elsewhere. +describe('ContextMenu rows on a touch screen', () => { + it('gives items and submenu entries a 44px minimum height under a coarse pointer only', () => { + render( + {}}> + {}} /> + + {}} /> + + , + ); + const item = screen.getByRole('menuitem', { name: 'Reply' }); + const sub = screen.getByTestId('sub'); + for (const row of [item, sub]) { + expect(row.className).toContain('pointer-coarse:min-h-11'); + expect(row.className).toContain('py-1.5'); + expect(row.className).not.toMatch(/(^|\s)min-h-11/); + } + }); +}); diff --git a/components/ui/context-menu.tsx b/components/ui/context-menu.tsx index 053ab1c3b..26b09ef97 100644 --- a/components/ui/context-menu.tsx +++ b/components/ui/context-menu.tsx @@ -512,7 +512,8 @@ export function ContextMenuItem({ disabled={disabled} title={title} className={cn( - "w-full px-3 py-1.5 text-sm text-start flex items-center gap-2", + // 44px rows on a touch screen (R7); the desktop keeps its density. + "w-full px-3 py-1.5 text-sm text-start flex items-center gap-2 pointer-coarse:min-h-11", "transition-colors duration-150", "focus:outline-none focus-visible:bg-muted", disabled && "opacity-50 cursor-not-allowed", @@ -637,7 +638,7 @@ export function ContextMenuSubMenu({ ref={triggerRef} data-submenu-trigger="" className={cn( - "w-full px-3 py-1.5 text-sm flex items-center gap-2 outline-none", + "w-full px-3 py-1.5 text-sm flex items-center gap-2 outline-none pointer-coarse:min-h-11", "transition-colors duration-150", disabled ? "opacity-50 cursor-not-allowed" : "cursor-pointer hover:bg-muted focus-visible:bg-muted", isOpen && !disabled && "bg-muted"