From ea5a3a5bca9bb1985f868e2a2d247435890bc52a Mon Sep 17 00:00:00 2001 From: Gabriel Donadel Dall'Agnol Date: Wed, 7 Sep 2022 08:38:25 -0700 Subject: [PATCH] feat: Add aria-hidden prop to Pressable, View and Touchables components (#34552) Summary: This adds the ` aria-hidden` prop to `Pressable`, `TouchableBounce`, `TouchableHighlight`, `TouchableNativeFeedback`, `TouchableOpacity`, `TouchableWithoutFeedback` and `View` components as requested on https://github.com/facebook/react-native/issues/34424, being an alias `importantforAccessibility='no-hide-descendants'` on Android and an alias for `accessibilityElementsHidden` on iOS. This PR also updates RNTester AccessibilityExample in order to facilitate the manual QA. ## Changelog [General] [Added] - Add aria-hidden prop to Pressable, View and Touchables components Pull Request resolved: https://github.com/facebook/react-native/pull/34552 Test Plan: 1. Open the RNTester app and navigate to the Accessibility page 2. Test the `aria-hidden` prop through the `View with hidden children from accessibility tree` section, this can be tested either by enabling Voice Over if you're using a real device or through the Accessibility Inspector if you're using a simulator https://user-images.githubusercontent.com/11707729/187814455-6937e33e-7edd-434e-b7d3-ee6c03f635ca.mov Reviewed By: NickGerleman Differential Revision: D39206245 Pulled By: jacdebug fbshipit-source-id: 551dc671fbcedc824f253e22b8d7753c466838c7 --- Libraries/Components/Pressable/Pressable.js | 5 +++++ .../Components/Touchable/TouchableBounce.js | 10 ++++++++-- .../Components/Touchable/TouchableHighlight.js | 10 ++++++++-- .../Touchable/TouchableNativeFeedback.js | 8 ++++++-- .../Components/Touchable/TouchableOpacity.js | 10 ++++++++-- .../Touchable/TouchableWithoutFeedback.js | 10 ++++++++-- Libraries/Components/View/View.js | 17 ++++++++++++++--- Libraries/Components/View/ViewPropTypes.js | 8 ++++++++ .../Accessibility/AccessibilityExample.js | 8 ++++++++ 9 files changed, 73 insertions(+), 13 deletions(-) diff --git a/Libraries/Components/Pressable/Pressable.js b/Libraries/Components/Pressable/Pressable.js index aab4f21d33ed89..992378df706985 100644 --- a/Libraries/Components/Pressable/Pressable.js +++ b/Libraries/Components/Pressable/Pressable.js @@ -52,6 +52,11 @@ type Props = $ReadOnly<{| accessibilityValue?: ?AccessibilityValue, accessibilityViewIsModal?: ?boolean, accessible?: ?boolean, + /** + * A value indicating whether the accessibility elements contained within + * this accessibility element are hidden. + */ + 'aria-hidden'?: ?boolean, focusable?: ?boolean, importantForAccessibility?: ?('auto' | 'yes' | 'no' | 'no-hide-descendants'), onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed, diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index f77ab0f5738fcf..62a7820d68d7e2 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -143,10 +143,16 @@ class TouchableBounce extends React.Component { accessibilityActions={this.props.accessibilityActions} onAccessibilityAction={this.props.onAccessibilityAction} accessibilityValue={this.props.accessibilityValue} - importantForAccessibility={this.props.importantForAccessibility} + importantForAccessibility={ + this.props['aria-hidden'] === true + ? 'no-hide-descendants' + : this.props.importantForAccessibility + } accessibilityLiveRegion={this.props.accessibilityLiveRegion} accessibilityViewIsModal={this.props.accessibilityViewIsModal} - accessibilityElementsHidden={this.props.accessibilityElementsHidden} + accessibilityElementsHidden={ + this.props['aria-hidden'] ?? this.props.accessibilityElementsHidden + } nativeID={this.props.nativeID} testID={this.props.testID} hitSlop={this.props.hitSlop} diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index d41513b189b55d..9bda74f946a4ea 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -302,10 +302,16 @@ class TouchableHighlight extends React.Component { accessibilityValue={this.props.accessibilityValue} accessibilityActions={this.props.accessibilityActions} onAccessibilityAction={this.props.onAccessibilityAction} - importantForAccessibility={this.props.importantForAccessibility} + importantForAccessibility={ + this.props['aria-hidden'] === true + ? 'no-hide-descendants' + : this.props.importantForAccessibility + } accessibilityLiveRegion={this.props.accessibilityLiveRegion} accessibilityViewIsModal={this.props.accessibilityViewIsModal} - accessibilityElementsHidden={this.props.accessibilityElementsHidden} + accessibilityElementsHidden={ + this.props['aria-hidden'] ?? this.props.accessibilityElementsHidden + } style={StyleSheet.compose( this.props.style, this.state.extraStyles?.underlay, diff --git a/Libraries/Components/Touchable/TouchableNativeFeedback.js b/Libraries/Components/Touchable/TouchableNativeFeedback.js index 8fff2f302397f2..3b6de69fb9991f 100644 --- a/Libraries/Components/Touchable/TouchableNativeFeedback.js +++ b/Libraries/Components/Touchable/TouchableNativeFeedback.js @@ -278,10 +278,14 @@ class TouchableNativeFeedback extends React.Component { accessibilityActions: this.props.accessibilityActions, onAccessibilityAction: this.props.onAccessibilityAction, accessibilityValue: this.props.accessibilityValue, - importantForAccessibility: this.props.importantForAccessibility, + importantForAccessibility: + this.props['aria-hidden'] === true + ? 'no-hide-descendants' + : this.props.importantForAccessibility, accessibilityLiveRegion: this.props.accessibilityLiveRegion, accessibilityViewIsModal: this.props.accessibilityViewIsModal, - accessibilityElementsHidden: this.props.accessibilityElementsHidden, + accessibilityElementsHidden: + this.props['aria-hidden'] ?? this.props.accessibilityElementsHidden, hasTVPreferredFocus: this.props.hasTVPreferredFocus, hitSlop: this.props.hitSlop, focusable: diff --git a/Libraries/Components/Touchable/TouchableOpacity.js b/Libraries/Components/Touchable/TouchableOpacity.js index 67e18c06d54c83..936caceaf16bf7 100644 --- a/Libraries/Components/Touchable/TouchableOpacity.js +++ b/Libraries/Components/Touchable/TouchableOpacity.js @@ -231,10 +231,16 @@ class TouchableOpacity extends React.Component { accessibilityActions={this.props.accessibilityActions} onAccessibilityAction={this.props.onAccessibilityAction} accessibilityValue={this.props.accessibilityValue} - importantForAccessibility={this.props.importantForAccessibility} + importantForAccessibility={ + this.props['aria-hidden'] === true + ? 'no-hide-descendants' + : this.props.importantForAccessibility + } accessibilityLiveRegion={this.props.accessibilityLiveRegion} accessibilityViewIsModal={this.props.accessibilityViewIsModal} - accessibilityElementsHidden={this.props.accessibilityElementsHidden} + accessibilityElementsHidden={ + this.props['aria-hidden'] ?? this.props.accessibilityElementsHidden + } style={[this.props.style, {opacity: this.state.anim}]} nativeID={this.props.nativeID} testID={this.props.testID} diff --git a/Libraries/Components/Touchable/TouchableWithoutFeedback.js b/Libraries/Components/Touchable/TouchableWithoutFeedback.js index 2b3bd4b930e14b..f260f2cc9d7334 100755 --- a/Libraries/Components/Touchable/TouchableWithoutFeedback.js +++ b/Libraries/Components/Touchable/TouchableWithoutFeedback.js @@ -42,6 +42,7 @@ type Props = $ReadOnly<{| accessibilityValue?: ?AccessibilityValue, accessibilityViewIsModal?: ?boolean, accessible?: ?boolean, + 'aria-hidden'?: ?boolean, children?: ?React.Node, delayLongPress?: ?number, delayPressIn?: ?number, @@ -71,7 +72,6 @@ type State = $ReadOnly<{| const PASSTHROUGH_PROPS = [ 'accessibilityActions', - 'accessibilityElementsHidden', 'accessibilityHint', 'accessibilityLanguage', 'accessibilityIgnoresInvertColors', @@ -81,7 +81,6 @@ const PASSTHROUGH_PROPS = [ 'accessibilityValue', 'accessibilityViewIsModal', 'hitSlop', - 'importantForAccessibility', 'nativeID', 'onAccessibilityAction', 'onBlur', @@ -123,6 +122,13 @@ class TouchableWithoutFeedback extends React.Component { : this.props.accessibilityState, focusable: this.props.focusable !== false && this.props.onPress !== undefined, + + accessibilityElementsHidden: + this.props['aria-hidden'] ?? this.props.accessibilityElementsHidden, + importantForAccessibility: + this.props['aria-hidden'] === true + ? 'no-hide-descendants' + : this.props.importantForAccessibility, }; for (const prop of PASSTHROUGH_PROPS) { if (this.props[prop] !== undefined) { diff --git a/Libraries/Components/View/View.js b/Libraries/Components/View/View.js index bc2145e52982e2..1de58236e720e6 100644 --- a/Libraries/Components/View/View.js +++ b/Libraries/Components/View/View.js @@ -30,12 +30,15 @@ const View: React.AbstractComponent< > = React.forwardRef( ( { - tabIndex, - focusable, - role, + accessibilityElementsHidden, accessibilityRole, + 'aria-hidden': ariaHidden, + focusable, + importantForAccessibility, pointerEvents, + role, style, + tabIndex, ...otherProps }: ViewProps, forwardedRef, @@ -118,6 +121,14 @@ const View: React.AbstractComponent< accessibilityRole={ role ? roleToAccessibilityRoleMapping[role] : accessibilityRole } + accessibilityElementsHidden={ + ariaHidden ?? accessibilityElementsHidden + } + importantForAccessibility={ + ariaHidden === true + ? 'no-hide-descendants' + : importantForAccessibility + } {...otherProps} style={style} pointerEvents={newPointerEvents} diff --git a/Libraries/Components/View/ViewPropTypes.js b/Libraries/Components/View/ViewPropTypes.js index 62faf8a0030b9b..bb0859e45e944a 100644 --- a/Libraries/Components/View/ViewPropTypes.js +++ b/Libraries/Components/View/ViewPropTypes.js @@ -491,6 +491,14 @@ export type ViewProps = $ReadOnly<{| */ accessibilityLabelledBy?: ?string | ?Array, + /** + * A value indicating whether the accessibility elements contained within + * this accessibility element are hidden. + * + * See https://reactnative.dev/docs/view#aria-hidden + */ + 'aria-hidden'?: ?boolean, + /** * Views that are only used to layout their children or otherwise don't draw * anything may be automatically removed from the native hierarchy as an diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js index 1db5bc2249f662..cc227872250c72 100644 --- a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js +++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js @@ -126,6 +126,14 @@ class AccessibilityExample extends React.Component<{}> { + + + + This view's children are hidden from the accessibility tree + + + + {/* Android screen readers will say the accessibility hint instead of the text since the view doesn't have a label. */}