fix: make the remove cross on a participant chip easier to tap

This commit is contained in:
Linus Rath
2026-10-31 20:31:23 +01:00
parent 3165b40127
commit 6a1c40e7eb
3 changed files with 27 additions and 3 deletions
+6 -3
View File
@@ -14,7 +14,10 @@ import { useContactsStore, type RecipientSuggestion } from '../../stores/contact
import { useLocaleStore } from '../../stores/locale-store';
import type { Attendee } from '../../lib/calendar-participants';
import { participantSuggestions, participantsFromSuggestion } from '../../lib/calendar-participant-suggestions';
import { participantRemoveAccessibilityLabel } from '../../lib/calendar-a11y';
import { participantRemoveAccessibilityLabel, touchSlop } from '../../lib/calendar-a11y';
/** The cross on a chip: 12 dp drawn, 44 dp to tap. */
const REMOVE_ICON = 12;
interface ParticipantInputProps {
attendees: Attendee[];
@@ -91,11 +94,11 @@ export function ParticipantInput({ attendees, onAdd, onRemove }: ParticipantInpu
</Text>
<Pressable
onPress={() => onRemove(a.email)}
hitSlop={6}
hitSlop={touchSlop(REMOVE_ICON)}
accessibilityRole="button"
accessibilityLabel={participantRemoveAccessibilityLabel(a, t)}
>
<X size={12} color={c.textMuted} />
<X size={REMOVE_ICON} color={c.textMuted} />
</Pressable>
</View>
))}
+13
View File
@@ -12,6 +12,7 @@ import {
eventAccessibilityLabel,
monthButtonAccessibilityLabel,
participantRemoveAccessibilityLabel,
touchSlop,
} from '../calendar-a11y';
import { useSettingsStore } from '../../stores/settings-store';
import { gregorianCalendar, jalaliCalendar } from '../calendar-system';
@@ -85,6 +86,18 @@ describe('header and chip buttons', () => {
expect(participantRemoveAccessibilityLabel({ name: '', email: 'ana@example.com' }, t)).toBe('Remove ana@example.com');
});
it('grows the 12 dp cross on a participant chip to a 44 dp touch target', async () => {
expect(touchSlop(12)).toBe(16);
expect(12 + 2 * touchSlop(12)).toBeGreaterThanOrEqual(44);
expect(touchSlop(13)).toBe(16);
expect(touchSlop(48)).toBe(0);
const fs = await import('node:fs');
const path = await import('node:path');
const chips = fs.readFileSync(path.resolve(__dirname, '../../components/calendar/ParticipantInput.tsx'), 'utf8');
expect(chips).toMatch(/const REMOVE_ICON = 12;/);
expect(chips).toMatch(/hitSlop=\{touchSlop\(REMOVE_ICON\)\}[\s\S]{0,200}<X size=\{REMOVE_ICON\}/);
});
it('is what the calendar header and the participant chips use', async () => {
const fs = await import('node:fs');
const path = await import('node:path');
+8
View File
@@ -21,6 +21,14 @@ export function monthButtonAccessibilityLabel(date: Date, system: CalendarSystem
return t('calendar.change_month_named', '{month}, change month', { month: monthTitle(date, system, locale, true) });
}
/** The smallest touch target, in dp (WCAG 2.5.5, Android's 48 dp guideline rounded to Apple's 44 pt). */
export const MIN_TOUCH_TARGET = 44;
/** The hitSlop that grows an icon of `size` dp to a `target` dp touch area on every side. */
export function touchSlop(size: number, target: number = MIN_TOUCH_TARGET): number {
return Math.max(0, Math.ceil((target - size) / 2));
}
/** "Remove Ana" for the cross on an invited participant's chip (webmail recipient_remove_named). */
export function participantRemoveAccessibilityLabel(attendee: { name?: string; email: string }, t: Translate): string {
return t('calendar.participants.remove_named', 'Remove {name}', { name: attendee.name?.trim() || attendee.email });