Skip to content
7 changes: 5 additions & 2 deletions src/components/SelectionList/ListItem/BaseListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function BaseListItem<TItem extends ListItem>({
shouldDisableHoverStyle,
shouldStopMouseLeavePropagation = true,
shouldShowRightCaret = false,
accessible,
}: BaseListItemProps<TItem>) {
const theme = useTheme();
const styles = useThemeStyles();
Expand Down Expand Up @@ -113,7 +114,6 @@ function BaseListItem<TItem extends ListItem>({
disabled={isDisabled && !item.isSelected}
interactive={item.isInteractive}
accessibilityLabel={item.accessibilityLabel ?? [item.text, item.text !== item.alternateText ? item.alternateText : undefined].filter(Boolean).join(', ')}
role={getButtonRole(true)}
isNested
hoverDimmingValue={1}
pressDimmingValue={item.isInteractive === false ? 1 : variables.pressDimValue}
Expand All @@ -134,9 +134,12 @@ function BaseListItem<TItem extends ListItem>({
]}
onFocus={onFocus}
onMouseLeave={handleMouseLeave}
tabIndex={item.tabIndex}
tabIndex={accessible === false ? -1 : item.tabIndex}
wrapperStyle={pressableWrapperStyle}
testID={testID}
accessible={accessible}
role={accessible === false ? CONST.ROLE.PRESENTATION : getButtonRole(true)}
sentryLabel={`BaseListItem-${keyForList}`}
>
<View
testID={`${CONST.BASE_LIST_ITEM_TEST_ID}${item.keyForList}`}
Expand Down
43 changes: 38 additions & 5 deletions src/components/SelectionList/ListItem/SplitListItem.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import React, {useCallback, useEffect, useRef, useState} from 'react';
import {InteractionManager, View} from 'react-native';
import Icon from '@components/Icon';
import PressableWithFeedback from '@components/Pressable/PressableWithFeedback';
import type {ListItem} from '@components/SelectionList/types';
import Text from '@components/Text';
import type {BaseTextInputRef} from '@components/TextInput/BaseTextInput/types';
import useAnimatedHighlightStyle from '@hooks/useAnimatedHighlightStyle';
import {useMemoizedLazyExpensifyIcons} from '@hooks/useLazyAsset';
import useLocalize from '@hooks/useLocalize';
import useScreenWrapperTransitionStatus from '@hooks/useScreenWrapperTransitionStatus';
import useTheme from '@hooks/useTheme';
import useThemeStyles from '@hooks/useThemeStyles';
Expand Down Expand Up @@ -34,6 +36,7 @@ function SplitListItem<TItem extends ListItem>({
const icons = useMemoizedLazyExpensifyIcons(['ArrowRight', 'Folder', 'Tag'] as const);
const theme = useTheme();
const styles = useThemeStyles();
const {translate} = useLocalize();
const {didScreenTransitionEnd} = useScreenWrapperTransitionStatus();

const splitItem = item as unknown as SplitListItemType;
Expand Down Expand Up @@ -83,6 +86,16 @@ function SplitListItem<TItem extends ListItem>({

const isPercentageMode = splitItem.mode === CONST.TAB.SPLIT.PERCENTAGE;

// Build accessibility label for the grouped text content (date, merchant, category, tags)
const textContentAccessibilityLabel = [
splitItem.headerText,
splitItem.merchant,
splitItem.category ? getDecodedCategoryName(splitItem.category) : undefined,
splitItem.tags?.at(0) ? getCommaSeparatedTagNameWithSanitizedColons(splitItem.tags.at(0) ?? '') : undefined,
]
.filter(Boolean)
.join(', ');

return (
<BaseListItem
item={item}
Expand All @@ -100,10 +113,21 @@ function SplitListItem<TItem extends ListItem>({
keyForList={item.keyForList}
onFocus={onFocus}
pendingAction={item.pendingAction}
accessible={!splitItem.isEditable}
>
<View style={[styles.flexRow, styles.containerWithSpaceBetween, styles.p3]}>
<View style={[styles.flex1]}>
<View style={[styles.containerWithSpaceBetween, !isBottomVisible && styles.justifyContentCenter]}>
<View
style={[styles.flex1]}
accessible={splitItem.isEditable}
accessibilityLabel={textContentAccessibilityLabel}
aria-label={splitItem.isEditable ? textContentAccessibilityLabel : undefined}
tabIndex={splitItem.isEditable ? 0 : undefined}
role={splitItem.isEditable ? CONST.ROLE.SUMMARY : undefined}
>
<View
style={[styles.containerWithSpaceBetween, !isBottomVisible && styles.justifyContentCenter]}
aria-hidden={splitItem.isEditable ? true : undefined}
>
<View style={[styles.minHeight5, styles.justifyContentCenter]}>
<Text
numberOfLines={1}
Expand Down Expand Up @@ -131,7 +155,10 @@ function SplitListItem<TItem extends ListItem>({
</View>
</View>
{isBottomVisible && (
<View style={[styles.splitItemBottomContent]}>
<View
style={[styles.splitItemBottomContent]}
aria-hidden={splitItem.isEditable ? true : undefined}
>
{!!splitItem.category && (
<View style={[styles.flexRow, styles.alignItemsCenter, styles.gap1, styles.pr1, styles.flexShrink1, !!splitItem.tags?.at(0) && styles.mw50]}>
<Icon
Expand Down Expand Up @@ -184,12 +211,18 @@ function SplitListItem<TItem extends ListItem>({
</View>
<View style={[styles.popoverMenuIcon]}>
{!splitItem.isEditable ? null : (
<View style={styles.pointerEventsAuto}>
<PressableWithFeedback
onPress={() => onSelectRow(item)}
accessibilityLabel={translate('common.edit')}
role="button"
style={styles.pointerEventsAuto}
sentryLabel="SplitListItem-EditButton"
>
<Icon
src={icons.ArrowRight}
fill={theme.icon}
/>
</View>
</PressableWithFeedback>
)}
</View>
</View>
Expand Down
6 changes: 6 additions & 0 deletions src/components/SelectionList/ListItem/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,6 +314,12 @@ type BaseListItemProps<TItem extends ListItem> = CommonListItemProps<TItem> & {

/** Whether to call stopPropagation on the mouseleave event in BaseListItem */
shouldStopMouseLeavePropagation?: boolean;

/**
* Whether the pressable should be accessible as a single element.
* When false, allows child elements (like TextInput) to be independently focusable by screen readers.
*/
accessible?: boolean;
};

type SplitListItemType = ListItem &
Expand Down
Loading