mirror of
https://github.com/bulwarkmail/native.git
synced 2026-11-01 00:07:01 +00:00
fix: give the contact, event and files fields a name for screen readers
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
// Text fields need a name a screen reader can say (file 11 "Form fields
|
||||
// have no name for screen readers"; field-names.test.ts covers Settings and
|
||||
// sign-in). The contact form, the event editor and the calendar, contacts
|
||||
// and files sheets had fields whose only name was their placeholder, or
|
||||
// their value (the custom reminder's "30"). Scanned so a bare field doesn't
|
||||
// creep back in.
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { readdirSync, readFileSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
|
||||
const SRC = join(__dirname, '..', '..');
|
||||
|
||||
function elementEnd(source: string, from: number): number {
|
||||
let depth = 0;
|
||||
for (let i = from; i < source.length; i++) {
|
||||
const ch = source[i];
|
||||
if (ch === '{') depth += 1;
|
||||
else if (ch === '}') depth -= 1;
|
||||
else if (depth === 0 && (ch === '>' || (ch === '/' && source[i + 1] === '>'))) return i;
|
||||
}
|
||||
return source.length;
|
||||
}
|
||||
|
||||
/** `file:line` of every TextInput element without an accessibilityLabel. */
|
||||
function unnamedFields(file: string): string[] {
|
||||
const source = readFileSync(join(SRC, file), 'utf8');
|
||||
const out: string[] = [];
|
||||
// `<TextInput>` as a type argument (useRef<TextInput>) is not an element.
|
||||
for (const m of source.matchAll(/<TextInput(?=\s)/g)) {
|
||||
const element = source.slice(m.index!, elementEnd(source, m.index! + m[0].length));
|
||||
if (/\baccessibilityLabel=/.test(element)) continue;
|
||||
out.push(`${file}:${source.slice(0, m.index!).split('\n').length}`);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const dir = (rel: string) => readdirSync(join(SRC, rel))
|
||||
.filter((name) => name.endsWith('.tsx'))
|
||||
.map((name) => `${rel}/${name}`);
|
||||
|
||||
const FILES = [
|
||||
...dir('components/calendar'),
|
||||
...dir('components/contacts'),
|
||||
...dir('components/files'),
|
||||
...dir('components/sharing'),
|
||||
'components/settings/VacationSettings.tsx',
|
||||
'screens/CalendarScreen.tsx',
|
||||
'screens/ContactFormScreen.tsx',
|
||||
'screens/ContactsScreen.tsx',
|
||||
'screens/ContactDetailScreen.tsx',
|
||||
'screens/FilesScreen.tsx',
|
||||
];
|
||||
|
||||
describe('field names in calendar, contacts and files', () => {
|
||||
it('every field has a name', () => {
|
||||
expect(FILES.flatMap(unnamedFields)).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -225,6 +225,7 @@ export function CalendarEditSheet({ visible, calendar, onSave, onClose }: Calend
|
||||
<ScrollView keyboardShouldPersistTaps="handled" contentContainerStyle={styles.body}>
|
||||
<Text style={styles.label}>{t('calendar.management.name', 'Name')}</Text>
|
||||
<TextInput
|
||||
accessibilityLabel={t('calendar.management.name', 'Name')}
|
||||
value={name}
|
||||
onChangeText={setName}
|
||||
placeholder={t('calendar.management.name_placeholder', 'Calendar name')}
|
||||
@@ -308,6 +309,7 @@ export function CalendarEditSheet({ visible, calendar, onSave, onClose }: Calend
|
||||
)}
|
||||
<Text style={styles.label}>{t('calendar.management.description', 'Description')}</Text>
|
||||
<TextInput
|
||||
accessibilityLabel={t('calendar.management.description', 'Description')}
|
||||
value={description}
|
||||
onChangeText={setDescription}
|
||||
placeholder={t('calendar.management.description_placeholder', 'Optional')}
|
||||
|
||||
@@ -558,6 +558,7 @@ export function EventModal({
|
||||
automaticallyAdjustKeyboardInsets
|
||||
>
|
||||
<TextInput
|
||||
accessibilityLabel={t('calendar.form.title', 'Title')}
|
||||
value={title}
|
||||
onChangeText={setTitle}
|
||||
placeholder={t('calendar.form.title', 'Title')}
|
||||
@@ -797,6 +798,7 @@ export function EventModal({
|
||||
{customReminderOpen && (
|
||||
<View style={styles.customReminderRow}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('calendar.alerts.amount', 'Reminder amount')}
|
||||
value={customAmount}
|
||||
onChangeText={(v) => setCustomAmount(v.replace(/[^0-9]/g, ''))}
|
||||
keyboardType="number-pad"
|
||||
@@ -834,6 +836,7 @@ export function EventModal({
|
||||
|
||||
<Section icon={<MapPin size={16} color={c.textMuted} />} label={t('calendar.form.location', 'Location')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('calendar.form.location', 'Location')}
|
||||
value={location}
|
||||
onChangeText={setLocation}
|
||||
placeholder={t('calendar.form.location_placeholder', 'Place or address')}
|
||||
@@ -844,6 +847,7 @@ export function EventModal({
|
||||
|
||||
<Section icon={<Video size={16} color={c.textMuted} />} label={t('calendar.form.meeting_link', 'Meeting link')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('calendar.form.meeting_link', 'Meeting link')}
|
||||
value={videoUrl}
|
||||
onChangeText={setVideoUrl}
|
||||
placeholder="https://meet.example.com/…"
|
||||
@@ -860,6 +864,7 @@ export function EventModal({
|
||||
label={t('calendar.form.description', 'Description')}
|
||||
>
|
||||
<TextInput
|
||||
accessibilityLabel={t('calendar.form.description', 'Description')}
|
||||
value={description}
|
||||
onChangeText={setDescription}
|
||||
placeholder={t('calendar.form.description_placeholder', 'Notes, agenda…')}
|
||||
|
||||
@@ -94,6 +94,7 @@ export function ParticipantInput({ attendees, onAdd, onRemove }: ParticipantInpu
|
||||
)}
|
||||
|
||||
<TextInput
|
||||
accessibilityLabel={t('calendar.event_modal.participant_placeholder', 'Add participant by email')}
|
||||
// No full-screen keyboard in landscape: it would hide the suggestions
|
||||
// and the text around the field.
|
||||
disableFullscreenUI
|
||||
|
||||
@@ -282,6 +282,7 @@ export function TasksSheet({
|
||||
|
||||
<View style={styles.addRow}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('calendar.tasks.title_placeholder', 'Task title')}
|
||||
value={editor.title}
|
||||
onChangeText={(v) => setEditor((e) => ({ ...e, title: v }))}
|
||||
placeholder={editor.id ? t('calendar.tasks.title_placeholder', 'Task title') : t('calendar.tasks.add_placeholder', 'Add a task…')}
|
||||
@@ -305,6 +306,7 @@ export function TasksSheet({
|
||||
{expanded && (
|
||||
<View style={styles.editor}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('calendar.tasks.description_placeholder', 'Notes')}
|
||||
value={editor.description}
|
||||
onChangeText={(v) => setEditor((e) => ({ ...e, description: v }))}
|
||||
placeholder={t('calendar.tasks.description_placeholder', 'Notes')}
|
||||
|
||||
@@ -128,6 +128,7 @@ export default function AddressBookPickerSheet({
|
||||
{creating ? (
|
||||
<View style={styles.createRow}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.address_books.name_label', 'Address book name')}
|
||||
style={styles.createInput}
|
||||
placeholder={t('contacts.address_books.name_label', 'Address book name')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
|
||||
@@ -140,6 +140,7 @@ export default function ContactPickerSheet({
|
||||
<View style={styles.searchBar}>
|
||||
<Search size={16} color={c.textMuted} />
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.search_placeholder', 'Search contacts...')}
|
||||
style={styles.searchInput}
|
||||
placeholder={t('contacts.search_placeholder', 'Search contacts...')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
|
||||
@@ -282,6 +282,7 @@ export default function ContactsSidebarDrawer({ visible, onClose }: Props) {
|
||||
<View key={kw.keyword} style={styles.renameRow}>
|
||||
<Tag size={16} color={c.textSecondary} />
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.rename_category', 'Rename category')}
|
||||
style={styles.renameInput}
|
||||
value={renameValue}
|
||||
onChangeText={setRenameValue}
|
||||
|
||||
@@ -94,6 +94,7 @@ export default function TagAssignSheet({
|
||||
|
||||
<View style={styles.inputRow}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.tag_name_placeholder', 'New tag name')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.tag_name_placeholder', 'New tag name')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
|
||||
@@ -274,6 +274,7 @@ export default function ShareSheet({ node, onClose, onChanged }: ShareSheetProps
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionLabel}>{t('files.share_add_people', 'Add people')}</Text>
|
||||
<TextInput
|
||||
accessibilityLabel={t('sharing.add_person', 'Add person or group')}
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
placeholder={t('files.share_search_placeholder', 'Search by name or email')}
|
||||
|
||||
@@ -331,6 +331,7 @@ export function VacationSettings() {
|
||||
description={t('settings.vacation.date_range.start_description', 'Leave empty for no start limit')}
|
||||
>
|
||||
<TextInput
|
||||
accessibilityLabel={t('settings.vacation.date_range.start', 'Start Date')}
|
||||
value={fromDate}
|
||||
onChangeText={setFromDate}
|
||||
placeholder="YYYY-MM-DD HH:MM"
|
||||
@@ -345,6 +346,7 @@ export function VacationSettings() {
|
||||
description={t('settings.vacation.date_range.end_description', 'Leave empty for no end limit')}
|
||||
>
|
||||
<TextInput
|
||||
accessibilityLabel={t('settings.vacation.date_range.end', 'End Date')}
|
||||
value={toDate}
|
||||
onChangeText={setToDate}
|
||||
placeholder="YYYY-MM-DD HH:MM"
|
||||
@@ -379,6 +381,7 @@ export function VacationSettings() {
|
||||
<Text style={styles.bodyLabel}>{t('settings.vacation.message.body_label', 'Message Body')}</Text>
|
||||
<Text style={styles.bodyDesc}>{t('settings.vacation.message.body_description', 'Plain text message content')}</Text>
|
||||
<TextInput
|
||||
accessibilityLabel={t('settings.vacation.message.body_label', 'Message Body')}
|
||||
value={body}
|
||||
onChangeText={setBody}
|
||||
placeholder={t('settings.vacation.message.body_placeholder', 'Thank you for your email. I am currently out of the office and will respond when I return.')}
|
||||
|
||||
@@ -236,6 +236,7 @@ export function ShareCollectionSheet<R extends object>({
|
||||
<View style={styles.section}>
|
||||
<Text style={styles.sectionLabel}>{t('calendar.share.add_people', 'Add people')}</Text>
|
||||
<TextInput
|
||||
accessibilityLabel={t('sharing.add_person', 'Add person or group')}
|
||||
value={search}
|
||||
onChangeText={setSearch}
|
||||
placeholder={t('calendar.share.search_placeholder', 'Search by name or email')}
|
||||
|
||||
@@ -538,6 +538,7 @@ export default function ContactFormScreen() {
|
||||
onRemove={() => updateForm('notes', form.notes.filter((_, idx) => idx !== i))}
|
||||
>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.note', 'Notes')}
|
||||
style={[styles.input, styles.multiline]}
|
||||
placeholder={t('contacts.form.note_placeholder', 'Add a note...')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -580,6 +581,7 @@ export default function ContactFormScreen() {
|
||||
</View>
|
||||
)}
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.categories', 'Categories')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.categories_placeholder_mobile', 'Add tag and press Enter')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -703,6 +705,7 @@ export default function ContactFormScreen() {
|
||||
<Section icon={<Users size={16} color={c.textMuted} />} label={t('contacts.group', 'Group')}>
|
||||
<Field label={t('contacts.groups.name_label', 'Group Name')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.groups.name_label', 'Group Name')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.groups.name_placeholder', 'e.g., Team, Family')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -815,6 +818,7 @@ export default function ContactFormScreen() {
|
||||
{form.isOrg ? (
|
||||
<Field label={t('contacts.form.organization', 'Organization')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.organization', 'Organization')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.organization_placeholder', 'Company name')}
|
||||
ref={orgRef}
|
||||
@@ -835,6 +839,7 @@ export default function ContactFormScreen() {
|
||||
<View style={styles.row2}>
|
||||
<Field label={t('contacts.form.prefix', 'Prefix')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.prefix', 'Prefix')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.prefix_placeholder', 'Dr., Mr., Mrs.')}
|
||||
ref={prefixRef}
|
||||
@@ -848,6 +853,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field label={t('contacts.form.suffix', 'Suffix')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.suffix', 'Suffix')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.suffix_placeholder', 'Jr., Sr., III')}
|
||||
ref={suffixRef}
|
||||
@@ -862,6 +868,7 @@ export default function ContactFormScreen() {
|
||||
</View>
|
||||
<Field label={t('contacts.form.given_name', 'First name')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.given_name', 'First name')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.given_name', 'First name')}
|
||||
ref={givenRef}
|
||||
@@ -876,6 +883,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field label={t('contacts.form.middle_name', 'Middle name')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.middle_name', 'Middle name')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.middle_name', 'Middle name')}
|
||||
ref={middleRef}
|
||||
@@ -889,6 +897,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field label={t('contacts.form.surname', 'Last name')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.surname', 'Last name')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.surname', 'Last name')}
|
||||
ref={surnameRef}
|
||||
@@ -905,6 +914,7 @@ export default function ContactFormScreen() {
|
||||
<Field key={i} label={i === 0 ? t('contacts.form.nickname', 'Nickname') : undefined}>
|
||||
<View style={styles.dateInputRow}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.nickname', 'Nickname')}
|
||||
style={[styles.input, { flex: 1 }]}
|
||||
placeholder={t('contacts.form.nickname_placeholder', 'Nickname')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -936,6 +946,7 @@ export default function ContactFormScreen() {
|
||||
)}
|
||||
<Field label={t('contacts.form.display_name_optional', 'Display name (optional)')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.display_name_optional', 'Display name (optional)')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.display_name_placeholder', 'Custom display name')}
|
||||
ref={fullRef}
|
||||
@@ -957,6 +968,7 @@ export default function ContactFormScreen() {
|
||||
onRemove={() => updateForm('emails', form.emails.filter((_, idx) => idx !== i))}
|
||||
>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.email', 'Email')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.email_placeholder', 'email@example.com')}
|
||||
ref={i === 0 ? firstEmailRef : undefined}
|
||||
@@ -996,6 +1008,7 @@ export default function ContactFormScreen() {
|
||||
onRemove={() => updateForm('phones', form.phones.filter((_, idx) => idx !== i))}
|
||||
>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.phone', 'Phone')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.phone_placeholder', '+1 234 567 890')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1048,6 +1061,7 @@ export default function ContactFormScreen() {
|
||||
{!(form.isOrg && i === 0) && (
|
||||
<Field label={t('contacts.form.organization', 'Organization')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.organization', 'Organization')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.organization_placeholder', 'Company name')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1062,6 +1076,7 @@ export default function ContactFormScreen() {
|
||||
)}
|
||||
<Field label={t('contacts.form.department', 'Department')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.department', 'Department')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.department_placeholder', 'Department')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1075,6 +1090,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field label={t('contacts.form.job_title', 'Job title')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.job_title', 'Job title')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.job_title_placeholder', 'e.g., Software Engineer')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1088,6 +1104,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field label={t('contacts.form.role', 'Role')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.role', 'Role')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.role_placeholder', 'e.g., Team Lead')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1120,6 +1137,7 @@ export default function ContactFormScreen() {
|
||||
onRemove={() => updateForm('addresses', form.addresses.filter((_, idx) => idx !== i))}
|
||||
>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.street', 'Street')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.street', 'Street')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1133,6 +1151,7 @@ export default function ContactFormScreen() {
|
||||
<View style={styles.row2}>
|
||||
<Field>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.city', 'City')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.city', 'City')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1146,6 +1165,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.region', 'State / Region')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.region', 'State / Region')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1161,6 +1181,7 @@ export default function ContactFormScreen() {
|
||||
<View style={styles.row2}>
|
||||
<Field>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.postcode', 'Postal code')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.postcode', 'Postal code')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1174,6 +1195,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.country', 'Country')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.country', 'Country')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1221,6 +1243,7 @@ export default function ContactFormScreen() {
|
||||
>
|
||||
<Field label={t('contacts.form.url', 'URL')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.url', 'URL')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.url_placeholder', 'https://...')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1236,6 +1259,7 @@ export default function ContactFormScreen() {
|
||||
<View style={styles.row2}>
|
||||
<Field label={t('contacts.form.service_placeholder', 'Service')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.service_placeholder', 'Service')}
|
||||
style={styles.input}
|
||||
placeholder="LinkedIn, Mastodon, …"
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1249,6 +1273,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field label={t('contacts.form.label', 'Label')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.label', 'Label')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.label_placeholder', 'Work, Personal, …')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1283,6 +1308,7 @@ export default function ContactFormScreen() {
|
||||
>
|
||||
<View style={styles.dateInputRow}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.anniversary_date', 'Date')}
|
||||
style={[
|
||||
styles.input,
|
||||
{ flex: 1 },
|
||||
@@ -1337,6 +1363,7 @@ export default function ContactFormScreen() {
|
||||
onRemove={() => updateForm('personalInfo', form.personalInfo.filter((_, idx) => idx !== i))}
|
||||
>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.personal_info', 'Personal info')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.personal_info_placeholder', 'e.g., Photography')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1397,6 +1424,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field label={t('contacts.form.pronouns', 'Pronouns')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.pronouns', 'Pronouns')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.pronouns_placeholder', 'they/them')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1416,6 +1444,7 @@ export default function ContactFormScreen() {
|
||||
>
|
||||
<Field label={t('contacts.form.calendar_uri', 'Calendar URL')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.calendar_uri', 'Calendar URL')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.url_placeholder', 'https://...')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1426,6 +1455,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field label={t('contacts.form.scheduling_uri', 'Scheduling URL')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.scheduling_uri', 'Scheduling URL')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.url_placeholder', 'https://...')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
@@ -1436,6 +1466,7 @@ export default function ContactFormScreen() {
|
||||
</Field>
|
||||
<Field label={t('contacts.form.freebusy_uri', 'Free/Busy URL')}>
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.form.freebusy_uri', 'Free/Busy URL')}
|
||||
style={styles.input}
|
||||
placeholder={t('contacts.form.url_placeholder', 'https://...')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
|
||||
@@ -530,6 +530,7 @@ export default function ContactsScreen({ onOpenContact, activeContactId = null }
|
||||
<View style={styles.searchBar}>
|
||||
<Search size={16} color={c.textMuted} />
|
||||
<TextInput
|
||||
accessibilityLabel={t('contacts.search_placeholder', 'Search contacts...')}
|
||||
style={styles.searchInput}
|
||||
placeholder={t('contacts.search_placeholder', 'Search contacts...')}
|
||||
placeholderTextColor={c.textMuted}
|
||||
|
||||
@@ -1092,6 +1092,7 @@ export default function FilesScreen() {
|
||||
<View style={styles.toolbarRow}>
|
||||
{searchOpen ? (
|
||||
<TextInput
|
||||
accessibilityLabel={t('files.search_placeholder', 'Search files...')}
|
||||
value={searchQuery}
|
||||
onChangeText={setSearchQuery}
|
||||
placeholder={t('files.search_placeholder', 'Search files...')}
|
||||
@@ -1748,6 +1749,7 @@ function PromptModal(props: PromptModalProps) {
|
||||
<View style={styles.dialog}>
|
||||
<Text style={styles.title}>{props.title}</Text>
|
||||
<TextInput
|
||||
accessibilityLabel={props.placeholder ?? props.title}
|
||||
value={props.value}
|
||||
onChangeText={props.onChange}
|
||||
placeholder={props.placeholder}
|
||||
|
||||
Reference in New Issue
Block a user