Disable DatePicker auto-focus on mobile and add screen reader announcement#90448
Disable DatePicker auto-focus on mobile and add screen reader announcement#90448MelvinBot wants to merge 2 commits into
Conversation
…ement On mobile, the DatePicker auto-focus triggers the calendar modal to open immediately on screen load, violating WCAG 3.2.1. This disables auto-focus on small screens while preserving it on desktop. Additionally, when the date picker modal opens, screen readers now receive an announcement (e.g. "Date of birth, calendar opened") so assistive technology users know a new interactive context has appeared. Co-authored-by: truph01 <truph01@users.noreply.github.com>
Co-authored-by: truph01 <truph01@users.noreply.github.com>
|
Hey! I see that you made changes to our Form component. Make sure to update the docs in FORMS.md accordingly. Cheers! |
🦜 Polyglot Parrot! 🦜Squawk! Looks like you added some shiny new English strings. Allow me to parrot them back to you in other tongues: View the translation diffdiff --git a/src/languages/es.ts b/src/languages/es.ts
index 9b5b1b8e..95dbc1c6 100644
--- a/src/languages/es.ts
+++ b/src/languages/es.ts
@@ -1688,10 +1688,8 @@ const translations: TranslationDeepObject<typeof en> = {
backdropLabel: 'Fondo del Modal',
},
nextStep: {
- /* eslint-disable @typescript-eslint/no-unused-vars */
message: {
[CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_ADD_TRANSACTIONS]: (actor, actorType, _eta, _etaType) => {
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Esperando a que <strong>tú</strong> añadas gastos.`;
@@ -1702,7 +1700,6 @@ const translations: TranslationDeepObject<typeof en> = {
}
},
[CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_SUBMIT]: (actor, actorType, _eta, _etaType) => {
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Esperando a que <strong>tú</strong> envíes los gastos.`;
@@ -1714,7 +1711,6 @@ const translations: TranslationDeepObject<typeof en> = {
},
[CONST.NEXT_STEP.MESSAGE_KEY.NO_FURTHER_ACTION]: (_actor, _actorType, _eta, _etaType) => `¡No se requiere ninguna acción adicional!`,
[CONST.NEXT_STEP.MESSAGE_KEY.WAITING_FOR_SUBMITTER_ACCOUNT]: (actor, actorType, _eta, _etaType) => {
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Esperando a que <strong>tú</strong> añadas una cuenta bancaria.`;
@@ -1729,7 +1725,6 @@ const translations: TranslationDeepObject<typeof en> = {
if (eta) {
formattedETA = etaType === CONST.NEXT_STEP.ETA_TYPE.DATE_TIME ? ` el ${eta} de cada mes` : ` ${eta}`;
}
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Esperando a que tus gastos se envíen automáticamente${formattedETA}.`;
@@ -1740,7 +1735,6 @@ const translations: TranslationDeepObject<typeof en> = {
}
},
[CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_FIX_ISSUES]: (actor, actorType, _eta, _etaType) => {
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Esperando a que <strong>tú</strong> soluciones ellos problemas.`;
@@ -1751,7 +1745,6 @@ const translations: TranslationDeepObject<typeof en> = {
}
},
[CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_APPROVE]: (actor, actorType, _eta, _etaType) => {
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Esperando a que <strong>tú</strong> apruebes los gastos.`;
@@ -1762,7 +1755,6 @@ const translations: TranslationDeepObject<typeof en> = {
}
},
[CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_EXPORT]: (actor, actorType, _eta, _etaType) => {
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Esperando a que <strong>tú</strong> exportes este informe.`;
@@ -1773,7 +1765,6 @@ const translations: TranslationDeepObject<typeof en> = {
}
},
[CONST.NEXT_STEP.MESSAGE_KEY.WAITING_TO_PAY]: (actor, actorType, _eta, _etaType) => {
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Esperando a que <strong>tú</strong> pagues los gastos.`;
@@ -1784,7 +1775,6 @@ const translations: TranslationDeepObject<typeof en> = {
}
},
[CONST.NEXT_STEP.MESSAGE_KEY.WAITING_FOR_POLICY_BANK_ACCOUNT]: (actor, actorType, _eta, _etaType) => {
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Esperando a que <strong>tú</strong> termines de configurar una cuenta bancaria de empresa.`;
@@ -1804,7 +1794,6 @@ const translations: TranslationDeepObject<typeof en> = {
[CONST.NEXT_STEP.MESSAGE_KEY.SUBMITTING_TO_SELF]: (_actor, _actorType, _eta, _etaType) =>
`¡Ups! Parece que estás enviando el informe a <strong>ti mismo</strong>. Aprobar tus propios informes está <strong>prohibido</strong> por tu espacio de trabajo. Por favor, envía este informe a otra persona o contacta a tu administrador para cambiar la persona a la que lo envías.`,
[CONST.NEXT_STEP.MESSAGE_KEY.REJECTED_REPORT]: (actor, actorType) => {
- // eslint-disable-next-line default-case
switch (actorType) {
case CONST.NEXT_STEP.ACTOR_TYPE.CURRENT_USER:
return `Este informe fue rechazado. Esperando a que <strong>tú</strong> corrijas los problemas y lo vuelvas a enviar manualmente.`;
@@ -2459,7 +2448,6 @@ ${amount} para ${merchant} - ${date}`,
two: 'º',
few: 'º',
other: 'º',
- /* eslint-disable @typescript-eslint/naming-convention */
'1': 'Primero',
'2': 'Segundo',
'3': 'Tercero',
@@ -2470,7 +2458,6 @@ ${amount} para ${merchant} - ${date}`,
'8': 'Octavo',
'9': 'Noveno',
'10': 'Décimo',
- /* eslint-enable @typescript-eslint/naming-convention */
},
},
approverInMultipleWorkflows: 'Este miembro ya pertenece a otro flujo de aprobación. Cualquier actualización aquí se reflejará allí también.',
@@ -6933,7 +6920,6 @@ ${amount} para ${merchant} - ${date}`,
restrictedDescription: 'Sólo las personas en tu espacio de trabajo pueden encontrar esta sala',
privateDescription: 'Sólo las personas que están invitadas a esta sala pueden encontrarla',
publicDescription: 'Cualquier persona puede unirse a esta sala',
- // eslint-disable-next-line @typescript-eslint/naming-convention
public_announceDescription: 'Cualquier persona puede unirse a esta sala',
createRoom: 'Crea una sala de chat',
roomAlreadyExistsError: 'Ya existe una sala con este nombre',
@@ -6953,7 +6939,6 @@ ${amount} para ${merchant} - ${date}`,
restricted: 'Espacio de trabajo',
private: 'Privada',
public: 'Público',
- // eslint-disable-next-line @typescript-eslint/naming-convention
public_announce: 'Anuncio Público',
},
},
@@ -7268,7 +7253,6 @@ ${amount} para ${merchant} - ${date}`,
updatedDefaultTitle: (newDefaultTitle, oldDefaultTitle) => `cambió la fórmula personalizada del nombre del informe a "${newDefaultTitle}" (previamente "${oldDefaultTitle}")`,
updatedOwnership: (oldOwnerEmail, oldOwnerName, policyName) => `asumió la propiedad del espacio de trabajo ${policyName} de ${oldOwnerName} (${oldOwnerEmail})`,
updatedAutoHarvesting: (enabled) => `${enabled ? 'habilitó' : 'deshabilitó'} el envío programado`,
- // eslint-disable-next-line @typescript-eslint/max-params
updatedIndividualBudgetNotification: (
budgetAmount,
budgetFrequency,
diff --git a/src/languages/ja.ts b/src/languages/ja.ts
index c52c9cb6..50a22a29 100644
--- a/src/languages/ja.ts
+++ b/src/languages/ja.ts
@@ -207,7 +207,7 @@ const translations: TranslationDeepObject<typeof en> = {
admin: '管理者',
owner: 'オーナー',
dateFormat: 'YYYY-MM-DD',
- calendarOpened: 'カレンダーが開きました',
+ calendarOpened: 'カレンダーを開きました',
send: '送信',
na: '該当なし',
noResultsFound: '結果が見つかりません',
diff --git a/src/languages/nl.ts b/src/languages/nl.ts
index c7b29eaa..bb7a4d00 100644
--- a/src/languages/nl.ts
+++ b/src/languages/nl.ts
@@ -207,7 +207,7 @@ const translations: TranslationDeepObject<typeof en> = {
admin: 'Beheerder',
owner: 'Eigenaar',
dateFormat: 'JJJJ-MM-DD',
- calendarOpened: 'kalender geopend',
+ calendarOpened: 'agenda geopend',
send: 'Verzenden',
na: 'n.v.t.',
noResultsFound: 'Geen resultaten gevonden',
Note You can apply these changes to your branch by copying the patch to your clipboard, then running |
Codecov Report✅ Changes either increased or maintained existing code coverage, great job!
|
|
All CI checks have now completed and passed. The only failing check is PR Author Checklist, which requires manual action (checking off items in the PR description) — it's not a code issue and cannot be auto-fixed. Every other check passed: Jest tests (all 8 jobs), TypeScript, ESLint, Prettier, React Compiler Compliance, Storybook, perf-tests, builds, spellcheck, and generateTranslations. |
|
@MelvinBot Mark this PR as ready for review |
|
PR has been marked as ready for review as requested by |
|
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
Explanation of Change
Two accessibility improvements to the DatePicker component:
Disable auto-focus on mobile: On small screens (mobile), the
autoFocusprop causeduseAutoFocusInputto programmatically focus the DatePicker text input after screen transition, which firedonFocus→showDatePickerModal, opening the calendar without user interaction (violating WCAG 3.2.1). The auto-focus callback is now skipped whenisSmallScreenWidthis true.Screen reader announcement: When the date picker modal opens (
isModalVisiblebecomes true), a localized announcement (e.g. "Date of birth, calendar opened") is now made viauseAccessibilityAnnouncementon both native and web, so assistive technology users know a new interactive context has appeared.Fixed Issues
$ #77327
PROPOSAL: #77327 (comment)
Tests
// TODO: The human co-author must fill out the tests you ran before marking this PR as "ready for review"
// Please describe what tests you performed that validates your changed worked.
Offline tests
N/A — these changes affect focus behavior and screen reader announcements only; no network interaction.
QA Steps
// TODO: The human co-author must fill out the QA tests you ran before marking this PR as "ready for review".
// Please describe what QA needs to do to validate your changes and what areas do they need to test for regressions.
PR Author Checklist
### Fixed Issuessection aboveTestssectionOffline stepssectionQA stepssectiontoggleReportand notonIconClick)src/languages/*files and using the translation methodSTYLE.md) were followedAvatar, I verified the components usingAvatarare working as expected)StyleUtils.getBackgroundAndBorderStyle(theme.componentBG))npm run compress-svg)Avataris modified, I verified thatAvataris working as expected in all cases)Designlabel and/or tagged@Expensify/designso the design team can review the changes.ScrollViewcomponent to make it scrollable when more elements are added to the page.mainbranch was merged into this PR after a review, I tested again and verified the outcome was still expected according to theTeststeps.Screenshots/Videos
Android: Native
Android: mWeb Chrome
iOS: Native
iOS: mWeb Safari
MacOS: Chrome / Safari