Phone hit: viewer headers and UIA stages (group 9) #135

Merged
void merged 2 commits from void/phone-hit-viewers into dev 2026-11-13 12:42:11 +00:00
8 changed files with 45 additions and 6 deletions

View file

@ -27,6 +27,7 @@ import { AsyncStatus } from '../../hooks/useAsyncCallback';
import { useZoom } from '../../hooks/useZoom';
import { createPage, usePdfDocumentLoader, usePdfJSLoader } from '../../plugins/pdfjs-dist';
import { stopPropagation } from '../../utils/keyboard';
import { PhoneHit, PhoneHitSpread24 } from '../../styles/PhoneHit.css';
export type PdfViewerProps = {
name: string;
@ -108,7 +109,7 @@ export const PdfViewer = as<'div', PdfViewerProps>(
<Box className={classNames(css.PdfViewer, className)} direction="Column" {...props} ref={ref}>
<Header className={css.PdfViewerHeader} size="400">
<Box grow="Yes" alignItems="Center" gap="200">
<IconButton size="300" radii="300" onClick={requestClose}>
<IconButton className={PhoneHit} size="300" radii="300" onClick={requestClose}>
<Icon size="50" src={Icons.ArrowLeft} />
</IconButton>
<Text size="T300" truncate>
@ -117,6 +118,7 @@ export const PdfViewer = as<'div', PdfViewerProps>(
</Box>
<Box shrink="No" alignItems="Center" gap="200">
<IconButton
className={PhoneHitSpread24}
variant={zoom < 1 ? 'Success' : 'SurfaceVariant'}
outlined={zoom < 1}
size="300"
@ -130,6 +132,7 @@ export const PdfViewer = as<'div', PdfViewerProps>(
<Text size="B300">{Math.round(zoom * 100)}%</Text>
</Chip>
<IconButton
className={PhoneHitSpread24}
variant={zoom > 1 ? 'Success' : 'SurfaceVariant'}
outlined={zoom > 1}
size="300"

View file

@ -13,6 +13,7 @@ import {
IconButton,
} from 'folds';
import FocusTrap from 'focus-trap-react';
import { PhoneHit } from '../styles/PhoneHit.css';
export type UIAFlowOverlayProps = {
currentStep: number;
@ -53,6 +54,7 @@ export function UIAFlowOverlay({
>
{(anchorRef) => (
<IconButton
className={PhoneHit}
ref={anchorRef}
variant="Critical"
size="300"

View file

@ -2,6 +2,7 @@ import React from 'react';
import classNames from 'classnames';
import { Box, Chip, Header, Icon, IconButton, Icons, Text, as } from 'folds';
import * as css from './ImageEditor.css';
import { PhoneHit } from '../../styles/PhoneHit.css';
export type ImageEditorProps = {
name: string;
@ -24,7 +25,7 @@ export const ImageEditor = as<'div', ImageEditorProps>(
>
<Header className={css.ImageEditorHeader} size="400">
<Box grow="Yes" alignItems="Center" gap="200">
<IconButton size="300" radii="300" onClick={requestClose}>
<IconButton className={PhoneHit} size="300" radii="300" onClick={requestClose}>
<Icon size="50" src={Icons.ArrowLeft} />
</IconButton>
<Text size="T300" truncate>

View file

@ -7,6 +7,7 @@ import * as css from './ImageViewer.css';
import { useZoom } from '../../hooks/useZoom';
import { usePan } from '../../hooks/usePan';
import { downloadMedia } from '../../utils/matrix';
import { PhoneHit, PhoneHitSpread24 } from '../../styles/PhoneHit.css';
export type ImageViewerProps = {
alt: string;
@ -33,7 +34,7 @@ export const ImageViewer = as<'div', ImageViewerProps>(
>
<Header className={css.ImageViewerHeader} size="400">
<Box grow="Yes" alignItems="Center" gap="200">
<IconButton size="300" radii="300" onClick={requestClose}>
<IconButton className={PhoneHit} size="300" radii="300" onClick={requestClose}>
<Icon size="50" src={Icons.ArrowLeft} />
</IconButton>
<Text size="T300" truncate>
@ -42,6 +43,7 @@ export const ImageViewer = as<'div', ImageViewerProps>(
</Box>
<Box shrink="No" alignItems="Center" gap="200">
<IconButton
className={PhoneHitSpread24}
variant={zoom < 1 ? 'Success' : 'SurfaceVariant'}
outlined={zoom < 1}
size="300"
@ -55,6 +57,7 @@ export const ImageViewer = as<'div', ImageViewerProps>(
<Text size="B300">{Math.round(zoom * 100)}%</Text>
</Chip>
<IconButton
className={PhoneHitSpread24}
variant={zoom > 1 ? 'Success' : 'SurfaceVariant'}
outlined={zoom > 1}
size="300"

View file

@ -5,6 +5,7 @@ import { Box, Chip, Header, Icon, IconButton, Icons, Scroll, Text, as } from 'fo
import { ErrorBoundary } from 'react-error-boundary';
import * as css from './TextViewer.css';
import { copyToClipboard } from '../../utils/dom';
import { PhoneHit } from '../../styles/PhoneHit.css';
const ReactPrism = lazy(() => import('../../plugins/react-prism/ReactPrism'));
@ -53,7 +54,7 @@ export const TextViewer = as<'div', TextViewerProps>(
>
<Header className={css.TextViewerHeader} size="400">
<Box grow="Yes" alignItems="Center" gap="200">
<IconButton size="300" radii="300" onClick={requestClose}>
<IconButton className={PhoneHit} size="300" radii="300" onClick={requestClose}>
<Icon size="50" src={Icons.ArrowLeft} />
</IconButton>
<Text size="T300" truncate>

View file

@ -4,6 +4,7 @@ import { AuthType } from 'matrix-js-sdk';
import { StageComponentProps } from './types';
import { ErrorCode } from '../../cs-errorcode';
import { PasswordInput } from '../password-input';
import { PhoneHit } from '../../styles/PhoneHit.css';
export function PasswordStage({
stageData,
@ -45,7 +46,7 @@ export function PasswordStage({
<Box grow="Yes">
<Text size="H4">Account Password</Text>
</Box>
<IconButton size="300" onClick={onCancel} radii="300">
<IconButton className={PhoneHit} size="300" onClick={onCancel} radii="300">
<Icon src={Icons.Cross} />
</IconButton>
</Header>

View file

@ -1,6 +1,7 @@
import { Box, Button, color, config, Dialog, Header, Icon, IconButton, Icons, Text } from 'folds';
import React, { useCallback, useEffect, useState } from 'react';
import { StageComponentProps } from './types';
import { PhoneHit } from '../../styles/PhoneHit.css';
export function SSOStage({
ssoRedirectURL,
@ -56,7 +57,7 @@ export function SSOStage({
<Box grow="Yes">
<Text size="H4">SSO Login</Text>
</Box>
<IconButton size="300" onClick={onCancel} radii="300">
<IconButton className={PhoneHit} size="300" onClick={onCancel} radii="300">
<Icon src={Icons.Cross} />
</IconButton>
</Header>

View file

@ -0,0 +1,27 @@
import { describe, it } from 'vitest';
import { expectPhoneHitClasses, expectSizeTie } from './phoneHitTestKit';
// Viewer headers and the UIA stages. The back/close button sits beside a title that is not tappable, so it is a
// plain PhoneHit; the zoom out/in buttons sit either side of a tappable zoom Chip, so they spread at their drawn
// 24 px (IconButton size 300 around a 16 px Icon).
// Not covered: the two scroll arrows of components/url-preview/UrlPreviewCard.tsx. They are position: absolute and
// PhoneHit sets position: relative on phones, which would pull them into the flow; they need a variant that keeps
// the position, which PhoneHit.css.ts does not have yet.
const files: [string, string[]][] = [
['components/uia-stages/PasswordStage.tsx', ['PhoneHit']],
['components/uia-stages/SSOStage.tsx', ['PhoneHit']],
['components/UIAFlowOverlay.tsx', ['PhoneHit']],
['components/text-viewer/TextViewer.tsx', ['PhoneHit']],
['components/image-editor/ImageEditor.tsx', ['PhoneHit']],
['components/image-viewer/ImageViewer.tsx', ['PhoneHit', 'PhoneHitSpread24', 'PhoneHitSpread24']],
['components/Pdf-viewer/PdfViewer.tsx', ['PhoneHit', 'PhoneHitSpread24', 'PhoneHitSpread24']],
];
describe.each(files)('phone hit area: %s', (file, want) => {
it('gives each icon button the variant of its surroundings', () => {
expectPhoneHitClasses(file, want);
});
it('ties each sized variant to the size the button draws', () => {
expectSizeTie(file);
});
});