Adds accessiblity actions on core components (#31532)

Summary:
Android: Adding custom actions (https://github.com/facebook/react-native/issues/30854).
Adds accessiblity actions on core components (Button, TextInput, Text, and Picker).

## Changelog
[General] [Added] - Adds accessiblity actions on core components

Pull Request resolved: https://github.com/facebook/react-native/pull/31532

Test Plan:
- `npm test`
- Rendering of components on `RNTesterApp` using talkback:
    - Check if accessibility actions were available;
    ![image](https://user-images.githubusercontent.com/33161939/118381843-a668c180-b5c5-11eb-9ce4-016a49157dc5.png)
    - Trigger `activate` action for all components;
    ![image](https://user-images.githubusercontent.com/33161939/118381736-7bca3900-b5c4-11eb-82fb-32e824e1b38c.png)

## Notes
- For `TextInput` an unexpected error is raised:
![image](https://user-images.githubusercontent.com/33161939/118381603-d1054b00-b5c2-11eb-93f2-1d5730ee2d24.png)

Reviewed By: kacieb

Differential Revision: D28654294

Pulled By: lunaleaps

fbshipit-source-id: 80dd3f3c7aa27bbaf16ef12997e8f55952a02eb2
This commit is contained in:
Dennis Urtubia
2021-05-26 16:49:11 -07:00
committed by Facebook GitHub Bot
parent 52b51f0886
commit 44717152ca
5 changed files with 121 additions and 2 deletions
+14 -1
View File
@@ -20,7 +20,11 @@ import TouchableOpacity from './Touchable/TouchableOpacity';
import View from './View/View';
import invariant from 'invariant';
import type {AccessibilityState} from './View/ViewAccessibility';
import type {
AccessibilityState,
AccessibilityActionEvent,
AccessibilityActionInfo,
} from './View/ViewAccessibility';
import type {PressEvent} from '../Types/CoreEventTypes';
type ButtonProps = $ReadOnly<{|
@@ -137,6 +141,9 @@ type ButtonProps = $ReadOnly<{|
/**
* Accessibility props.
*/
accessible?: ?boolean,
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
accessibilityState?: ?AccessibilityState,
|}>;
@@ -266,6 +273,9 @@ class Button extends React.Component<ButtonProps> {
nextFocusRight,
nextFocusUp,
testID,
accessible,
accessibilityActions,
onAccessibilityAction,
} = this.props;
const buttonStyles = [styles.button];
const textStyles = [styles.text];
@@ -303,6 +313,9 @@ class Button extends React.Component<ButtonProps> {
return (
<Touchable
accessible={accessible}
accessibilityActions={accessibilityActions}
onAccessibilityAction={onAccessibilityAction}
accessibilityLabel={accessibilityLabel}
accessibilityRole="button"
accessibilityState={accessibilityState}
+26
View File
@@ -18,6 +18,11 @@ import UnimplementedView from '../UnimplementedViews/UnimplementedView';
import type {TextStyleProp, ColorValue} from '../../StyleSheet/StyleSheet';
import type {
AccessibilityActionEvent,
AccessibilityActionInfo,
} from '../View/ViewAccessibility';
const MODE_DIALOG = 'dialog';
const MODE_DROPDOWN = 'dropdown';
@@ -115,6 +120,27 @@ type PickerProps = $ReadOnly<{|
* The string used for the accessibility label. Will be read once focused on the picker but not on change.
*/
accessibilityLabel?: ?string,
/**
* When `true`, indicates that the view is an accessibility element.
* By default, all the touchable elements are accessible.
*
* See https://reactnative.dev/docs/view.html#accessible
*/
accessible?: ?boolean,
/**
* Provides an array of custom actions available for accessibility.
*
*/
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
/**
* When `accessible` is true, the system will try to invoke this function
* when the user performs an accessibility custom action.
*
*/
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
|}>;
/**
@@ -19,6 +19,11 @@ import StyleSheet from '../../StyleSheet/StyleSheet';
import invariant from 'invariant';
import processColor from '../../StyleSheet/processColor';
import type {
AccessibilityActionEvent,
AccessibilityActionInfo,
} from '../View/ViewAccessibility';
import type {SyntheticEvent} from '../../Types/CoreEventTypes';
import type {ColorValue, TextStyleProp} from '../../StyleSheet/StyleSheet';
@@ -31,6 +36,9 @@ type PickerItemSelectSyntheticEvent = SyntheticEvent<
type PickerItemValue = number | string;
type Props = $ReadOnly<{|
accessible?: ?boolean,
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
accessibilityLabel?: ?Stringish,
children?: React.Node,
style?: ?TextStyleProp,
@@ -111,6 +119,9 @@ function PickerAndroid(props: Props): React.Node {
);
const rootProps = {
accessible: props.accessible,
accessibilityActions: props.accessibilityActions,
onAccessibilityAction: props.onAccessibilityAction,
accessibilityLabel: props.accessibilityLabel,
enabled: props.enabled,
items,
+4
View File
@@ -20,6 +20,8 @@ import type {TextStyleProp} from '../StyleSheet/StyleSheet';
import type {
AccessibilityRole,
AccessibilityState,
AccessibilityActionInfo,
AccessibilityActionEvent,
} from '../Components/View/ViewAccessibility';
export type PressRetentionOffset = $ReadOnly<{|
@@ -39,6 +41,8 @@ export type TextProps = $ReadOnly<{|
* See https://reactnative.dev/docs/text.html#accessible
*/
accessible?: ?boolean,
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
accessibilityHint?: ?Stringish,
accessibilityLabel?: ?Stringish,
accessibilityRole?: ?AccessibilityRole,
@@ -23,6 +23,7 @@ const {
Alert,
StyleSheet,
Slider,
Picker,
Platform,
} = require('react-native');
import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
@@ -664,7 +665,7 @@ class AccessibilityActionsExample extends React.Component<{}> {
</View>
</RNTesterBlock>
<RNTesterBlock title="Button with custom accessibility actions">
<RNTesterBlock title="TouchableWithoutFeedback with custom accessibility actions">
<TouchableWithoutFeedback
accessible={true}
accessibilityActions={[
@@ -692,6 +693,70 @@ class AccessibilityActionsExample extends React.Component<{}> {
</View>
</TouchableWithoutFeedback>
</RNTesterBlock>
<RNTesterBlock title="Button with accessibility actions">
<Button
accessible={true}
accessibilityActions={[
{name: 'activate', label: 'activate label'},
{name: 'copy', label: 'copy label'},
]}
onAccessibilityAction={event => {
switch (event.nativeEvent.actionName) {
case 'activate':
Alert.alert('Alert', 'Activate accessiblity action');
break;
case 'copy':
Alert.alert('Alert', 'copy action success');
break;
}
}}
onPress={() => Alert.alert('Button has been pressed!')}
title="Button with accessiblity action"
/>
</RNTesterBlock>
<RNTesterBlock title="Text with custom accessibility actions">
<Text
accessible={true}
accessibilityActions={[
{name: 'activate', label: 'activate label'},
{name: 'copy', label: 'copy label'},
]}
onAccessibilityAction={event => {
switch (event.nativeEvent.actionName) {
case 'activate':
Alert.alert('Alert', 'Activate accessiblity action');
break;
case 'copy':
Alert.alert('Alert', 'copy action success');
break;
}
}}>
Text
</Text>
</RNTesterBlock>
<RNTesterBlock title="Picker with accessibility actions">
<Picker
accessible={true}
accessibilityActions={[
{name: 'activate', label: 'activate label'},
{name: 'copy', label: 'copy label'},
]}
onAccessibilityAction={event => {
switch (event.nativeEvent.actionName) {
case 'activate':
Alert.alert('Alert', 'Activate accessiblity action');
break;
case 'copy':
Alert.alert('Alert', 'copy action success');
break;
}
}}>
<Picker.Item label="Item 1" value="item1" />
</Picker>
</RNTesterBlock>
</View>
);
}