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
8 changes: 5 additions & 3 deletions src/components/Pressable/PressableWithDelayToggle.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable react-native-a11y/has-valid-accessibility-descriptors */
import React from 'react';
import type {StyleProp, TextStyle, ViewStyle} from 'react-native';
import Icon from '@components/Icon';
Expand Down Expand Up @@ -83,7 +82,7 @@ function PressableWithDelayToggle({
iconStyles,
icon,
ref,
accessibilityRole,
accessibilityRole = CONST.ROLE.BUTTON,
shouldHaveActiveBackground,
iconWidth = variables.iconSizeSmall,
iconHeight = variables.iconSizeSmall,
Expand All @@ -109,6 +108,8 @@ function PressableWithDelayToggle({
// of a Pressable
const PressableView = inline ? Text : PressableWithoutFeedback;
const tooltipTexts = !isActive ? tooltipTextChecked : tooltipText;
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Fallback to visible text when tooltip is empty for screen readers
const processedAccessibilityLabel = tooltipTexts || (!isActive && textChecked ? textChecked : text) || '';
const shouldShowIcon = !!icon || (!isActive && !!resolvedIconChecked);
const labelText =
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- Disabling this line for safeness as nullish coalescing works only if the value is undefined or null
Expand All @@ -132,7 +133,7 @@ function PressableWithDelayToggle({
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment
ref={ref as any}
onPress={updatePressState}
accessibilityLabel={tooltipTexts}
accessibilityLabel={processedAccessibilityLabel}
suppressHighlighting={inline ? true : undefined}
accessibilityRole={accessibilityRole}
>
Expand All @@ -142,6 +143,7 @@ function PressableWithDelayToggle({
text={tooltipTexts}
shouldRender
>
{/* eslint-disable-next-line react-native-a11y/has-valid-accessibility-descriptors -- Inner pressable is intentionally non-accessible (accessible={false}) since the outer PressableView handles accessibility */}
<PressableWithoutFeedback
tabIndex={-1}
accessible={false}
Expand Down
4 changes: 2 additions & 2 deletions src/pages/settings/Security/TwoFactorAuth/CopyCodesPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,9 +123,9 @@ function CopyCodesPage({route}: TwoFactorAuthPageProps) {
}}
styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCodesButton]}
textStyles={[styles.buttonMediumText]}
accessible={false}
tooltipText=""
tooltipTextChecked=""
accessibilityLabel={translate('twoFactorAuth.copy')}
sentryLabel={CONST.SENTRY_LABEL.TWO_FACTOR_AUTH.COPY_CODES}
/>
<PressableWithDelayToggle
Expand All @@ -139,9 +139,9 @@ function CopyCodesPage({route}: TwoFactorAuthPageProps) {
inline={false}
styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCodesButton]}
textStyles={[styles.buttonMediumText]}
accessible={false}
tooltipText=""
tooltipTextChecked=""
accessibilityLabel={translate('common.download')}
sentryLabel={CONST.SENTRY_LABEL.TWO_FACTOR_AUTH.DOWNLOAD_CODES}
/>
</View>
Expand Down
2 changes: 1 addition & 1 deletion src/pages/settings/Security/TwoFactorAuth/VerifyPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ function VerifyPage({route}: VerifyPageProps) {
onPress={() => Clipboard.setString(account?.twoFactorAuthSecretKey ?? '')}
styles={[styles.button, styles.buttonMedium, styles.twoFactorAuthCopyCodeButton]}
textStyles={[styles.buttonMediumText]}
accessible={false}
accessibilityLabel={translate('twoFactorAuth.copy')}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@MelvinBot It looks like accessibilityLabel is never used in PressableWithDelayToggle, right? If that’s the case, passing accessibilityLabel={translate('twoFactorAuth.copy')} would be redundant.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@MelvinBot Could you investigate this one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're correct — accessibilityLabel is not used by PressableWithDelayToggle at runtime.

Here's what happens:

  1. Not destructured: The component destructures 18 specific props by name at PressableWithDelayToggle.tsx:72-91, but accessibilityLabel is not among them, and there's no ...rest spread. The caller's value is silently dropped.

  2. Computed internally: At PressableWithDelayToggle.tsx:135, the component builds its own accessibilityLabel:

    accessibilityLabel={tooltipTexts || (!isActive && textChecked ? textChecked : text) || ''}

    This derives from tooltipTexts, textChecked, and text — never from the caller-supplied prop.

  3. But required by TypeScript: PressableWithDelayToggleProps extends PressableProps, which uses a discriminated union (RequiredAccessibilityLabel in GenericPressable/types.ts:12-39) requiring either accessibilityLabel: string or accessible: false. Removing the prop would cause a compilation error.

So the prop is redundant at runtime but required by the type system. The proper fix would be to update PressableWithDelayToggleProps to override the RequiredAccessibilityLabel constraint (since the component always computes its own label), so callers don't need to pass a value that gets discarded. I'll address this in the next commit.

sentryLabel={CONST.SENTRY_LABEL.TWO_FACTOR_AUTH.COPY}
/>
</View>
Expand Down
Loading