diff --git a/src/CONST/index.ts b/src/CONST/index.ts index e8d303b1b8e1..f3de00a035d2 100755 --- a/src/CONST/index.ts +++ b/src/CONST/index.ts @@ -5730,6 +5730,8 @@ const CONST = { LINK: 'link', /** Use to identify a list of items. */ LIST: 'list', + /** Use for individual items within a list. */ + LISTITEM: 'listitem', /** Use for a list of choices or options. */ MENU: 'menu', /** Use for a container of multiple menus. */ @@ -5770,6 +5772,14 @@ const CONST = { NAVIGATION: 'navigation', /** Use for Tooltips */ TOOLTIP: 'tooltip', + /** Use for data table containers. */ + TABLE: 'table', + /** Use for table rows. */ + ROW: 'row', + /** Use for column header cells in a table. */ + COLUMNHEADER: 'columnheader', + /** Use for data cells in a table row. */ + CELL: 'cell', }, TRANSLATION_KEYS: { ATTACHMENT: 'common.attachment', diff --git a/src/components/FormHelpMessage.tsx b/src/components/FormHelpMessage.tsx index cbd8ddbc5892..7cacdbe459a4 100644 --- a/src/components/FormHelpMessage.tsx +++ b/src/components/FormHelpMessage.tsx @@ -32,9 +32,21 @@ type FormHelpMessageProps = { /** Whether to show information icon */ isInfo?: boolean; + + /** Native ID for accessibility association (aria-describedby) */ + nativeID?: string; }; -function FormHelpMessage({message = '', children, isError = true, style, shouldShowRedDotIndicator = true, shouldRenderMessageAsHTML = false, isInfo = false}: FormHelpMessageProps) { +function FormHelpMessage({ + message = '', + children, + isError = true, + style, + shouldShowRedDotIndicator = true, + shouldRenderMessageAsHTML = false, + isInfo = false, + nativeID, +}: FormHelpMessageProps) { const theme = useTheme(); const styles = useThemeStyles(); const icons = useMemoizedLazyExpensifyIcons(['DotIndicator', 'Exclamation']); @@ -60,7 +72,10 @@ function FormHelpMessage({message = '', children, isError = true, style, shouldS } return ( - + {isError && shouldShowRedDotIndicator && ( {!!suffixCharacter && ( @@ -560,6 +562,7 @@ function BaseTextInput({ {!!inputHelpText && ( diff --git a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx index 93bc886fa2ed..57fbe34444b7 100644 --- a/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx +++ b/src/components/ValidateCodeActionModal/ValidateCodeForm/BaseValidateCodeForm.tsx @@ -1,7 +1,7 @@ import {useFocusEffect} from '@react-navigation/native'; import type {ForwardedRef} from 'react'; import React, {useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState} from 'react'; -import {View} from 'react-native'; +import {AccessibilityInfo, View} from 'react-native'; import type {StyleProp, ViewStyle} from 'react-native'; import Button from '@components/Button'; import DotIndicatorMessage from '@components/DotIndicatorMessage'; @@ -198,6 +198,13 @@ function BaseValidateCodeForm({ countdownRef.current?.resetCountdown(); }, [isCountdownRunning]); + useEffect(() => { + if (!validateCodeSent) { + return; + } + AccessibilityInfo.announceForAccessibility(translate('validateCodeModal.successfulNewCodeRequest')); + }, [validateCodeSent, translate]); + useEffect(() => { if (!validateCodeSent) { return; @@ -331,14 +338,16 @@ function BaseValidateCodeForm({ )} - {!!validateCodeSent && ( - - )} + + {!!validateCodeSent && ( + + )} + , 'personal'>; @@ -56,30 +57,38 @@ function SubscriptionPlanCard({subscriptionPlan, isFromComparisonModal = false, const benefitsColumns = shouldUseNarrowLayout || isFromComparisonModal ? 1 : 2; const renderBenefits = () => { - const amountOfRows = Math.ceil(benefits.length / benefitsColumns); - - return Array.from({length: amountOfRows}).map((_, rowIndex) => ( + return ( - {benefits.slice(rowIndex * benefitsColumns, (rowIndex + 1) * benefitsColumns).map((item) => ( - - - {item} - - ))} + {benefits.map((item, index) => { + const {accessible, accessibilityLabel} = getSubscriptionPlanBenefitA11yProps({benefitText: item, index, totalBenefits: benefits.length, ofLabel: translate('common.of')}); + return ( + + + + + {item} + + ); + })} - )); + ); }; const shouldHideSubscriptionSettingsButton = diff --git a/src/pages/settings/Subscription/SubscriptionPlan/getSubscriptionPlanBenefitA11yProps/index.native.ts b/src/pages/settings/Subscription/SubscriptionPlan/getSubscriptionPlanBenefitA11yProps/index.native.ts new file mode 100644 index 000000000000..11ec7ba82a5a --- /dev/null +++ b/src/pages/settings/Subscription/SubscriptionPlan/getSubscriptionPlanBenefitA11yProps/index.native.ts @@ -0,0 +1,8 @@ +import type {GetSubscriptionPlanBenefitA11yProps} from './types'; + +const getSubscriptionPlanBenefitA11yProps: GetSubscriptionPlanBenefitA11yProps = (params) => ({ + accessible: true, + accessibilityLabel: `${params?.benefitText}, ${(params?.index ?? 0) + 1} ${params?.ofLabel} ${params?.totalBenefits}`, +}); + +export default getSubscriptionPlanBenefitA11yProps; diff --git a/src/pages/settings/Subscription/SubscriptionPlan/getSubscriptionPlanBenefitA11yProps/index.ts b/src/pages/settings/Subscription/SubscriptionPlan/getSubscriptionPlanBenefitA11yProps/index.ts new file mode 100644 index 000000000000..8c750a04b26b --- /dev/null +++ b/src/pages/settings/Subscription/SubscriptionPlan/getSubscriptionPlanBenefitA11yProps/index.ts @@ -0,0 +1,5 @@ +import type {GetSubscriptionPlanBenefitA11yProps} from './types'; + +const getSubscriptionPlanBenefitA11yProps: GetSubscriptionPlanBenefitA11yProps = () => ({}); + +export default getSubscriptionPlanBenefitA11yProps; diff --git a/src/pages/settings/Subscription/SubscriptionPlan/getSubscriptionPlanBenefitA11yProps/types.ts b/src/pages/settings/Subscription/SubscriptionPlan/getSubscriptionPlanBenefitA11yProps/types.ts new file mode 100644 index 000000000000..b7df3daa04e4 --- /dev/null +++ b/src/pages/settings/Subscription/SubscriptionPlan/getSubscriptionPlanBenefitA11yProps/types.ts @@ -0,0 +1,15 @@ +type GetSubscriptionPlanBenefitA11yPropsParams = { + benefitText: string; + index: number; + totalBenefits: number; + ofLabel: string; +}; + +type SubscriptionPlanBenefitA11yProps = { + accessible?: boolean; + accessibilityLabel?: string; +}; + +type GetSubscriptionPlanBenefitA11yProps = (params?: GetSubscriptionPlanBenefitA11yPropsParams) => SubscriptionPlanBenefitA11yProps; + +export type {GetSubscriptionPlanBenefitA11yPropsParams, SubscriptionPlanBenefitA11yProps, GetSubscriptionPlanBenefitA11yProps}; diff --git a/src/pages/workspace/WorkspacesListPage.tsx b/src/pages/workspace/WorkspacesListPage.tsx index 7161260b1ad4..f7a1d948f2fd 100755 --- a/src/pages/workspace/WorkspacesListPage.tsx +++ b/src/pages/workspace/WorkspacesListPage.tsx @@ -462,7 +462,7 @@ function WorkspacesListPage() { shouldHideOnDelete={false} > )} {!isLessThanMediumScreen && filteredWorkspaces.length > 0 && ( - - + + - + - + - + )} @@ -810,20 +825,26 @@ function WorkspacesListPage() { ) : ( - { - flatlistRef.current?.scrollToOffset({ - offset: info.averageItemLength * info.index, - animated: true, - }); - }} - renderItem={renderItem} - ListHeaderComponent={listHeaderComponent} - keyboardShouldPersistTaps="handled" - contentContainerStyle={styles.pb20} - /> + + { + flatlistRef.current?.scrollToOffset({ + offset: info.averageItemLength * info.index, + animated: true, + }); + }} + renderItem={renderItem} + ListHeaderComponent={listHeaderComponent} + keyboardShouldPersistTaps="handled" + contentContainerStyle={styles.pb20} + /> + )} - + {isNarrow && ThreeDotMenuOrPendingIcon} - + {!!ownerDetails && ( <> )} - + - + {!isNarrow && ThreeDotMenuOrPendingIcon} {!isNarrow && (