diff --git a/src/components/compose/RecipientSuggestionList.tsx b/src/components/compose/RecipientSuggestionList.tsx index 972fc93b..5d0931dc 100644 --- a/src/components/compose/RecipientSuggestionList.tsx +++ b/src/components/compose/RecipientSuggestionList.tsx @@ -6,6 +6,7 @@ import { radius, spacing, typography, type ThemePalette } from '../../theme/toke import { useLocaleStore } from '../../stores/locale-store'; import { getEmailInitials } from '../../lib/avatar-utils'; import { SUGGESTION_ROW_HEIGHT } from '../../lib/recipient-picker'; +import { recipientRemoveAccessibilityLabel } from '../../lib/recipient-chip-a11y'; import type { RecipientSuggestion } from '../../stores/contacts-store'; /** A recipient chip's data. Group chips carry their resolved members and no email. */ @@ -43,7 +44,7 @@ export function RecipientChip({ // A 12 px icon: the touch area reaches about 40 dp around it. hitSlop={14} accessibilityRole="button" - accessibilityLabel={t('email_composer.recipient_remove', 'Remove')} + accessibilityLabel={recipientRemoveAccessibilityLabel(recipient, t)} > diff --git a/src/lib/__tests__/recipient-chip-a11y.test.ts b/src/lib/__tests__/recipient-chip-a11y.test.ts new file mode 100644 index 00000000..8c3678f6 --- /dev/null +++ b/src/lib/__tests__/recipient-chip-a11y.test.ts @@ -0,0 +1,27 @@ +// The cross on a composer recipient chip said only "Remove". +import { describe, expect, it, vi } from 'vitest'; +import fs from 'node:fs'; +import path from 'node:path'; + +vi.mock('expo-localization', () => ({ getLocales: () => [] })); + +import { translate } from '../../i18n'; +import { recipientRemoveAccessibilityLabel } from '../recipient-chip-a11y'; + +const t = (key: string, fallback?: string, params?: Record) => + translate('en', key, fallback ?? key, params); + +describe('recipient chip remove label', () => { + it('names the recipient', () => { + expect(recipientRemoveAccessibilityLabel({ name: 'Anna Berg', email: 'anna@example.org' }, t)).toBe('Remove Anna Berg'); + }); + + it('falls back to the address when the chip has no name', () => { + expect(recipientRemoveAccessibilityLabel({ name: ' ', email: 'anna@example.org' }, t)).toBe('Remove anna@example.org'); + }); + + it('is what the chip cross uses', () => { + const source = fs.readFileSync(path.resolve(__dirname, '../../components/compose/RecipientSuggestionList.tsx'), 'utf8'); + expect(source).toMatch(/accessibilityLabel=\{recipientRemoveAccessibilityLabel\(/); + }); +}); diff --git a/src/lib/recipient-chip-a11y.ts b/src/lib/recipient-chip-a11y.ts new file mode 100644 index 00000000..266a94b4 --- /dev/null +++ b/src/lib/recipient-chip-a11y.ts @@ -0,0 +1,16 @@ +// What a screen reader says for the cross on a To/Cc/Bcc chip. It said only +// "Remove", so with several chips nobody could tell which one a cross took +// away. The webmail names the recipient (email-composer.tsx, +// recipient_remove_named), as the event editor here already does. + +type Translate = (key: string, fallback?: string, params?: Record) => string; + +/** "Remove Anna Berg", or "Remove anna@example.org" when the chip has no name. */ +export function recipientRemoveAccessibilityLabel( + recipient: { name?: string; email: string }, + t: Translate, +): string { + const name = recipient.name?.trim() || recipient.email.trim(); + if (!name) return t('email_composer.recipient_remove', 'Remove'); + return t('email_composer.recipient_remove_named', 'Remove {name}', { name }); +}