Skip to content

Commit

Permalink
Add accessibilityHint for iOS (#18093)
Browse files Browse the repository at this point in the history
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: #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.

facebook/react-native-website#222

[IOS][FEATURE][Accessibility] - Add accessibilityHint for View, Text, Touchable* on iOS
Closes #18093

Reviewed By: hramos

Differential Revision: D7230780

Pulled By: ziqichen6

fbshipit-source-id: 172ad28dc9ae2b67ea256100f6acb939f2466d0b
  • Loading branch information
draperunner authored and facebook-github-bot committed Jul 26, 2018
1 parent b7bb25f commit 253b29d
Show file tree
Hide file tree
Showing 9 changed files with 47 additions and 2 deletions.
1 change: 1 addition & 0 deletions Libraries/Components/Touchable/TouchableBounce.js
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down
1 change: 1 addition & 0 deletions Libraries/Components/Touchable/TouchableHighlight.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ const TouchableHighlight = ((createReactClass({
<View
accessible={this.props.accessible !== false}
accessibilityLabel={this.props.accessibilityLabel}
accessibilityHint={this.props.accessibilityHint}
accessibilityComponentType={this.props.accessibilityComponentType}
accessibilityTraits={this.props.accessibilityTraits}
style={StyleSheet.compose(
Expand Down
1 change: 1 addition & 0 deletions Libraries/Components/Touchable/TouchableOpacity.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ const TouchableOpacity = ((createReactClass({
<Animated.View
accessible={this.props.accessible !== false}
accessibilityLabel={this.props.accessibilityLabel}
accessibilityHint={this.props.accessibilityHint}
accessibilityComponentType={this.props.accessibilityComponentType}
accessibilityTraits={this.props.accessibilityTraits}
style={[this.props.style, {opacity: this.state.anim}]}
Expand Down
3 changes: 3 additions & 0 deletions Libraries/Components/Touchable/TouchableWithoutFeedback.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ export type Props = $ReadOnly<{|
| string
| Array<any>
| any,
accessibilityHint?: string,
accessibilityTraits?: ?AccessibilityTraitsFlow,
children?: ?React.Node,
delayLongPress?: ?number,
Expand Down Expand Up @@ -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),
Expand Down Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions Libraries/Components/View/ReactNativeViewAttributes.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ ReactNativeViewAttributes.UIView = {
accessibilityRole: true,
accessibilityStates: true,
accessibilityTraits: true,
accessibilityHint: true,
importantForAccessibility: true,
nativeID: true,
testID: true,
Expand Down
12 changes: 12 additions & 0 deletions Libraries/Components/View/ViewPropTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export type ViewProps = $ReadOnly<{|
| string
| Array<any>
| any,
accessibilityHint?: string,
accessibilityActions?: Array<string>,
accessibilityComponentType?: AccessibilityComponentType,
accessibilityLiveRegion?: 'none' | 'polite' | 'assertive',
Expand Down Expand Up @@ -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.
*
Expand Down
2 changes: 1 addition & 1 deletion Libraries/Text/Text/RCTTextView.m
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ - (BOOL)canPerformAction:(SEL)action withSender:(id)sender
if (_selectable && action == @selector(copy:)) {
return YES;
}

return [self.nextResponder canPerformAction:action withSender:sender];
}

Expand Down
27 changes: 26 additions & 1 deletion RNTester/js/AccessibilityIOSExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -39,6 +39,31 @@ class AccessibilityIOSExample extends React.Component<{}> {
<Text accessibilityLabel="Test of accessibilityLabel" accessible={true}>
This text component's accessibilityLabel is set explicitly.
</Text>
<View
accessibilityLabel="Test of accessibilityHint"
accessibilityHint="The hint provides more info than the label does"
accessible={true}>
<Text>
This view component has both an accessibilityLabel and an
accessibilityHint explicitly set.
</Text>
</View>
<Text
accessibilityLabel="Test of accessibilityHint"
accessibilityHint="The hint provides more info than the label does">
This text component has both an accessibilityLabel and an
accessibilityHint explicitly set.
</Text>
<TouchableOpacity
accessibilityLabel="Test of accessibilityHint"
accessibilityHint="The hint provides more info than the label does">
<View>
<Text>
This button has both an accessibilityLabel and an
accessibilityHint explicitly set.
</Text>
</View>
</TouchableOpacity>
<View accessibilityElementsHidden={true}>
<Text>
This view's children are hidden from the accessibility tree
Expand Down
1 change: 1 addition & 0 deletions React/Views/RCTViewManager.m
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ - (RCTShadowView *)shadowView
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)
Expand Down

0 comments on commit 253b29d

Please sign in to comment.