From 253b29dbd8ddb11824866e423c00a4a68bb856f3 Mon Sep 17 00:00:00 2001 From: Mats Byrkeland Date: Wed, 25 Jul 2018 17:33:58 -0700 Subject: [PATCH] Add accessibilityHint for iOS (#18093) Summary: This adds the accessibilityHint for View, Text and Touchable* on iOS. The accessibilityHint provides some more information about an element when the accessibilityLabel is not enough. The accessibilityHint is a core accessibility property on iOS. From https://developer.apple.com/documentation/objectivec/nsobject/1615093-accessibilityhint: > An accessibility hint helps users understand what will happen when they perform an action on the accessibility element when that result is not obvious from the accessibility label. Related issue: https://github.com/facebook/react-native/issues/14706 The npm scripts `test`, `flow`, `lint` and `prettier` are satisfied. I added a couple of examples to the RNTester app. The Accessibility Inspector on Mac helps debugging accessibility stuff on a simulator, but it does not show the accessibilityHint. Therefore I tested the RNTester app on an iPhone 8 device using VoiceOver to verify the hint functionality. It works fine, and I've tested disabling and enabling "read hints" in the VoiceOver settings on the phone. https://github.com/facebook/react-native-website/pull/222 [IOS][FEATURE][Accessibility] - Add accessibilityHint for View, Text, Touchable* on iOS Closes https://github.com/facebook/react-native/pull/18093 Reviewed By: hramos Differential Revision: D7230780 Pulled By: ziqichen6 fbshipit-source-id: 172ad28dc9ae2b67ea256100f6acb939f2466d0b --- .../Components/Touchable/TouchableBounce.js | 1 + .../Touchable/TouchableHighlight.js | 1 + .../Components/Touchable/TouchableOpacity.js | 1 + .../Touchable/TouchableWithoutFeedback.js | 3 +++ .../View/ReactNativeViewAttributes.js | 1 + Libraries/Components/View/ViewPropTypes.js | 12 +++++++++ Libraries/Text/Text/RCTTextView.m | 2 +- RNTester/js/AccessibilityIOSExample.js | 27 ++++++++++++++++++- React/Views/RCTViewManager.m | 1 + 9 files changed, 47 insertions(+), 2 deletions(-) diff --git a/Libraries/Components/Touchable/TouchableBounce.js b/Libraries/Components/Touchable/TouchableBounce.js index c899748b4..e26d9b221 100644 --- a/Libraries/Components/Touchable/TouchableBounce.js +++ b/Libraries/Components/Touchable/TouchableBounce.js @@ -160,6 +160,7 @@ const TouchableBounce = ((createReactClass({ style={[{transform: [{scale: this.state.scale}]}, this.props.style]} accessible={this.props.accessible !== false} accessibilityLabel={this.props.accessibilityLabel} + accessibilityHint={this.props.accessibilityHint} accessibilityComponentType={this.props.accessibilityComponentType} accessibilityTraits={this.props.accessibilityTraits} nativeID={this.props.nativeID} diff --git a/Libraries/Components/Touchable/TouchableHighlight.js b/Libraries/Components/Touchable/TouchableHighlight.js index 5a681c099..42360c057 100644 --- a/Libraries/Components/Touchable/TouchableHighlight.js +++ b/Libraries/Components/Touchable/TouchableHighlight.js @@ -348,6 +348,7 @@ const TouchableHighlight = ((createReactClass({ | any, + accessibilityHint?: string, accessibilityTraits?: ?AccessibilityTraitsFlow, children?: ?React.Node, delayLongPress?: ?number, @@ -75,6 +76,7 @@ const TouchableWithoutFeedback = ((createReactClass({ propTypes: { accessible: PropTypes.bool, accessibilityLabel: PropTypes.node, + accessibilityHint: PropTypes.string, accessibilityComponentType: PropTypes.oneOf(AccessibilityComponentTypes), accessibilityTraits: PropTypes.oneOfType([ PropTypes.oneOf(AccessibilityTraits), @@ -238,6 +240,7 @@ const TouchableWithoutFeedback = ((createReactClass({ return (React: any).cloneElement(child, { accessible: this.props.accessible !== false, accessibilityLabel: this.props.accessibilityLabel, + accessibilityHint: this.props.accessibilityHint, accessibilityComponentType: this.props.accessibilityComponentType, accessibilityTraits: this.props.accessibilityTraits, nativeID: this.props.nativeID, diff --git a/Libraries/Components/View/ReactNativeViewAttributes.js b/Libraries/Components/View/ReactNativeViewAttributes.js index 4bbe4081d..889145dfe 100644 --- a/Libraries/Components/View/ReactNativeViewAttributes.js +++ b/Libraries/Components/View/ReactNativeViewAttributes.js @@ -24,6 +24,7 @@ ReactNativeViewAttributes.UIView = { accessibilityRole: true, accessibilityStates: true, accessibilityTraits: true, + accessibilityHint: true, importantForAccessibility: true, nativeID: true, testID: true, diff --git a/Libraries/Components/View/ViewPropTypes.js b/Libraries/Components/View/ViewPropTypes.js index d353dbe77..fb8b27a36 100644 --- a/Libraries/Components/View/ViewPropTypes.js +++ b/Libraries/Components/View/ViewPropTypes.js @@ -87,6 +87,7 @@ export type ViewProps = $ReadOnly<{| | string | Array | any, + accessibilityHint?: string, accessibilityActions?: Array, accessibilityComponentType?: AccessibilityComponentType, accessibilityLiveRegion?: 'none' | 'polite' | 'assertive', @@ -128,6 +129,17 @@ module.exports = { */ accessibilityLabel: PropTypes.node, + /** + * An accessibility hint helps users understand what will happen when they perform + * an action on the accessibility element when that result is not obvious from the + * accessibility label. + * + * @platform ios + * + * See http://facebook.github.io/react-native/docs/view.html#accessibilityHint + */ + accessibilityHint: PropTypes.string, + /** * Provides an array of custom actions available for accessibility. * diff --git a/Libraries/Text/Text/RCTTextView.m b/Libraries/Text/Text/RCTTextView.m index 45c093139..36bd156d3 100644 --- a/Libraries/Text/Text/RCTTextView.m +++ b/Libraries/Text/Text/RCTTextView.m @@ -238,7 +238,7 @@ if (_selectable && action == @selector(copy:)) { return YES; } - + return [self.nextResponder canPerformAction:action withSender:sender]; } diff --git a/RNTester/js/AccessibilityIOSExample.js b/RNTester/js/AccessibilityIOSExample.js index 60d9ceeed..90056cb5d 100644 --- a/RNTester/js/AccessibilityIOSExample.js +++ b/RNTester/js/AccessibilityIOSExample.js @@ -12,7 +12,7 @@ var React = require('react'); var ReactNative = require('react-native'); -var {AccessibilityInfo, Text, View} = ReactNative; +var {AccessibilityInfo, Text, View, TouchableOpacity} = ReactNative; class AccessibilityIOSExample extends React.Component<{}> { render() { @@ -39,6 +39,31 @@ class AccessibilityIOSExample extends React.Component<{}> { This text component's accessibilityLabel is set explicitly. + + + This view component has both an accessibilityLabel and an + accessibilityHint explicitly set. + + + + This text component has both an accessibilityLabel and an + accessibilityHint explicitly set. + + + + + This button has both an accessibilityLabel and an + accessibilityHint explicitly set. + + + This view's children are hidden from the accessibility tree diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index c0cba4611..420f47d49 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -111,6 +111,7 @@ RCT_EXPORT_VIEW_PROPERTY(nativeID, NSString) RCT_REMAP_VIEW_PROPERTY(accessible, reactAccessibilityElement.isAccessibilityElement, BOOL) RCT_REMAP_VIEW_PROPERTY(accessibilityActions, reactAccessibilityElement.accessibilityActions, NSString) RCT_REMAP_VIEW_PROPERTY(accessibilityLabel, reactAccessibilityElement.accessibilityLabel, NSString) +RCT_REMAP_VIEW_PROPERTY(accessibilityHint, reactAccessibilityElement.accessibilityHint, NSString) RCT_REMAP_VIEW_PROPERTY(accessibilityTraits, reactAccessibilityElement.accessibilityTraits, UIAccessibilityTraits) RCT_REMAP_VIEW_PROPERTY(accessibilityViewIsModal, reactAccessibilityElement.accessibilityViewIsModal, BOOL) RCT_REMAP_VIEW_PROPERTY(accessibilityElementsHidden, reactAccessibilityElement.accessibilityElementsHidden, BOOL)