Skip to content

Commit

Permalink
[partly]1aeac1c62528004d994200664368dc85fba1795d|Facebook Github Bot|…
Browse files Browse the repository at this point in the history
…Additional Accessibility Roles and States (facebook#24095)
  • Loading branch information
williamdeng committed Apr 21, 2020
1 parent 429ab0d commit b788d01
Show file tree
Hide file tree
Showing 10 changed files with 762 additions and 74 deletions.
29 changes: 27 additions & 2 deletions Libraries/Components/View/ViewAccessibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,32 @@ export type AccessibilityRole =
| 'adjustable'
| 'imagebutton'
| 'header'
| 'summary';
| 'summary'
| 'alert'
| 'checkbox'
| 'combobox'
| 'menu'
| 'menubar'
| 'menuitem'
| 'progressbar'
| 'radio'
| 'radiogroup'
| 'scrollbar'
| 'spinbutton'
| 'switch'
| 'tab'
| 'tablist'
| 'timer'
| 'toolbar';

// This must be kept in sync with the AccessibilityStatesMask in RCTViewManager.m
export type AccessibilityStates = $ReadOnlyArray<'disabled' | 'selected'>;
export type AccessibilityStates = $ReadOnlyArray<
| 'disabled'
| 'selected'
| 'checked'
| 'unchecked'
| 'busy'
| 'expanded'
| 'collapsed'
| 'hasPopup',
>;
27 changes: 26 additions & 1 deletion Libraries/DeprecatedPropTypes/DeprecatedViewAccessibility.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,32 @@ module.exports = {
'imagebutton',
'header',
'summary',
'alert',
'checkbox',
'combobox',
'menu',
'menubar',
'menuitem',
'progressbar',
'radio',
'radiogroup',
'scrollbar',
'spinbutton',
'switch',
'tab',
'tablist',
'timer',
'toolbar',
],
// This must be kept in sync with the AccessibilityStatesMask in RCTViewManager.m
DeprecatedAccessibilityStates: ['selected', 'disabled'],
DeprecatedAccessibilityStates: [
'selected',
'disabled',
'checked',
'unchecked',
'busy',
'expanded',
'collapsed',
'hasPopup',
],
};
287 changes: 285 additions & 2 deletions RNTester/js/AccessibilityExample.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,17 @@
'use strict';

const React = require('react');
const ReactNative = require('react-native');
const {AccessibilityInfo, Text, View, TouchableOpacity, Alert} = ReactNative;
const {
AccessibilityInfo,
Button,
Text,
View,
TouchableOpacity,
Alert,
UIManager,
findNodeHandle,
Platform,
} = require('react-native');

const RNTesterBlock = require('./RNTesterBlock');

Expand Down Expand Up @@ -133,6 +142,274 @@ class AccessibilityExample extends React.Component {
}
}

class CheckboxExample extends React.Component {
state = {
checkboxState: 'checked',
};

_onCheckboxPress = () => {
const checkboxState =
this.state.checkboxState === 'checked' ? 'unchecked' : 'checked';

this.setState({
checkboxState: checkboxState,
});

if (Platform.OS === 'android') {
UIManager.sendAccessibilityEvent(
findNodeHandle(this),
UIManager.AccessibilityEventTypes.typeViewClicked,
);
}
};

render() {
return (
<TouchableOpacity
onPress={this._onCheckboxPress}
accessibilityLabel="element 2"
accessibilityRole="checkbox"
accessibilityStates={[this.state.checkboxState]}
accessibilityHint="click me to change state">
<Text>Checkbox example</Text>
</TouchableOpacity>
);
}
}

class SwitchExample extends React.Component {
state = {
switchState: 'checked',
};

_onSwitchToggle = () => {
const switchState =
this.state.switchState === 'checked' ? 'unchecked' : 'checked';

this.setState({
switchState: switchState,
});

if (Platform.OS === 'android') {
UIManager.sendAccessibilityEvent(
findNodeHandle(this),
UIManager.AccessibilityEventTypes.typeViewClicked,
);
}
};

render() {
return (
<TouchableOpacity
onPress={this._onSwitchToggle}
accessibilityLabel="element 12"
accessibilityRole="switch"
accessibilityStates={[this.state.switchState]}
accessible={true}>
<Text>Switch example</Text>
</TouchableOpacity>
);
}
}

class SelectionExample extends React.Component {
constructor(props) {
super(props);
this.selectableElement = React.createRef();
}

state = {
isSelected: true,
isEnabled: false,
};

render() {
let accessibilityStates = [];
let accessibilityHint = 'click me to select';
if (this.state.isSelected) {
accessibilityStates.push('selected');
accessibilityHint = 'click me to unselect';
}
if (!this.state.isEnabled) {
accessibilityStates.push('disabled');
accessibilityHint = 'use the button on the right to enable selection';
}
let buttonTitle = this.state.isEnabled
? 'Disable selection'
: 'Enable selection';

return (
<View style={{flex: 1, flexDirection: 'row'}}>
<TouchableOpacity
ref={this.selectableElement}
accessible={true}
onPress={() => {
this.setState({
isSelected: !this.state.isSelected,
});

if (Platform.OS === 'android') {
UIManager.sendAccessibilityEvent(
findNodeHandle(this.selectableElement.current),
UIManager.AccessibilityEventTypes.typeViewClicked,
);
}
}}
accessibilityLabel="element 19"
accessibilityStates={accessibilityStates}
accessibilityHint={accessibilityHint}>
<Text>Selectable element example</Text>
</TouchableOpacity>
<Button
onPress={() => {
this.setState({
isEnabled: !this.state.isEnabled,
});
}}
title={buttonTitle}
/>
</View>
);
}
}

class ExpandableElementExample extends React.Component {
state = {
expandState: 'collapsed',
};

_onElementPress = () => {
const expandState =
this.state.expandState === 'collapsed' ? 'expanded' : 'collapsed';

this.setState({
expandState: expandState,
});

if (Platform.OS === 'android') {
UIManager.sendAccessibilityEvent(
findNodeHandle(this),
UIManager.AccessibilityEventTypes.typeViewClicked,
);
}
};

render() {
return (
<TouchableOpacity
onPress={this._onElementPress}
accessibilityLabel="element 18"
accessibilityStates={[this.state.expandState]}
accessibilityHint="click me to change state">
<Text>Expandable element example</Text>
</TouchableOpacity>
);
}
}

class AccessibilityRoleAndStateExample extends React.Component<{}> {
render() {
return (
<View>
<View
accessibilityLabel="element 1"
accessibilityRole="alert"
accessible={true}>
<Text>Alert example</Text>
</View>
<CheckboxExample />
<View
accessibilityLabel="element 3"
accessibilityRole="combobox"
accessible={true}>
<Text>Combobox example</Text>
</View>
<View
accessibilityLabel="element 4"
accessibilityRole="menu"
accessible={true}>
<Text>Menu example</Text>
</View>
<View
accessibilityLabel="element 5"
accessibilityRole="menubar"
accessible={true}>
<Text>Menu bar example</Text>
</View>
<View
accessibilityLabel="element 6"
accessibilityRole="menuitem"
accessible={true}>
<Text>Menu item example</Text>
</View>
<View
accessibilityLabel="element 7"
accessibilityRole="progressbar"
accessible={true}>
<Text>Progress bar example</Text>
</View>
<View
accessibilityLabel="element 8"
accessibilityRole="radio"
accessible={true}>
<Text>Radio button example</Text>
</View>
<View
accessibilityLabel="element 9"
accessibilityRole="radiogroup"
accessible={true}>
<Text>Radio group example</Text>
</View>
<View
accessibilityLabel="element 10"
accessibilityRole="scrollbar"
accessible={true}>
<Text>Scrollbar example</Text>
</View>
<View
accessibilityLabel="element 11"
accessibilityRole="spinbutton"
accessible={true}>
<Text>Spin button example</Text>
</View>
<SwitchExample />
<View
accessibilityLabel="element 13"
accessibilityRole="tab"
accessible={true}>
<Text>Tab example</Text>
</View>
<View
accessibilityLabel="element 14"
accessibilityRole="tablist"
accessible={true}>
<Text>Tab list example</Text>
</View>
<View
accessibilityLabel="element 15"
accessibilityRole="timer"
accessible={true}>
<Text>Timer example</Text>
</View>
<View
accessibilityLabel="element 16"
accessibilityRole="toolbar"
accessible={true}>
<Text>Toolbar example</Text>
</View>
<View
accessibilityLabel="element 17"
accessibilityStates={['busy']}
accessible={true}>
<Text>State busy example</Text>
</View>
<ExpandableElementExample />
<SelectionExample />
</View>
);
}
}

class ScreenReaderStatusExample extends React.Component<{}> {
state = {
screenReaderEnabled: false,
Expand Down Expand Up @@ -184,6 +461,12 @@ exports.examples = [
return <AccessibilityExample />;
},
},
{
title: 'New accessibility roles and states',
render(): React.Element<typeof AccessibilityRoleAndStateExamples> {
return <AccessibilityRoleAndStateExample />;
},
},
{
title: 'Check if the screen reader is enabled',
render(): React.Element<typeof ScreenReaderStatusExample> {
Expand Down
4 changes: 4 additions & 0 deletions React/Views/RCTView.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
#import <React/RCTPointerEvents.h>
#import <React/RCTView.h>

extern const UIAccessibilityTraits SwitchAccessibilityTrait;

@protocol RCTAutoInsetsProtocol;

@class RCTView;
Expand All @@ -31,6 +33,8 @@
* Accessibility properties
*/
@property (nonatomic, copy) NSArray <NSString *> *accessibilityActions;
@property (nonatomic, copy) NSString *accessibilityRole;
@property (nonatomic, copy) NSArray <NSString *> *accessibilityStates;

/**
* Used to control how touch events are processed.
Expand Down

0 comments on commit b788d01

Please sign in to comment.