fix: name the month button by its month and the participant remove button by the person

This commit is contained in:
Linus Rath
2026-10-31 13:32:21 +01:00
parent 11fa0365f9
commit 8e0b447d8f
6 changed files with 52 additions and 6 deletions
+4 -2
View File
@@ -93,7 +93,8 @@
},
"occurrence_not_found_again": "Die Änderung wurde nicht gespeichert: Der Termin konnte auf dem Server nicht wiedergefunden werden. Versuchen Sie es erneut.",
"participants": {
"limit_warning": "Mehr als {max, plural, one {# Teilnehmer} other {# Teilnehmer}}, den Organisator mitgezählt: Der Server lehnt das Speichern dieses Termins möglicherweise ab. Ihr Administrator kann das Limit erhöhen."
"limit_warning": "Mehr als {max, plural, one {# Teilnehmer} other {# Teilnehmer}}, den Organisator mitgezählt: Der Server lehnt das Speichern dieses Termins möglicherweise ab. Ihr Administrator kann das Limit erhöhen.",
"remove_named": "{name} entfernen"
},
"recurrence": {
"editor_occurrences_count": "{count, plural, one {Termin} other {Terminen}}",
@@ -148,7 +149,8 @@
},
"detail": {
"delete_confirm": "Dieses Ereignis löschen?"
}
},
"change_month_named": "{month}, Monat wechseln"
},
"common": {
"add": "Hinzufügen",
+3 -1
View File
@@ -13,6 +13,7 @@
"alerts": {
"minutes_after": "{count, plural, one {# minute after} other {# minutes after}}"
},
"change_month_named": "{month}, change month",
"colors": {
"indigo": "Indigo",
"teal": "Teal"
@@ -116,7 +117,8 @@
"occurrence_not_found_again": "The change was not saved: the occurrence could not be found again on the server. Please try again.",
"participants": {
"group_members": "{count, plural, one {# member} other {# members}}",
"limit_warning": "More than {max, plural, one {# participant} other {# participants}}, including the organizer: the server may refuse to save this event. An administrator can raise the limit."
"limit_warning": "More than {max, plural, one {# participant} other {# participants}}, including the organizer: the server may refuse to save this event. An administrator can raise the limit.",
"remove_named": "Remove {name}"
},
"recurrence": {
"editor_occurrences_count": "{count, plural, one {occurrence} other {occurrences}}",
+2 -1
View File
@@ -14,6 +14,7 @@ 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';
interface ParticipantInputProps {
attendees: Attendee[];
@@ -92,7 +93,7 @@ export function ParticipantInput({ attendees, onAdd, onRemove }: ParticipantInpu
onPress={() => onRemove(a.email)}
hitSlop={6}
accessibilityRole="button"
accessibilityLabel={t('calendar.participants.remove', 'Remove')}
accessibilityLabel={participantRemoveAccessibilityLabel(a, t)}
>
<X size={12} color={c.textMuted} />
</Pressable>
+27 -1
View File
@@ -7,7 +7,12 @@ vi.hoisted(() => {
import { enUS } from 'date-fns/locale';
import type { CalendarEvent } from '../../api/types';
import { formatMessage } from '../../i18n/format';
import { dayCellAccessibilityLabel, eventAccessibilityLabel } from '../calendar-a11y';
import {
dayCellAccessibilityLabel,
eventAccessibilityLabel,
monthButtonAccessibilityLabel,
participantRemoveAccessibilityLabel,
} from '../calendar-a11y';
import { useSettingsStore } from '../../stores/settings-store';
const t = (_key: string, fallback?: string, params?: Record<string, string | number>) =>
@@ -46,3 +51,24 @@ describe('eventAccessibilityLabel', () => {
.toMatch(/^\(No title\), Monday, Nov(ember)? 2, 2026, 9:00 AM – 9:15 AM, Cancelled$/);
});
});
describe('header and chip buttons', () => {
it('names the month button by the month it shows, not "Click to change month"', () => {
expect(monthButtonAccessibilityLabel('October 2026', t)).toBe('October 2026, change month');
});
it('names the cross on a participant chip by the person it removes', () => {
expect(participantRemoveAccessibilityLabel({ name: 'Ana Lima', email: 'ana@example.com' }, t)).toBe('Remove Ana Lima');
expect(participantRemoveAccessibilityLabel({ name: '', email: 'ana@example.com' }, t)).toBe('Remove ana@example.com');
});
it('is what the calendar header and the participant chips use', async () => {
const fs = await import('node:fs');
const path = await import('node:path');
const screen = fs.readFileSync(path.resolve(__dirname, '../../screens/CalendarScreen.tsx'), 'utf8');
const chips = fs.readFileSync(path.resolve(__dirname, '../../components/calendar/ParticipantInput.tsx'), 'utf8');
expect(screen).toMatch(/accessibilityLabel=\{monthButtonAccessibilityLabel\(/);
expect(screen).not.toMatch(/mini_calendar_change/);
expect(chips).toMatch(/accessibilityLabel=\{participantRemoveAccessibilityLabel\(a, t\)\}/);
});
});
+14
View File
@@ -11,6 +11,20 @@ type Translate = (key: string, fallback?: string, params?: Record<string, string
// 19e0acde9): a day cell by its full date and how many events it has, an
// event by its title, date, time, calendar and state.
/**
* "October 2026, change month" for the header button that opens the month
* panel: the month shown first, then what the button does. Not the
* webmail's "Click to change month" (calendar.mini_calendar_change).
*/
export function monthButtonAccessibilityLabel(title: string, t: Translate): string {
return t('calendar.change_month_named', '{month}, change month', { month: title });
}
/** "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 });
}
/** "Monday, November 2, 2026, 3 events" for a month day cell. */
export function dayCellAccessibilityLabel(day: Date, eventCount: number, t: Translate, locale: Locale): string {
const date = formatCalendarDate(day, locale.code, 'longWeekdayMonthDayYear');
+2 -1
View File
@@ -12,6 +12,7 @@ import {
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context';
import { useFocusEffect } from '@react-navigation/native';
import { monthButtonAccessibilityLabel } from '../lib/calendar-a11y';
import { TASK_CHIP_ID_PREFIX, multiDayCount } from '../lib/calendar-time-grid';
import {
Calendar as CalendarIcon,
@@ -1037,7 +1038,7 @@ export default function CalendarScreen() {
style={styles.titleBtn}
hitSlop={6}
accessibilityRole="button"
accessibilityLabel={t('calendar.mini_calendar_change', 'Change month')}
accessibilityLabel={monthButtonAccessibilityLabel(headerTitle(titleDate, locale), t)}
accessibilityState={{ expanded: monthPanelOpen }}
>
<Text style={styles.headerTitle} numberOfLines={1}>