Skip to content
Open
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
19 changes: 17 additions & 2 deletions src/components/Checkbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import type ChildrenProps from '@src/types/utils/ChildrenProps';
import type WithSentryLabel from '@src/types/utils/SentryLabel';
import Icon from './Icon';
import * as Expensicons from './Icon/Expensicons';

Check warning on line 12 in src/components/Checkbox.tsx

View workflow job for this annotation

GitHub Actions / Changed files ESLint check

'./Icon/Expensicons' import is restricted from being used by a pattern. Direct imports from Icon/Expensicons are deprecated. Please use lazy loading hooks instead. Use `useMemoizedLazyExpensifyIcons` from @hooks/useLazyAsset. See docs/LAZY_ICONS_AND_ILLUSTRATIONS.md for details
import type {PressableRef} from './Pressable/GenericPressable/types';
import PressableWithFeedback from './Pressable/PressableWithFeedback';

Expand All @@ -18,7 +18,7 @@
/** Whether checkbox is checked */
isChecked?: boolean;

/** Whether checkbox is in the indeterminate (mixed) state */
/** Whether checkbox is in the indeterminate ("mixed") state */
isIndeterminate?: boolean;

/** A function that is called when the box/label is pressed */
Expand Down Expand Up @@ -51,6 +51,12 @@
/** An accessibility label for the checkbox */
accessibilityLabel: string;

/** Accessibility role - defaults to checkbox, can be overridden to radio */
accessibilityRole?: 'checkbox' | 'radio';

/** Accessibility state */
accessibilityState?: {selected?: boolean; checked?: boolean};

/** stop propagation of the mouse down event */
shouldStopMouseDownPropagation?: boolean;

Expand Down Expand Up @@ -81,6 +87,8 @@
caretSize = 14,
onPress,
accessibilityLabel,
accessibilityRole = CONST.ROLE.CHECKBOX,
accessibilityState,
shouldStopMouseDownPropagation,
shouldSelectOnPressEnter,
wrapperStyle,
Expand Down Expand Up @@ -116,6 +124,12 @@
onPress();
};

const computedAccessibilityState = accessibilityState ?? (
accessibilityRole === CONST.ROLE.RADIO
? {selected: isChecked}
: {checked: isIndeterminate ? 'mixed' : isChecked}
);

return (
<PressableWithFeedback
testID={testID}
Expand All @@ -130,7 +144,8 @@
ref={ref as PressableRef}
style={[StyleUtils.getCheckboxPressableStyle(containerBorderRadius + 2), style]} // to align outline on focus, border-radius of pressable should be 2px more than Checkbox
onKeyDown={handleSpaceOrEnterKey}
role={CONST.ROLE.CHECKBOX}
role={accessibilityRole}
accessibilityState={computedAccessibilityState}
/* true → checked
false → unchecked
mixed → indeterminate */
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {useCallback} from 'react';
import Checkbox from '@components/Checkbox';
import useThemeStyles from '@hooks/useThemeStyles';
import CONST from '@src/CONST';
import RadioListItem from './RadioListItem';
import type {ListItem, SingleSelectListItemProps} from './types';

Expand Down Expand Up @@ -33,6 +34,8 @@ function SingleSelectListItem<TItem extends ListItem>({
shouldSelectOnPressEnter
containerBorderRadius={999}
accessibilityLabel="SingleSelectListItem"
accessibilityRole={CONST.ROLE.RADIO}
accessibilityState={{selected: item.isSelected}}
isChecked={item.isSelected}
onPress={() => onSelectRow(item)}
/>
Expand Down
4 changes: 3 additions & 1 deletion src/components/TabSelector/TabSelectorItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,12 +115,14 @@ function TabSelectorItem({
const children = (
<AnimatedPressableWithFeedback
accessibilityLabel={title}
accessibilityRole={CONST.ROLE.TAB}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❌ PERF-4 (docs)

Creating a new object {selected: isActive} on every render breaks memoization of the AnimatedPressableWithFeedback child component, causing unnecessary re-renders.

Reasoning: The parent TabSelectorItem is not optimized by React Compiler, while the child AnimatedPressableWithFeedback (via PressableWithFeedback) is memoized. Each new object reference forces the child to re-render even when isActive hasn't changed.

Suggested fix:

const accessibilityState = useMemo(() => ({selected: isActive}), [isActive]);

// Then use:
accessibilityState={accessibilityState}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a valid comment as isActive is a variable though I think it should comment on line 119

accessibilityState={{selected: isActive}}
style={[styles.tabSelectorButton, styles.tabBackground(isHovered, isActive, backgroundColor), styles.userSelectNone]}
wrapperStyle={[equalWidth ? styles.flex1 : styles.flexGrow1]}
onPress={onPress}
onHoverIn={() => setIsHovered(true)}
onHoverOut={() => setIsHovered(false)}
role={CONST.ROLE.BUTTON}
role={CONST.ROLE.TAB}
dataSet={{[CONST.SELECTION_SCRAPER_HIDDEN_ELEMENT]: true}}
testID={testID}
ref={childRef}
Expand Down
Loading