Skip to content

Commit

Permalink
Adds accessiblity actions on core components (#31532)
Browse files Browse the repository at this point in the history
Summary:
Android: Adding custom actions (#30854).
Adds accessiblity actions on core components (Button, TextInput, Text, and Picker).

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

Pull Request resolved: #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
  • Loading branch information
Dennis Urtubia authored and facebook-github-bot committed May 26, 2021
1 parent 52b51f0 commit 4471715
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 2 deletions.
15 changes: 14 additions & 1 deletion Libraries/Components/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<{|
Expand Down Expand Up @@ -137,6 +141,9 @@ type ButtonProps = $ReadOnly<{|
/**
* Accessibility props.
*/
accessible?: ?boolean,
accessibilityActions?: ?$ReadOnlyArray<AccessibilityActionInfo>,
onAccessibilityAction?: ?(event: AccessibilityActionEvent) => mixed,
accessibilityState?: ?AccessibilityState,
|}>;

Expand Down Expand Up @@ -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];
Expand Down Expand Up @@ -303,6 +313,9 @@ class Button extends React.Component<ButtonProps> {

return (
<Touchable
accessible={accessible}
accessibilityActions={accessibilityActions}
onAccessibilityAction={onAccessibilityAction}
accessibilityLabel={accessibilityLabel}
accessibilityRole="button"
accessibilityState={accessibilityState}
Expand Down
26 changes: 26 additions & 0 deletions Libraries/Components/Picker/Picker.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -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,
|}>;

/**
Expand Down
11 changes: 11 additions & 0 deletions Libraries/Components/Picker/PickerAndroid.android.js
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
4 changes: 4 additions & 0 deletions Libraries/Text/TextProps.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import type {TextStyleProp} from '../StyleSheet/StyleSheet';
import type {
AccessibilityRole,
AccessibilityState,
AccessibilityActionInfo,
AccessibilityActionEvent,
} from '../Components/View/ViewAccessibility';

export type PressRetentionOffset = $ReadOnly<{|
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const {
Alert,
StyleSheet,
Slider,
Picker,
Platform,
} = require('react-native');
import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
Expand Down Expand Up @@ -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={[
Expand Down Expand Up @@ -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>
);
}
Expand Down

0 comments on commit 4471715

Please sign in to comment.