Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/CONST/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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. */
Expand Down Expand Up @@ -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',
Expand Down
19 changes: 17 additions & 2 deletions src/components/FormHelpMessage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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']);
Expand All @@ -60,7 +72,10 @@ function FormHelpMessage({message = '', children, isError = true, style, shouldS
}

return (
<View style={[styles.flexRow, styles.alignItemsCenter, styles.mt2, styles.mb1, style]}>
<View
style={[styles.flexRow, styles.alignItemsCenter, styles.mt2, styles.mb1, style]}
nativeID={nativeID}
>
{isError && shouldShowRedDotIndicator && (
<View
accessible
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ function BaseTextInput({
// eslint-disable-next-line
{...inputProps}
accessibilityLabel={inputProps.accessibilityLabel ?? accessibilityLabel}
accessibilityHint={errorText || inputProps.accessibilityHint}
autoCorrect={inputProps.secureTextEntry ? false : autoCorrect}
placeholder={placeholderValue}
placeholderTextColor={placeholderTextColor ?? theme.placeholderText}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Str} from 'expensify-common';
import type {RefObject} from 'react';
import React, {useCallback, useEffect, useRef, useState} from 'react';
import React, {useCallback, useEffect, useId, useRef, useState} from 'react';
import type {BlurEvent, FocusEvent, GestureResponderEvent, LayoutChangeEvent, StyleProp, TextInput, ViewStyle} from 'react-native';
import {StyleSheet, View} from 'react-native';
import {Easing, useSharedValue, withTiming} from 'react-native-reanimated';
Expand Down Expand Up @@ -91,6 +91,7 @@ function BaseTextInput({
const InputComponent = InputComponentMap.get(type) ?? RNTextInput;
const isMarkdownEnabled = type === 'markdown';
const isAutoGrowHeightMarkdown = isMarkdownEnabled && autoGrowHeight;
const helpMessageId = useId();

const theme = useTheme();
const styles = useThemeStyles();
Expand Down Expand Up @@ -488,6 +489,7 @@ function BaseTextInput({
defaultValue={defaultValue}
markdownStyle={markdownStyle}
accessibilityLabel={inputProps.accessibilityLabel}
aria-describedby={inputHelpText ? helpMessageId : undefined}
/>
{!!suffixCharacter && (
<View style={[styles.textInputSuffixWrapper, suffixContainerStyle]}>
Expand Down Expand Up @@ -560,6 +562,7 @@ function BaseTextInput({
</PressableWithoutFeedback>
{!!inputHelpText && (
<FormHelpMessage
nativeID={helpMessageId}
isError={!!errorText}
message={inputHelpText}
/>
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -331,14 +338,16 @@ function BaseValidateCodeForm({
</View>
)}
</OfflineWithFeedback>
{!!validateCodeSent && (
<DotIndicatorMessage
type="success"
style={[styles.mt6, styles.flex0]}
// eslint-disable-next-line @typescript-eslint/naming-convention
messages={{0: translate('validateCodeModal.successfulNewCodeRequest')}}
/>
)}
<View accessibilityLiveRegion="polite">
{!!validateCodeSent && (
<DotIndicatorMessage
type="success"
style={[styles.mt6, styles.flex0]}
// eslint-disable-next-line @typescript-eslint/naming-convention
messages={{0: translate('validateCodeModal.successfulNewCodeRequest')}}
/>
)}
</View>

<OfflineWithFeedback
shouldDisplayErrorAbove
Expand Down
1 change: 1 addition & 0 deletions src/libs/actions/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@

let currentUserAccountID = -1;
let currentEmail = '';
Onyx.connect({

Check warning on line 68 in src/libs/actions/User.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.SESSION,
callback: (value) => {
currentUserAccountID = value?.accountID ?? CONST.DEFAULT_NUMBER_ID;
Expand All @@ -74,7 +74,7 @@
});

let allPolicies: OnyxCollection<Policy>;
Onyx.connect({

Check warning on line 77 in src/libs/actions/User.ts

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

Onyx.connect() is deprecated. Use useOnyx() hook instead and pass the data as parameters to a pure function
key: ONYXKEYS.COLLECTION.POLICY,
waitForCollectionCallback: true,
callback: (value) => (allPolicies = value),
Expand Down Expand Up @@ -444,6 +444,7 @@
onyxMethod: Onyx.METHOD.MERGE,
key: ONYXKEYS.VALIDATE_ACTION_CODE,
value: {
validateCodeSent: false,
isLoading: true,
pendingFields: {
actionVerified: CONST.RED_BRICK_ROAD_PENDING_ACTION.ADD,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import useThemeStyles from '@hooks/useThemeStyles';
import {getSubscriptionPlanInfo, isSubscriptionTypeOfInvoicing} from '@libs/SubscriptionUtils';
import variables from '@styles/variables';
import CONST from '@src/CONST';
import getSubscriptionPlanBenefitA11yProps from './getSubscriptionPlanBenefitA11yProps';
import SubscriptionPlanCardActionButton from './SubscriptionPlanCardActionButton';

type PersonalPolicyTypeExcludedProps = Exclude<ValueOf<typeof CONST.POLICY.TYPE>, 'personal'>;
Expand Down Expand Up @@ -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 (
<View
// eslint-disable-next-line react/no-array-index-key
key={`row-${rowIndex}`}
style={styles.flexRow}
role={CONST.ROLE.LIST}
style={[styles.flexRow, styles.flexWrap]}
>
{benefits.slice(rowIndex * benefitsColumns, (rowIndex + 1) * benefitsColumns).map((item) => (
<View
key={item}
style={[styles.flex1, styles.flexRow, styles.alignItemsCenter, shouldUseNarrowLayout ? styles.mt3 : styles.mt4]}
>
<Icon
src={Expensicons.Checkmark}
fill={theme.iconSuccessFill}
width={variables.iconSizeSmall}
height={variables.iconSizeSmall}
/>
<Text style={[styles.textLabelSupporting, styles.ml2]}>{item}</Text>
</View>
))}
{benefits.map((item, index) => {
const {accessible, accessibilityLabel} = getSubscriptionPlanBenefitA11yProps({benefitText: item, index, totalBenefits: benefits.length, ofLabel: translate('common.of')});
return (
<View
key={item}
style={[styles.flexRow, styles.alignItemsCenter, shouldUseNarrowLayout ? styles.mt3 : styles.mt4, {width: `${100 / benefitsColumns}%`}]}
role={CONST.ROLE.LISTITEM}
accessible={accessible}
accessibilityLabel={accessibilityLabel}
>
<View
aria-hidden
importantForAccessibility="no-hide-descendants"
>
<Icon
src={Expensicons.Checkmark}
fill={theme.iconSuccessFill}
width={variables.iconSizeSmall}
height={variables.iconSizeSmall}
/>
</View>
<Text style={[styles.textLabelSupporting, styles.ml2]}>{item}</Text>
</View>
);
})}
</View>
));
);
};

const shouldHideSubscriptionSettingsButton =
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import type {GetSubscriptionPlanBenefitA11yProps} from './types';

const getSubscriptionPlanBenefitA11yProps: GetSubscriptionPlanBenefitA11yProps = () => ({});

export default getSubscriptionPlanBenefitA11yProps;
Original file line number Diff line number Diff line change
@@ -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};
61 changes: 41 additions & 20 deletions src/pages/workspace/WorkspacesListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ function WorkspacesListPage() {
shouldHideOnDelete={false}
>
<PressableWithoutFeedback
role={CONST.ROLE.BUTTON}
role={isLessThanMediumScreen ? CONST.ROLE.BUTTON : CONST.ROLE.ROW}
accessibilityLabel={accessibilityLabel}
style={[styles.mh5]}
disabled={item.disabled}
Expand Down Expand Up @@ -692,32 +692,47 @@ function WorkspacesListPage() {
/>
)}
{!isLessThanMediumScreen && filteredWorkspaces.length > 0 && (
<View style={[styles.flexRow, styles.gap5, styles.pt2, styles.pb3, styles.pr5, styles.pl10, styles.appBG]}>
<View style={[styles.flexRow, styles.flex2]}>
<View
role={CONST.ROLE.ROW}
style={[styles.flexRow, styles.gap5, styles.pt2, styles.pb3, styles.pr5, styles.pl10, styles.appBG]}
>
<View
role={CONST.ROLE.COLUMNHEADER}
style={[styles.flexRow, styles.flex2]}
>
<Text
numberOfLines={1}
style={[styles.flexGrow1, styles.textLabelSupporting]}
>
{translate('workspace.common.workspaceName')}
</Text>
</View>
<View style={[styles.flexRow, styles.flex1, styles.workspaceOwnerSectionTitle, styles.workspaceOwnerSectionMinWidth]}>
<View
role={CONST.ROLE.COLUMNHEADER}
style={[styles.flexRow, styles.flex1, styles.workspaceOwnerSectionTitle, styles.workspaceOwnerSectionMinWidth]}
>
<Text
numberOfLines={1}
style={[styles.flexGrow1, styles.textLabelSupporting]}
>
{translate('workspace.common.workspaceOwner')}
</Text>
</View>
<View style={[styles.flexRow, styles.flex1, styles.workspaceTypeSectionTitle]}>
<View
role={CONST.ROLE.COLUMNHEADER}
style={[styles.flexRow, styles.flex1, styles.workspaceTypeSectionTitle]}
>
<Text
numberOfLines={1}
style={[styles.flexGrow1, styles.textLabelSupporting]}
>
{translate('workspace.common.workspaceType')}
</Text>
</View>
<View style={[styles.workspaceRightColumn, styles.mr7]} />
<View
role={CONST.ROLE.COLUMNHEADER}
style={[styles.workspaceRightColumn, styles.mr7]}
/>
</View>
)}
</>
Expand Down Expand Up @@ -810,20 +825,26 @@ function WorkspacesListPage() {
<FullScreenLoadingIndicator style={[styles.flex1, styles.pRelative]} />
</View>
) : (
<FlatList
ref={flatlistRef}
data={data}
onScrollToIndexFailed={(info) => {
flatlistRef.current?.scrollToOffset({
offset: info.averageItemLength * info.index,
animated: true,
});
}}
renderItem={renderItem}
ListHeaderComponent={listHeaderComponent}
keyboardShouldPersistTaps="handled"
contentContainerStyle={styles.pb20}
/>
<View
style={styles.flex1}
role={isLessThanMediumScreen ? undefined : CONST.ROLE.TABLE}
aria-label={isLessThanMediumScreen ? undefined : translate('common.workspaces')}
>
<FlatList
ref={flatlistRef}
data={data}
onScrollToIndexFailed={(info) => {
flatlistRef.current?.scrollToOffset({
offset: info.averageItemLength * info.index,
animated: true,
});
}}
renderItem={renderItem}
ListHeaderComponent={listHeaderComponent}
keyboardShouldPersistTaps="handled"
contentContainerStyle={styles.pb20}
/>
</View>
)}
</View>
<ConfirmModal
Expand Down
Loading
Loading