mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: Currently there isn't any way to test if the Accessibility Display Options or the event listeners for them were working properly within RNTester. It turns out that because of changes to the accessibility API Apple made when they added Smart Invert AccessibilityInfo.isInvertColorsEnabled() and the associated event listener are working incorrectly. You can see this if you take a look at how RNTester (with this PR incorporated) responds to toggling Invert Colors. I've submitted a bug report to Apple, but this might have been caught earlier if this had been implemented. ## Changelog [General] [Added] - Adding Accessibility Display Options Enabled to RNTester Pull Request resolved: https://github.com/facebook/react-native/pull/29231 Test Plan: 1. Build iOS app 2. Navigate to the Accessibility page 3. Switch over to settings and change any of the settings at - Settings>Accessibility>Display and Text Size>Bold Text - Settings>Accessibility>Display and Text Size>Color Filters>Grayscale - Settings>Accessibility>Display and Text Size>Smart Invert (or Classic Invert) - Settings>Accessibility>Motion>Reduce Motion - Settings>Accessibility>VoiceOver>VoiceOver 4. Switch back to the app and see the results   Modified it a bit to only show relevant statuses for the Platform | {F656951547} | {F656951643} | Reviewed By: TheSavior Differential Revision: D29531642 Pulled By: lunaleaps fbshipit-source-id: f54fb3216ef663428c23b613f0a3b10b01d7f24d
1167 lines
33 KiB
JavaScript
1167 lines
33 KiB
JavaScript
/**
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @format
|
|
* @flow
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
const React = require('react');
|
|
const {
|
|
AccessibilityInfo,
|
|
TextInput,
|
|
Button,
|
|
Image,
|
|
Text,
|
|
View,
|
|
TouchableOpacity,
|
|
TouchableWithoutFeedback,
|
|
Alert,
|
|
StyleSheet,
|
|
Slider,
|
|
Platform,
|
|
} = require('react-native');
|
|
import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter';
|
|
|
|
const RNTesterBlock = require('../../components/RNTesterBlock');
|
|
|
|
const checkImageSource = require('./check.png');
|
|
const uncheckImageSource = require('./uncheck.png');
|
|
const mixedCheckboxImageSource = require('./mixed.png');
|
|
const {createRef} = require('react');
|
|
|
|
const styles = StyleSheet.create({
|
|
default: {
|
|
borderWidth: StyleSheet.hairlineWidth,
|
|
borderColor: '#0f0f0f',
|
|
flex: 1,
|
|
fontSize: 13,
|
|
padding: 4,
|
|
},
|
|
touchable: {
|
|
backgroundColor: 'blue',
|
|
borderColor: 'red',
|
|
borderWidth: 1,
|
|
borderRadius: 10,
|
|
padding: 10,
|
|
borderStyle: 'solid',
|
|
},
|
|
image: {
|
|
width: 20,
|
|
height: 20,
|
|
resizeMode: 'contain',
|
|
marginRight: 10,
|
|
},
|
|
disabledImage: {
|
|
width: 120,
|
|
height: 120,
|
|
},
|
|
containerAlignCenter: {
|
|
display: 'flex',
|
|
flexDirection: 'column',
|
|
justifyContent: 'space-between',
|
|
},
|
|
});
|
|
|
|
class AccessibilityExample extends React.Component<{}> {
|
|
render(): React.Node {
|
|
return (
|
|
<View>
|
|
<RNTesterBlock title="TextView without label">
|
|
<Text>
|
|
Text's accessibilityLabel is the raw text itself unless it is set
|
|
explicitly.
|
|
</Text>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="TextView with label">
|
|
<Text accessibilityLabel="I have label, so I read it instead of embedded text.">
|
|
This text component's accessibilityLabel is set explicitly.
|
|
</Text>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Nonaccessible view with TextViews">
|
|
<View>
|
|
<Text style={{color: 'green'}}>This is text one.</Text>
|
|
<Text style={{color: 'blue'}}>This is text two.</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Accessible view with TextViews wihout label">
|
|
<View accessible={true}>
|
|
<Text style={{color: 'green'}}>This is text one.</Text>
|
|
<Text style={{color: 'blue'}}>This is text two.</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Accessible view with TextViews with label">
|
|
<View
|
|
accessible={true}
|
|
accessibilityLabel="I have label, so I read it instead of embedded text.">
|
|
<Text style={{color: 'green'}}>This is text one.</Text>
|
|
<Text style={{color: 'blue'}}>This is text two.</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
{/* Android screen readers will say the accessibility hint instead of the text
|
|
since the view doesn't have a label. */}
|
|
<RNTesterBlock title="Accessible view with TextViews with hint">
|
|
<View accessibilityHint="Accessibility hint." accessible={true}>
|
|
<Text style={{color: 'green'}}>This is text one.</Text>
|
|
<Text style={{color: 'blue'}}>This is text two.</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Accessible view TextViews with label and hint">
|
|
<View
|
|
accessibilityLabel="Accessibility label."
|
|
accessibilityHint="Accessibility hint."
|
|
accessible={true}>
|
|
<Text style={{color: 'green'}}>This is text one.</Text>
|
|
<Text style={{color: 'blue'}}>This is text two.</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Text with accessibilityRole = header">
|
|
<Text accessibilityRole="header">This is a title.</Text>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Touchable with accessibilityRole = link">
|
|
<TouchableOpacity
|
|
onPress={() => Alert.alert('Link has been clicked!')}
|
|
accessibilityRole="link">
|
|
<View>
|
|
<Text>Click me</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Touchable with accessibilityRole = button">
|
|
<TouchableOpacity
|
|
onPress={() => Alert.alert('Button has been pressed!')}
|
|
accessibilityRole="button">
|
|
<Text>Click me</Text>
|
|
</TouchableOpacity>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Disabled Touchable with role">
|
|
<TouchableOpacity
|
|
onPress={() => Alert.alert('Button has been pressed!')}
|
|
accessibilityRole="button"
|
|
accessibilityState={{disabled: true}}
|
|
disabled={true}>
|
|
<View>
|
|
<Text>
|
|
I am disabled. Clicking me will not trigger any action.
|
|
</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Disabled TouchableOpacity">
|
|
<TouchableOpacity
|
|
onPress={() => Alert.alert('Disabled Button has been pressed!')}
|
|
accessibilityLabel={'You are pressing Disabled TouchableOpacity'}
|
|
accessibilityState={{disabled: true}}>
|
|
<View>
|
|
<Text>
|
|
I am disabled. Clicking me will not trigger any action.
|
|
</Text>
|
|
</View>
|
|
</TouchableOpacity>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock title="View with multiple states">
|
|
<View
|
|
accessible={true}
|
|
accessibilityState={{selected: true, disabled: true}}>
|
|
<Text>This view is selected and disabled.</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="View with label, hint, role, and state">
|
|
<View
|
|
accessible={true}
|
|
accessibilityLabel="Accessibility label."
|
|
accessibilityRole="button"
|
|
accessibilityState={{selected: true}}
|
|
accessibilityHint="Accessibility hint.">
|
|
<Text>Accessible view with label, hint, role, and state</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class CheckboxExample extends React.Component<
|
|
{},
|
|
{
|
|
checkboxState: boolean | 'mixed',
|
|
},
|
|
> {
|
|
state = {
|
|
checkboxState: true,
|
|
};
|
|
|
|
_onCheckboxPress = () => {
|
|
let checkboxState = false;
|
|
if (this.state.checkboxState === false) {
|
|
checkboxState = 'mixed';
|
|
} else if (this.state.checkboxState === 'mixed') {
|
|
checkboxState = true;
|
|
} else {
|
|
checkboxState = false;
|
|
}
|
|
|
|
this.setState({
|
|
checkboxState: checkboxState,
|
|
});
|
|
};
|
|
|
|
render() {
|
|
return (
|
|
<TouchableOpacity
|
|
onPress={this._onCheckboxPress}
|
|
accessibilityLabel="element 2"
|
|
accessibilityRole="checkbox"
|
|
accessibilityState={{checked: this.state.checkboxState}}
|
|
accessibilityHint="click me to change state">
|
|
<Text>Checkbox example</Text>
|
|
</TouchableOpacity>
|
|
);
|
|
}
|
|
}
|
|
|
|
class SwitchExample extends React.Component<
|
|
{},
|
|
{
|
|
switchState: boolean,
|
|
},
|
|
> {
|
|
state = {
|
|
switchState: true,
|
|
};
|
|
|
|
_onSwitchToggle = () => {
|
|
const switchState = !this.state.switchState;
|
|
|
|
this.setState({
|
|
switchState: switchState,
|
|
});
|
|
};
|
|
|
|
render() {
|
|
return (
|
|
<TouchableOpacity
|
|
onPress={this._onSwitchToggle}
|
|
accessibilityLabel="element 12"
|
|
accessibilityRole="switch"
|
|
accessibilityState={{checked: this.state.switchState}}
|
|
accessible={true}>
|
|
<Text>Switch example</Text>
|
|
</TouchableOpacity>
|
|
);
|
|
}
|
|
}
|
|
|
|
class SelectionExample extends React.Component<
|
|
{},
|
|
{
|
|
isSelected: boolean,
|
|
isEnabled: boolean,
|
|
},
|
|
> {
|
|
constructor(props: {}) {
|
|
super(props);
|
|
this.selectableElement = createRef();
|
|
}
|
|
selectableElement: {
|
|
current: React.ElementRef<typeof TouchableOpacity> | null,
|
|
};
|
|
|
|
state = {
|
|
isSelected: true,
|
|
isEnabled: false,
|
|
};
|
|
|
|
render(): React.Node {
|
|
const {isSelected, isEnabled} = this.state;
|
|
let accessibilityHint = 'click me to select';
|
|
if (isSelected) {
|
|
accessibilityHint = 'click me to unselect';
|
|
}
|
|
if (!isEnabled) {
|
|
accessibilityHint = 'use the button on the right to enable selection';
|
|
}
|
|
let buttonTitle = isEnabled ? 'Disable selection' : 'Enable selection';
|
|
const touchableHint = ` (touching the TouchableOpacity will ${
|
|
isSelected ? 'disable' : 'enable'
|
|
} accessibilityState.selected)`;
|
|
return (
|
|
<View style={styles.containerAlignCenter}>
|
|
<TouchableOpacity
|
|
ref={this.selectableElement}
|
|
accessible={true}
|
|
onPress={() => {
|
|
if (isEnabled) {
|
|
this.setState({
|
|
isSelected: !isSelected,
|
|
});
|
|
} else {
|
|
console.warn('selection is disabled, please enable selection.');
|
|
}
|
|
}}
|
|
accessibilityLabel="element 19"
|
|
accessibilityState={{
|
|
selected: isSelected,
|
|
disabled: !isEnabled,
|
|
}}
|
|
style={styles.touchable}
|
|
accessibilityHint={accessibilityHint}>
|
|
<Text style={{color: 'white'}}>
|
|
{`Selectable TouchableOpacity Example ${touchableHint}`}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
<TextInput
|
|
accessibilityLabel="element 20"
|
|
accessibilityState={{
|
|
selected: isSelected,
|
|
}}
|
|
multiline={true}
|
|
placeholder={`TextInput Example - ${
|
|
isSelected ? 'enabled' : 'disabled'
|
|
} selection`}
|
|
/>
|
|
<Button
|
|
onPress={() => {
|
|
this.setState({
|
|
isEnabled: !this.state.isEnabled,
|
|
});
|
|
}}
|
|
title={buttonTitle}
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class ExpandableElementExample extends React.Component<
|
|
{},
|
|
{
|
|
expandState: boolean,
|
|
},
|
|
> {
|
|
state = {
|
|
expandState: false,
|
|
};
|
|
|
|
_onElementPress = () => {
|
|
const expandState = !this.state.expandState;
|
|
|
|
this.setState({
|
|
expandState: expandState,
|
|
});
|
|
};
|
|
|
|
render() {
|
|
return (
|
|
<TouchableOpacity
|
|
onPress={this._onElementPress}
|
|
accessibilityLabel="element 18"
|
|
accessibilityState={{expanded: this.state.expandState}}
|
|
accessibilityHint="click me to change state">
|
|
<Text>Expandable element example</Text>
|
|
</TouchableOpacity>
|
|
);
|
|
}
|
|
}
|
|
|
|
class NestedCheckBox extends React.Component<
|
|
{},
|
|
{
|
|
checkbox1: boolean | 'mixed',
|
|
checkbox2: boolean | 'mixed',
|
|
checkbox3: boolean | 'mixed',
|
|
},
|
|
> {
|
|
state = {
|
|
checkbox1: false,
|
|
checkbox2: false,
|
|
checkbox3: false,
|
|
};
|
|
|
|
_onPress1 = () => {
|
|
let checkbox1 = false;
|
|
if (this.state.checkbox1 === false) {
|
|
checkbox1 = true;
|
|
} else if (this.state.checkbox1 === 'mixed') {
|
|
checkbox1 = false;
|
|
} else {
|
|
checkbox1 = false;
|
|
}
|
|
setTimeout(() => {
|
|
this.setState({
|
|
checkbox1: checkbox1,
|
|
checkbox2: checkbox1,
|
|
checkbox3: checkbox1,
|
|
});
|
|
}, 2000);
|
|
};
|
|
|
|
_onPress2 = () => {
|
|
const checkbox2 = !this.state.checkbox2;
|
|
|
|
this.setState({
|
|
checkbox2: checkbox2,
|
|
checkbox1:
|
|
checkbox2 && this.state.checkbox3
|
|
? true
|
|
: checkbox2 || this.state.checkbox3
|
|
? 'mixed'
|
|
: false,
|
|
});
|
|
};
|
|
|
|
_onPress3 = () => {
|
|
const checkbox3 = !this.state.checkbox3;
|
|
|
|
this.setState({
|
|
checkbox3: checkbox3,
|
|
checkbox1:
|
|
this.state.checkbox2 && checkbox3
|
|
? true
|
|
: this.state.checkbox2 || checkbox3
|
|
? 'mixed'
|
|
: false,
|
|
});
|
|
};
|
|
|
|
render() {
|
|
return (
|
|
<View>
|
|
<TouchableOpacity
|
|
style={{flex: 1, flexDirection: 'row'}}
|
|
onPress={this._onPress1}
|
|
accessibilityLabel="Meat"
|
|
accessibilityHint="State changes in 2 seconds after clicking."
|
|
accessibilityRole="checkbox"
|
|
accessibilityState={{checked: this.state.checkbox1}}>
|
|
<Image
|
|
style={styles.image}
|
|
source={
|
|
this.state.checkbox1 === 'mixed'
|
|
? mixedCheckboxImageSource
|
|
: this.state.checkbox1
|
|
? checkImageSource
|
|
: uncheckImageSource
|
|
}
|
|
/>
|
|
<Text>Meat</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity
|
|
style={{flex: 1, flexDirection: 'row'}}
|
|
onPress={this._onPress2}
|
|
accessibilityLabel="Beef"
|
|
accessibilityRole="checkbox"
|
|
accessibilityState={{checked: this.state.checkbox2}}>
|
|
<Image
|
|
style={styles.image}
|
|
source={
|
|
this.state.checkbox2 ? checkImageSource : uncheckImageSource
|
|
}
|
|
/>
|
|
<Text>Beef</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity
|
|
style={{flex: 1, flexDirection: 'row'}}
|
|
onPress={this._onPress3}
|
|
accessibilityLabel="Bacon"
|
|
accessibilityRole="checkbox"
|
|
accessibilityState={{checked: this.state.checkbox3}}>
|
|
<Image
|
|
style={styles.image}
|
|
source={
|
|
this.state.checkbox3 ? checkImageSource : uncheckImageSource
|
|
}
|
|
/>
|
|
<Text>Bacon</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class AccessibilityRoleAndStateExample extends React.Component<{}> {
|
|
render(): React.Node {
|
|
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"
|
|
accessibilityState={{busy: true}}
|
|
accessible={true}>
|
|
<Text>State busy example</Text>
|
|
</View>
|
|
<ExpandableElementExample />
|
|
<SelectionExample />
|
|
<RNTesterBlock title="Nested checkbox with delayed state change">
|
|
<NestedCheckBox />
|
|
</RNTesterBlock>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class AccessibilityActionsExample extends React.Component<{}> {
|
|
render(): React.Node {
|
|
return (
|
|
<View>
|
|
<RNTesterBlock title="Non-touchable with activate action">
|
|
<View
|
|
accessible={true}
|
|
accessibilityActions={[{name: 'activate'}]}
|
|
onAccessibilityAction={event => {
|
|
switch (event.nativeEvent.actionName) {
|
|
case 'activate':
|
|
Alert.alert('Alert', 'View is clicked');
|
|
break;
|
|
}
|
|
}}>
|
|
<Text>Click me</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="View with multiple actions">
|
|
<View
|
|
accessible={true}
|
|
accessibilityActions={[
|
|
{name: 'cut', label: 'cut label'},
|
|
{name: 'copy', label: 'copy label'},
|
|
{name: 'paste', label: 'paste label'},
|
|
]}
|
|
onAccessibilityAction={event => {
|
|
switch (event.nativeEvent.actionName) {
|
|
case 'cut':
|
|
Alert.alert('Alert', 'cut action success');
|
|
break;
|
|
case 'copy':
|
|
Alert.alert('Alert', 'copy action success');
|
|
break;
|
|
case 'paste':
|
|
Alert.alert('Alert', 'paste action success');
|
|
break;
|
|
}
|
|
}}>
|
|
<Text>This view supports many actions.</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Adjustable with increment/decrement actions">
|
|
<View
|
|
accessible={true}
|
|
accessibilityRole="adjustable"
|
|
accessibilityActions={[{name: 'increment'}, {name: 'decrement'}]}
|
|
onAccessibilityAction={event => {
|
|
switch (event.nativeEvent.actionName) {
|
|
case 'increment':
|
|
Alert.alert('Alert', 'increment action success');
|
|
break;
|
|
case 'decrement':
|
|
Alert.alert('Alert', 'decrement action success');
|
|
break;
|
|
}
|
|
}}>
|
|
<Text>Slider</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="TouchableWithoutFeedback with custom accessibility actions">
|
|
<TouchableWithoutFeedback
|
|
accessible={true}
|
|
accessibilityActions={[
|
|
{name: 'cut', label: 'cut label'},
|
|
{name: 'copy', label: 'copy label'},
|
|
{name: 'paste', label: 'paste label'},
|
|
]}
|
|
onAccessibilityAction={event => {
|
|
switch (event.nativeEvent.actionName) {
|
|
case 'cut':
|
|
Alert.alert('Alert', 'cut action success');
|
|
break;
|
|
case 'copy':
|
|
Alert.alert('Alert', 'copy action success');
|
|
break;
|
|
case 'paste':
|
|
Alert.alert('Alert', 'paste action success');
|
|
break;
|
|
}
|
|
}}
|
|
onPress={() => Alert.alert('Button has been pressed!')}
|
|
accessibilityRole="button">
|
|
<View>
|
|
<Text>Click me</Text>
|
|
</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>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
function SliderAccessibilityExample(): React.Node {
|
|
return (
|
|
<View>
|
|
<RNTesterBlock
|
|
title="Disabled Slider via disabled"
|
|
description="Verify with TalkBack/VoiceOver announces Slider as disabled">
|
|
<Slider value={25} maximumValue={100} minimumValue={0} disabled />
|
|
</RNTesterBlock>
|
|
<RNTesterBlock
|
|
title="Disabled Slider via accessibiltyState"
|
|
description="Verify with TalkBack/VoiceOver announces Slider as disabled">
|
|
<Slider
|
|
value={75}
|
|
maximumValue={100}
|
|
minimumValue={0}
|
|
accessibilityState={{disabled: true}}
|
|
/>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock
|
|
title="Selected Slider"
|
|
description="Verify with TalkBack/VoiceOver announces Slider as selected">
|
|
<Slider
|
|
value={75}
|
|
maximumValue={100}
|
|
minimumValue={0}
|
|
accessibilityState={{selected: true}}
|
|
/>
|
|
</RNTesterBlock>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
type FakeSliderExampleState = {
|
|
current: number,
|
|
textualValue: 'center' | 'left' | 'right',
|
|
};
|
|
class FakeSliderExample extends React.Component<{}, FakeSliderExampleState> {
|
|
state: FakeSliderExampleState = {
|
|
current: 50,
|
|
textualValue: 'center',
|
|
};
|
|
|
|
increment: () => void = () => {
|
|
let newValue = this.state.current + 2;
|
|
if (newValue > 100) {
|
|
newValue = 100;
|
|
}
|
|
this.setState({
|
|
current: newValue,
|
|
});
|
|
};
|
|
|
|
decrement: () => void = () => {
|
|
let newValue = this.state.current - 2;
|
|
if (newValue < 0) {
|
|
newValue = 0;
|
|
}
|
|
this.setState({
|
|
current: newValue,
|
|
});
|
|
};
|
|
|
|
render(): React.Node {
|
|
return (
|
|
<View>
|
|
<View
|
|
accessible={true}
|
|
accessibilityLabel="Fake Slider"
|
|
accessibilityRole="adjustable"
|
|
accessibilityActions={[{name: 'increment'}, {name: 'decrement'}]}
|
|
onAccessibilityAction={event => {
|
|
switch (event.nativeEvent.actionName) {
|
|
case 'increment':
|
|
this.increment();
|
|
break;
|
|
case 'decrement':
|
|
this.decrement();
|
|
break;
|
|
}
|
|
}}
|
|
accessibilityValue={{
|
|
min: 0,
|
|
now: this.state.current,
|
|
max: 100,
|
|
}}>
|
|
<Text>Fake Slider</Text>
|
|
</View>
|
|
<TouchableWithoutFeedback
|
|
accessible={true}
|
|
accessibilityLabel="Equalizer"
|
|
accessibilityRole="adjustable"
|
|
accessibilityActions={[{name: 'increment'}, {name: 'decrement'}]}
|
|
onAccessibilityAction={event => {
|
|
switch (event.nativeEvent.actionName) {
|
|
case 'increment':
|
|
if (this.state.textualValue === 'center') {
|
|
this.setState({textualValue: 'right'});
|
|
} else if (this.state.textualValue === 'left') {
|
|
this.setState({textualValue: 'center'});
|
|
}
|
|
break;
|
|
case 'decrement':
|
|
if (this.state.textualValue === 'center') {
|
|
this.setState({textualValue: 'left'});
|
|
} else if (this.state.textualValue === 'right') {
|
|
this.setState({textualValue: 'center'});
|
|
}
|
|
break;
|
|
}
|
|
}}
|
|
accessibilityValue={{text: this.state.textualValue}}>
|
|
<View>
|
|
<Text>Equalizer</Text>
|
|
</View>
|
|
</TouchableWithoutFeedback>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class AnnounceForAccessibility extends React.Component<{}> {
|
|
_handleOnPress = () =>
|
|
AccessibilityInfo.announceForAccessibility('Announcement Test');
|
|
|
|
render(): React.Node {
|
|
return (
|
|
<View>
|
|
<Button
|
|
onPress={this._handleOnPress}
|
|
title="Announce for Accessibility"
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class SetAccessibilityFocusExample extends React.Component<{}> {
|
|
render(): React.Node {
|
|
const myRef: {current: React.ElementRef<any> | null} = createRef();
|
|
|
|
const onClose = () => {
|
|
if (myRef && myRef.current) {
|
|
AccessibilityInfo.sendAccessibilityEvent_unstable(
|
|
myRef.current,
|
|
'focus',
|
|
);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<View>
|
|
<Text>SetAccessibilityFocus on native element</Text>
|
|
<Button
|
|
ref={myRef}
|
|
title={'Click'}
|
|
onPress={() => {
|
|
Alert.alert(
|
|
'Set Accessibility Focus',
|
|
'Press okay to proceed',
|
|
[{text: 'Okay', onPress: onClose}],
|
|
{cancelable: true},
|
|
);
|
|
}}
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class EnabledExamples extends React.Component<{}> {
|
|
render(): React.Node {
|
|
return (
|
|
<View>
|
|
{Platform.OS === 'ios' ? (
|
|
<>
|
|
<RNTesterBlock title="isBoldTextEnabled()">
|
|
<EnabledExample
|
|
test="bold text"
|
|
eventListener="boldTextChanged"
|
|
/>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock title="isGrayScaleEnabled()">
|
|
<EnabledExample
|
|
test="gray scale"
|
|
eventListener="grayscaleChanged"
|
|
/>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock title="isInvertColorsEnabled()">
|
|
<EnabledExample
|
|
test="invert colors"
|
|
eventListener="invertColorsChanged"
|
|
/>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock title="isReduceTransparencyEnabled()">
|
|
<EnabledExample
|
|
test="reduce transparency"
|
|
eventListener="reduceTransparencyChanged"
|
|
/>
|
|
</RNTesterBlock>
|
|
</>
|
|
) : null}
|
|
|
|
<RNTesterBlock title="isReduceMotionEnabled()">
|
|
<EnabledExample
|
|
test="reduce motion"
|
|
eventListener="reduceMotionChanged"
|
|
/>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="isScreenReaderEnabled()">
|
|
<EnabledExample
|
|
test="screen reader"
|
|
eventListener="screenReaderChanged"
|
|
/>
|
|
</RNTesterBlock>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class EnabledExample extends React.Component<
|
|
{
|
|
eventListener:
|
|
| 'reduceMotionChanged'
|
|
| 'boldTextChanged'
|
|
| 'grayscaleChanged'
|
|
| 'invertColorsChanged'
|
|
| 'reduceTransparencyChanged'
|
|
| 'reduceMotionChanged'
|
|
| 'screenReaderChanged',
|
|
test: string,
|
|
},
|
|
{
|
|
isEnabled: boolean,
|
|
},
|
|
> {
|
|
state = {
|
|
isEnabled: false,
|
|
};
|
|
_subscription: EventSubscription;
|
|
componentDidMount() {
|
|
this._subscription = AccessibilityInfo.addEventListener(
|
|
this.props.eventListener,
|
|
this._handleToggled,
|
|
);
|
|
|
|
switch (this.props.eventListener) {
|
|
case 'reduceMotionChanged':
|
|
return AccessibilityInfo.isReduceMotionEnabled().then(state => {
|
|
this.setState({isEnabled: state});
|
|
});
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
this._subscription?.remove();
|
|
}
|
|
|
|
_handleToggled = isEnabled => {
|
|
if (!this.state.isEnabled) {
|
|
this.setState({isEnabled: true});
|
|
} else {
|
|
this.setState({isEnabled: false});
|
|
}
|
|
};
|
|
|
|
render(): React.Node {
|
|
return (
|
|
<View>
|
|
<Text>
|
|
The {this.props.test} is{' '}
|
|
{this.state.isEnabled ? 'enabled' : 'disabled'}
|
|
</Text>
|
|
<Button
|
|
title={this.state.isEnabled ? 'disable' : 'enable'}
|
|
onPress={this._handleToggled}
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class DisplayOptionsStatusExample extends React.Component<{}> {
|
|
render(): React.Node {
|
|
const isAndroid = Platform.OS === 'android';
|
|
return (
|
|
<View>
|
|
<DisplayOptionStatusExample
|
|
optionName={'Reduce Motion'}
|
|
optionChecker={AccessibilityInfo.isReduceMotionEnabled}
|
|
notification={'reduceMotionChanged'}
|
|
/>
|
|
<DisplayOptionStatusExample
|
|
optionName={'Screen Reader'}
|
|
optionChecker={AccessibilityInfo.isScreenReaderEnabled}
|
|
notification={'reduceMotionChanged'}
|
|
/>
|
|
{isAndroid ? null : (
|
|
<>
|
|
<DisplayOptionStatusExample
|
|
optionName={'Bold Text'}
|
|
optionChecker={AccessibilityInfo.isBoldTextEnabled}
|
|
notification={'boldTextChanged'}
|
|
/>
|
|
<DisplayOptionStatusExample
|
|
optionName={'Grayscale'}
|
|
optionChecker={AccessibilityInfo.isGrayscaleEnabled}
|
|
notification={'grayscaleChanged'}
|
|
/>
|
|
<DisplayOptionStatusExample
|
|
optionName={'Invert Colors'}
|
|
optionChecker={AccessibilityInfo.isInvertColorsEnabled}
|
|
notification={'invertColorsChanged'}
|
|
/>
|
|
<DisplayOptionStatusExample
|
|
optionName={'Reduce Transparency'}
|
|
optionChecker={AccessibilityInfo.isReduceTransparencyEnabled}
|
|
notification={'reduceTransparencyChanged'}
|
|
/>
|
|
</>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
function DisplayOptionStatusExample({optionName, optionChecker, notification}) {
|
|
const [statusEnabled, setStatusEnabled] = React.useState(false);
|
|
React.useEffect(() => {
|
|
AccessibilityInfo.addEventListener(notification, setStatusEnabled);
|
|
optionChecker().then(isEnabled => {
|
|
setStatusEnabled(isEnabled);
|
|
});
|
|
return function cleanup() {
|
|
AccessibilityInfo.removeEventListener(notification, setStatusEnabled);
|
|
};
|
|
}, [optionChecker, notification]);
|
|
return (
|
|
<View>
|
|
<Text>
|
|
{optionName}
|
|
{' is '}
|
|
{statusEnabled ? 'enabled' : 'disabled'}.
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
exports.title = 'Accessibility';
|
|
exports.documentationURL = 'https://reactnative.dev/docs/accessibilityinfo';
|
|
exports.description = 'Examples of using Accessibility APIs.';
|
|
exports.examples = [
|
|
{
|
|
title: 'Accessibility elements',
|
|
render(): React.Element<typeof AccessibilityExample> {
|
|
return <AccessibilityExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'New accessibility roles and states',
|
|
render(): React.Element<typeof AccessibilityRoleAndStateExample> {
|
|
return <AccessibilityRoleAndStateExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Accessibility action examples',
|
|
render(): React.Element<typeof AccessibilityActionsExample> {
|
|
return <AccessibilityActionsExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Slider Accessibility Examples',
|
|
render(): React.Element<typeof SliderAccessibilityExample> {
|
|
return <SliderAccessibilityExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Fake Slider Example',
|
|
render(): React.Element<typeof FakeSliderExample> {
|
|
return <FakeSliderExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Check if the display options are enabled',
|
|
render(): React.Element<typeof DisplayOptionsStatusExample> {
|
|
return <DisplayOptionsStatusExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Check if the screen reader announces',
|
|
render(): React.Element<typeof AnnounceForAccessibility> {
|
|
return <AnnounceForAccessibility />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Check if accessibility is focused',
|
|
render(): React.Element<typeof SetAccessibilityFocusExample> {
|
|
return <SetAccessibilityFocusExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Check if these properties are enabled',
|
|
render(): React.Element<typeof EnabledExamples> {
|
|
return <EnabledExamples />;
|
|
},
|
|
},
|
|
{
|
|
title:
|
|
'Check if accessibilityState disabled is announced when the screenreader focus moves on the image',
|
|
render(): React.Element<typeof Image> {
|
|
return (
|
|
<Image
|
|
accessible={true}
|
|
accessibilityLabel="plain local image"
|
|
accessibilityState={{disabled: true}}
|
|
source={require('../../assets/like.png')}
|
|
style={styles.disabledImage}
|
|
/>
|
|
);
|
|
},
|
|
},
|
|
];
|