mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Summary: **Android**: The functionality consists of calling the [AccessibilityNodeInfo#setError][10] and [#setContentInvalid][13] method to display the error message in the TextInput. **Fixes [https://github.com/facebook/react-native/issues/30848][51] - Adding an accessibilityErrorMessage prop to the TextInput Component**: **Android**: The prop accessibilityErrorMessage triggers the AccessibilityNodeInfo method [setError][10] which automatically sets the correct properties on the AccessibilityNodeInfo that will inform screen readers of this state. The method calls setContentInvalid(true) and setError(youErrorString) on the AccessibilityNodeInfo. **Fixes [https://github.com/facebook/react-native/issues/30859][52] - Detecting changes in the Error state (text inputs)** **Fabric - Android** - Adding accessibilityErrorMessage to field AndroidTextInputState. ReactTextInputManager and ReactEditText receive state updates both from [Javascript][32] and [cpp (fabric)][34]. - accessibilityErrorMessage is added to the fabric AndroidTextInputState field - The updates are received in the ReactAndroid API with method updateState from ReactTextInputManager - After updating the TextInput text with onChangeText, the update of the accessibilityErrorMessage is triggered with method maybeSetAccessibilityError which triggers [setError][10]. More info: - An explanation of [state updates between fabric and ReactAndroid for the TextInput component][34] - [ReactNative renderer state updates][35] **Paper - Android** - Adding accessibilityErrorMessage to ReactTextInputShadowNode to trigger updates in Paper renderer when accessibilityErrorMessage is changed within the onChange callback. Related Links (Android): - [In this diff I'm shipping and deleting mapBufferSerialization for Text measurement][101] - [This diff implement and integrates Mapbuffer into Fabric text measure system][39] - [Refactor ViewPropsMapBuffer -> general MapBuffer props mechanism][100] - [TextInput: support modifying TextInputs with multiple Fragments (Cxx side)][24] - [TextInput: keep C++ state in-sync with updated AttributedStrings in Java][23] - [AccessibilityNodeInfo#setError][11] - [Explanation on how TextInput calls SET_TEXT_AND_SELECTION in Java API][32] - [Fabric: convertRawProp was extended to accept an optional default value][27] - [understanding onChangeText callback][31] - [Editable method replace()][12] - [Change of error state from onChangeText show/hides a TextInput error][30] - [AndroidTextInput: support using commands instead of setNativeProps (native change)][25] - [TextInput: support editing completely empty TextInputs][26] - [[Android] Fix letters duplication when using autoCapitalize https://github.com/facebook/react-native/issues/29070][40] - [Support optional types for C++ TurboModules][28] - [discussion on using announceForAccessibility in ReactEditText][36] - [ fix annoucement delayed to next character][61] - [Announce accessibility state changes happening in the background][29] - [Refactor MountingManager into MountingManager + SurfaceMountingManager][37] iOS Functionalities are included in separate PR https://github.com/facebook/react-native/pull/35908 Documentation PR https://github.com/facebook/react-native-website/pull/3010 Next PR [2/2 TextInput accessibilityErrorMessage (VoiceOver, iOS) https://github.com/facebook/react-native/issues/35908](https://github.com/facebook/react-native/pull/35908) Related https://github.com/facebook/react-native-deprecated-modules/pull/18 ## Changelog [Android] [Added] - Adding TextInput prop accessibilityErrorMessage to announce with TalkBack screenreaders Pull Request resolved: https://github.com/facebook/react-native/pull/33468 Test Plan: **Android - 20 Jan 2023** https://github.com/facebook/react-native/pull/33468#issuecomment-1398228674 **iOS - 20 Jan 2023** https://github.com/facebook/react-native/pull/33468#issuecomment-1398249006 <details><summary>CLICK TO OPEN OLD VIDEO TEST CASES</summary> <p> **PR Branch - Android and iOS 24th June** [88]: Android - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event (Fabric) ([link][88]) **PR Branch - Android** [1]. Test Cases of the functionality (Fabric) ([link][1]) [2]. Test Cases of the functionality (Paper) ([link][2]) **Main Branch** [6]. Android - Runtime Error in main branch when passing value of 1 to TextInput placeholder prop ([link][6]) **Issues Solved** [7]. TalkBack error does not clear error on the next typed character when using onChangeText ([link][7]) **Other Tests** [8]. Setting the TextInput errorMessage state with setTextAndSelection Java API from JavaScript ([link][8]) [9]. Setting the TextInput errorMessage state from fabric TextInput internal state to Java ReactTextUpdate API ([link][9]) </p> </details> [1]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1072101477 "Test Cases of the functionality (Android - Fabric)" [2]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1105964322 "Test Cases of the functionality (Android - Paper)" [3]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1116329282 "Test Cases of the functionality (iOS - Fabric)" [6]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1087020844 "Runtime Error in main branch when passing value of 1 to TextInput placeholder prop" [7]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1096086753 "TalkBack error announcement done on next typed character with onChangeText" [8]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1082594363 "setting the TextInput errorMessage state with setTextAndSelection Java API from JavaScript" [9]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1082598745 "Setting the TextInput errorMessage state from fabric TextInput internal state to Java ReactTextUpdate API" [10]: https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setError(java.lang.CharSequence) "AOSP setError" [11]: https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setError(java.lang.CharSequence) "AccessibilityNodeInfo#setError" [12]: https://github.com/aosp-mirror/platform_frameworks_base/blob/1ac46f932ef88a8f96d652580d8105e361ffc842/core/java/android/text/Editable.java#L28-L52 "Editable method replace" [13]: https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setContentInvalid(boolean) "setContentInvalid" [20]: https://github.com/facebook/react-native/commit/60b6c9be8e811241039a6db5dc906a0e88e6ba82 "draft implementation of android_errorMessage " [21]: https://github.com/facebook/react-native/commit/012d92d0b7e5de2436f186cdbff32ba128e537d5 "add errorMessage to ReactTextUpdate and maybeSetAccessibilityError" [22]: https://github.com/fabriziobertoglio1987/react-native/commit/cad239bded5748753cee2266c27809e24c6199fb "rename android_errorMessage to errorMessageAndroid" [23]: https://github.com/fabriziobertoglio1987/react-native/commit/0bae47434ef79eb606c453c5be8105b8df00783a "TextInput: keep C++ state in-sync with updated AttributedStrings in Java" [24]: https://github.com/fabriziobertoglio1987/react-native/commit/0556e86d09404105dc7ff695686b8b7c01911c5c "TextInput: support modifying TextInputs with multiple Fragments (Cxx side)" [25]: https://github.com/fabriziobertoglio1987/react-native/commit/7ab5eb4cafdea695c4c53ce2a737f6302afd6380 "AndroidTextInput: support using commands instead of setNativeProps (native change)" [26]: https://github.com/fabriziobertoglio1987/react-native/commit/b9491b7c5104066b2714045cd7710f995458c9e9 "TextInput: support editing completely empty TextInputs" [27]: https://github.com/fabriziobertoglio1987/react-native/commit/7f1ed6848f89bdccc7f7a5cc76019eec67e76b2f "Fabric: convertRawProp was extended to accept an optional default value" [28]: https://github.com/facebook/react-native/commit/6e0fa5f15eef71abcfb47750eb3669065ba2ab7d "Support optional types for C++ TurboModules" [29]: https://github.com/fabriziobertoglio1987/react-native/commit/baa66f63d8af2b772dea8ff8eda50eba264c3faf "Announce accessibility state changes happening in the background" [30]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1071989570 "Change of error state from onChangeText show/hides a TextInput error" [31]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1074827746 "understanding onChangeText callback" [32]: https://github.com/facebook/react-native/issues/29063#issuecomment-658189938 "Explanation on how TextInput calls SET_TEXT_AND_SELECTION in Java API" [33]: https://github.com/facebook/react-native/pull/33468#discussion_r835036889 "Explanation of TextInput state management with fabric C++ and JAVA API" [34]: https://github.com/facebook/react-native/pull/33468#discussion_r835036889 "state updates between fabric and ReactAndroid for the TextInput component" [35]: https://reactnative.dev/architecture/render-pipeline#react-native-renderer-state-updates "ReactNative renderer state updates" [35]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1080144483 "Analysis on how AndroidTextInputState.cpp sends updates to ReactTextInputManager" [36]: https://github.com/facebook/react-native/pull/33468#discussion_r848162849 "discussion on using announceForAccessibility in ReactEditText" [37]: https://github.com/fabriziobertoglio1987/react-native/commit/29eb632f1cb2ef5459253783eac43e5d7e999742 "Refactor MountingManager into MountingManager + SurfaceMountingManager" [38]: https://github.com/fabriziobertoglio1987/react-native/commit/733f2285067de401b925195266f4cec84c3f7fef "Diff C++ props for Android consumption" [39]: https://github.com/fabriziobertoglio1987/react-native/commit/91b3f5d48aa1322046b8c5335f8e2e1a5e702b67 "This diff implement and integrates Mapbuffer into Fabric text measure system" [40]: https://github.com/facebook/react-native/pull/29070 "[Android] Fix letters duplication when using autoCapitalize https://github.com/facebook/react-native/issues/29070" [50]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12 "Notes from work on iOS/Android: Text input error for screenreaders https://github.com/facebook/react-native/issues/12" [51]: https://github.com/facebook/react-native/issues/30848 "iOS/Android: Text input error for screenreaders https://github.com/facebook/react-native/issues/30848" [52]: https://github.com/facebook/react-native/issues/30859 "Android: Error state change (text inputs) https://github.com/facebook/react-native/issues/30859" [61]: https://github.com/facebook/react-native/pull/33468/commits/eb33c933c8bcb9a8421a6acdb7a51f261121be45 "fix annoucement delayed to next character" [70]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1116966512 "iOS - Paper renderer does not update the accessibilityValue" [71]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1124631221 "Test Cases of the functionality (Fabric) after removing changes to .cpp libs" [72]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1124892802 "Test Cases of the functionality (Paper) after removing changes to .cpp libs" [73]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1132830758 "iOS - announcing error onChangeText and screenreader focus" [74]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1150657065 "iOS - The screenreader announces the TextInput value after the errorMessage is cleared" [75]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/12#issuecomment-1152285978 "iOS - Exception thrown while executing UI block: - [RCTTextView setOnAccessibiltyAction:]: unrecognized selector sent to instance (Paper) (main branch)" [76]: https://github.com/facebook/react-native/issues/30859#issuecomment-1158790381 "iOS - announce lastChar (not entire text) onChangeText and avoid multiple announcements (Fabric)" [77]: https://github.com/facebook/react-native/issues/30859#issuecomment-1158794863 "iOS - announces or does not announce the accessibilityError through Button onPress (not onChangeText) (Fabric)" [78]: https://github.com/facebook/react-native/issues/30859#issuecomment-1158797801 "iOS - the error is announced with accessibilityInvalid true and does not clear after typing text (onChangeText) (Fabric)" [79]: https://github.com/facebook/react-native/issues/30848#issuecomment-1162799299 "iOS - Exception thrown while executing UI block: - RCTUITextView setAccessibilityErrorMessage:]: unrecognized selector sent to instance (iOS - Paper on main branch)" [80]: https://github.com/fabriziobertoglio1987/react-native/commit/e13b9c6e49480e8262df06b7c1e99caab74e801f "RCTTextField was spliited into two classes" [81]: https://github.com/fabriziobertoglio1987/react-native/commit/ee9697e5155aa972564d5aac90ceeb9db100750d "Introducing RCTBackedTextInputDelegate" [82]: https://github.com/fabriziobertoglio1987/react-native/commit/2dd2529b3ab3ace39136a6e24c09f80ae421a17e "Add option to hide context menu for TextInput" [83]: https://github.com/fabriziobertoglio1987/react-native/blob/343eea1e3150cf54d6f7727cd01d13eb7247c7f7/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentAccessibilityProvider.mm#L48-L72 "RCTParagraphComponentAccessibilityProvider accessibilityElements" [84]: https://github.com/fabriziobertoglio1987/react-native/blob/c8790a114f6f21774c43f0e9b9210e7b35d1c243/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm#L613 "RCTTextInputComponentView method _setAttributedString" [85]: https://github.com/fabriziobertoglio1987/react-native/blob/c8790a114f6f21774c43f0e9b9210e7b35d1c243/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm#L146 "RCTTextInputComponentView method updateProps" [86]: https://github.com/fabriziobertoglio1987/react-native/blob/c8790a114f6f21774c43f0e9b9210e7b35d1c243/Libraries/Text/TextInput/RCTBaseTextInputView.m#L150 "RCTBaseTextInputView setAttributedText" [87]: https://github.com/facebook/react-native/issues/30859#issuecomment-1165395361 "iOS - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event" [88]: https://github.com/facebook/react-native/issues/30859#issuecomment-1165398153 "Android - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event" [89]: https://github.com/facebook/react-native/issues/30859#issuecomment-1165413245 "iOS - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event (Fabric)" [100]: https://github.com/fabriziobertoglio1987/react-native/commit/110b191b14e3cb692bb6a33f0f129b4f0215f9a6 "Refactor ViewPropsMapBuffer -> general MapBuffer props mechanism" [101]: https://github.com/fabriziobertoglio1987/react-native/commit/22b6e1c8ec0e69700e9142cf5c9c1ab1e6a84b78 "In this diff I'm shipping and deleting mapBufferSerialization for Text measurement" Reviewed By: blavalla Differential Revision: D38410635 Pulled By: lunaleaps fbshipit-source-id: cd80e9a1be8f5ca017c979d7907974cf72ca4777
1796 lines
54 KiB
JavaScript
1796 lines
54 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and 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';
|
|
|
|
import type {PressEvent} from 'react-native/Libraries/Types/CoreEventTypes';
|
|
|
|
const React = require('react');
|
|
const {
|
|
ImageBackground,
|
|
AccessibilityInfo,
|
|
TextInput,
|
|
Button,
|
|
Image,
|
|
Text,
|
|
View,
|
|
TouchableOpacity,
|
|
TouchableNativeFeedback,
|
|
TouchableWithoutFeedback,
|
|
Alert,
|
|
StyleSheet,
|
|
Platform,
|
|
Switch,
|
|
ScrollView,
|
|
} = 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',
|
|
},
|
|
button: {
|
|
padding: 8,
|
|
borderWidth: 1,
|
|
borderColor: 'blue',
|
|
},
|
|
smallRedSquare: {
|
|
backgroundColor: 'red',
|
|
height: 40,
|
|
width: 40,
|
|
},
|
|
container: {
|
|
flex: 1,
|
|
},
|
|
ImageBackground: {
|
|
flex: 1,
|
|
justifyContent: 'center',
|
|
},
|
|
text: {
|
|
color: 'white',
|
|
fontSize: 20,
|
|
lineHeight: 84,
|
|
fontWeight: 'bold',
|
|
textAlign: 'center',
|
|
backgroundColor: '#000000c0',
|
|
},
|
|
scrollView: {
|
|
height: 50,
|
|
},
|
|
});
|
|
|
|
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 without 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>
|
|
|
|
<RNTesterBlock title="View with hidden children from accessibility tree.">
|
|
<View aria-hidden>
|
|
<Text>
|
|
This view's children are hidden from the accessibility tree
|
|
</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="Text with role = heading">
|
|
<Text role="heading">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>
|
|
|
|
<RNTesterBlock title="View with label, hint, role, and state">
|
|
<View
|
|
accessible={true}
|
|
accessibilityLabel="Accessibility label."
|
|
accessibilityRole="button"
|
|
aria-selected={true}
|
|
accessibilityHint="Accessibility hint.">
|
|
<Text>Accessible view with label, hint, role, and state</Text>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="TextInput with accessibilityLabelledBy attribute">
|
|
<View>
|
|
<Text nativeID="formLabel1">Mail Address</Text>
|
|
<TextInput
|
|
accessibilityLabel="input test1"
|
|
accessibilityLabelledBy="formLabel1"
|
|
style={styles.default}
|
|
/>
|
|
<Text nativeID="formLabel2">First Name</Text>
|
|
<TextInput
|
|
accessibilityLabel="input test2"
|
|
accessibilityLabelledBy={['formLabel2', 'formLabel3']}
|
|
style={styles.default}
|
|
value="Foo"
|
|
/>
|
|
</View>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock title="Switch with accessibilityLabelledBy attribute">
|
|
<View>
|
|
<Text nativeID="formLabel4">Enable Notifications</Text>
|
|
<Switch
|
|
value={true}
|
|
accessibilityLabel="switch test1"
|
|
accessibilityLabelledBy="formLabel4"
|
|
/>
|
|
</View>
|
|
</RNTesterBlock>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class AutomaticContentGrouping extends React.Component<{}> {
|
|
render(): React.Node {
|
|
return (
|
|
<View>
|
|
<RNTesterBlock title="The parent and the children have a different role">
|
|
<TouchableNativeFeedback accessible={true} accessibilityRole="button">
|
|
<View accessible={false}>
|
|
<Text accessibilityRole="image" accessible={false}>
|
|
Text number 1 with a role
|
|
</Text>
|
|
<Text accessible={false}>Text number 2</Text>
|
|
</View>
|
|
</TouchableNativeFeedback>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="The parent has the accessibilityActions cut, copy and paste">
|
|
<TouchableNativeFeedback
|
|
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;
|
|
}
|
|
}}
|
|
accessibilityRole="button">
|
|
<View>
|
|
<Text accessible={false}>Text number 1</Text>
|
|
<Text accessible={false}>
|
|
Text number 2<Text accessible={false}>Text number 3</Text>
|
|
</Text>
|
|
</View>
|
|
</TouchableNativeFeedback>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Talkback only pulls the child's contentDescription or text but does not include the child's accessibilityState or accessibilityRole. TalkBack avoids announcements of conflicting states or roles (for example, 'button' and 'slider').">
|
|
<View
|
|
accessible={true}
|
|
accessibilityRole="button"
|
|
accessibilityState={{checked: true}}>
|
|
<Text
|
|
accessible={false}
|
|
accessibilityState={{checked: true, disabled: false}}>
|
|
Text number 1
|
|
</Text>
|
|
<Text
|
|
style={styles.smallRedSquare}
|
|
accessible={false}
|
|
accessibilityState={{checked: false, disabled: true}}
|
|
accessibilityLabel="This child Text does not have text, but has an accessibilityLabel and accessibilityState. The child accessibility state disabled is not announced."
|
|
accessibilityRole="image"
|
|
/>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="One of the children has accessibilityLabel, role, state, and accessibilityValue.">
|
|
<View accessible={true} accessibilityRole="button">
|
|
<View>
|
|
<Text accessible={false}>Text number 1</Text>
|
|
<TouchableNativeFeedback
|
|
focusable={true}
|
|
onPress={() => console.warn('onPress child')}
|
|
accessible={false}
|
|
accessibilityLabel="this is my label"
|
|
accessibilityRole="image"
|
|
accessibilityState={{disabled: true}}
|
|
accessibilityValue={{text: 'this is the accessibility value'}}>
|
|
<Text accessible={false}>Text number 3</Text>
|
|
</TouchableNativeFeedback>
|
|
</View>
|
|
</View>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="The parent has a TextInput child component.">
|
|
<TouchableNativeFeedback accessible={true} accessibilityRole="button">
|
|
<TextInput
|
|
value="this is the value"
|
|
accessible={false}
|
|
style={styles.default}
|
|
placeholder="this is the placeholder"
|
|
/>
|
|
</TouchableNativeFeedback>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="The parents include three levels of nested Components.">
|
|
<TouchableNativeFeedback accessible={true} accessibilityRole="button">
|
|
<Text accessible={false}>
|
|
Text number 2
|
|
<Text accessible={false}>
|
|
Text number 3<Text accessible={false}>Text number 4</Text>
|
|
</Text>
|
|
</Text>
|
|
</TouchableNativeFeedback>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="The child is not TextInput. The contentDescription is not empty and does not have node text.">
|
|
<TouchableNativeFeedback
|
|
onPress={() => console.warn('onPress child')}
|
|
accessible={true}
|
|
accessibilityRole="button">
|
|
<View>
|
|
<Text
|
|
style={styles.smallRedSquare}
|
|
accessibilityLabel="this is the child Text accessibilityLabel"
|
|
accessible={false}
|
|
/>
|
|
</View>
|
|
</TouchableNativeFeedback>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="One of the child has accessibilityHint (hasText triggers the announcement).">
|
|
<View accessible={true} accessibilityRole="button">
|
|
<Text
|
|
style={styles.smallRedSquare}
|
|
accessible={false}
|
|
accessibilityHint="this child Text does not have text, but has hint and should be announced by TalkBack"
|
|
/>
|
|
</View>
|
|
</RNTesterBlock>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class CheckboxExample extends React.Component<
|
|
{},
|
|
{
|
|
checkboxState: boolean | 'mixed',
|
|
},
|
|
> {
|
|
state: {checkboxState: boolean | 'mixed'} = {
|
|
checkboxState: true,
|
|
};
|
|
|
|
_onCheckboxPress = () => {
|
|
let checkboxState: boolean | $TEMPORARY$string<'mixed'> = false;
|
|
if (this.state.checkboxState === false) {
|
|
checkboxState = 'mixed';
|
|
} else if (this.state.checkboxState === 'mixed') {
|
|
checkboxState = true;
|
|
} else {
|
|
checkboxState = false;
|
|
}
|
|
|
|
this.setState({
|
|
checkboxState: checkboxState,
|
|
});
|
|
};
|
|
|
|
render(): React.Node {
|
|
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: boolean} = {
|
|
switchState: true,
|
|
};
|
|
|
|
_onSwitchToggle = () => {
|
|
const switchState = !this.state.switchState;
|
|
|
|
this.setState({
|
|
switchState: switchState,
|
|
});
|
|
};
|
|
|
|
render(): React.Node {
|
|
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: {isEnabled: boolean, isSelected: boolean} = {
|
|
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: boolean} = {
|
|
expandState: false,
|
|
};
|
|
|
|
_onElementPress = () => {
|
|
const expandState = !this.state.expandState;
|
|
|
|
this.setState({
|
|
expandState: expandState,
|
|
});
|
|
};
|
|
|
|
render(): React.Node {
|
|
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: boolean | 'mixed',
|
|
checkbox2: boolean | 'mixed',
|
|
checkbox3: boolean | 'mixed',
|
|
} = {
|
|
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(): React.Node {
|
|
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 {
|
|
const content = [
|
|
<Text key={1}>This is some text</Text>,
|
|
<Text key={2}>This is some text</Text>,
|
|
<Text key={3}>This is some text</Text>,
|
|
<Text key={4}>This is some text</Text>,
|
|
<Text key={5}>This is some text</Text>,
|
|
<Text key={6}>This is some text</Text>,
|
|
<Text key={7}>This is some text</Text>,
|
|
];
|
|
|
|
return (
|
|
<>
|
|
<RNTesterBlock title="ScrollView with grid role">
|
|
<ScrollView accessibilityRole="grid" style={styles.scrollView}>
|
|
{content}
|
|
</ScrollView>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock title="ScrollView with scrollview role">
|
|
<ScrollView accessibilityRole="scrollview" style={styles.scrollView}>
|
|
{content}
|
|
</ScrollView>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock title="HorizontalScrollView with horizontalscrollview role">
|
|
<ScrollView
|
|
horizontal
|
|
accessibilityRole="horizontalscrollview"
|
|
style={styles.scrollView}>
|
|
{content}
|
|
</ScrollView>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock title="accessibilityRole with View Component">
|
|
<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>
|
|
<View
|
|
accessibilityLabel="element 18"
|
|
accessibilityRole="dropdownlist"
|
|
accessible={true}>
|
|
<Text>Drop Down List example</Text>
|
|
</View>
|
|
<View
|
|
accessibilityLabel="element 19"
|
|
accessibilityRole="pager"
|
|
accessible={true}>
|
|
<Text>Pager example</Text>
|
|
</View>
|
|
<View
|
|
accessibilityLabel="element 20"
|
|
accessibilityRole="togglebutton"
|
|
accessible={true}>
|
|
<Text>Toggle Button example</Text>
|
|
</View>
|
|
<View
|
|
accessibilityLabel="element 21"
|
|
accessibilityRole="viewgroup"
|
|
accessible={true}>
|
|
<Text>Viewgroup example</Text>
|
|
</View>
|
|
<View
|
|
accessibilityLabel="element 22"
|
|
accessibilityRole="webview"
|
|
accessible={true}>
|
|
<Text>Webview example</Text>
|
|
</View>
|
|
<ExpandableElementExample />
|
|
<SelectionExample />
|
|
<RNTesterBlock title="Nested checkbox with delayed state change">
|
|
<NestedCheckBox />
|
|
</RNTesterBlock>
|
|
</View>
|
|
</RNTesterBlock>
|
|
</>
|
|
);
|
|
}
|
|
}
|
|
|
|
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 accessibility action');
|
|
break;
|
|
case 'copy':
|
|
Alert.alert('Alert', 'copy action success');
|
|
break;
|
|
}
|
|
}}
|
|
onPress={() => Alert.alert('Button has been pressed!')}
|
|
title="Button with accessibility 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 accessibility action');
|
|
break;
|
|
case 'copy':
|
|
Alert.alert('Alert', 'copy action success');
|
|
break;
|
|
}
|
|
}}>
|
|
Text
|
|
</Text>
|
|
</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 FakeSliderExampleForAccessibilityValue 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;
|
|
}
|
|
}}
|
|
aria-valuemax={100}
|
|
aria-valuemin={0}
|
|
aria-valuetext={'slider aria value text'}
|
|
aria-valuenow={this.state.current}>
|
|
<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 = (): TimeoutID =>
|
|
setTimeout(
|
|
() => AccessibilityInfo.announceForAccessibility('Announcement Test'),
|
|
1000,
|
|
);
|
|
|
|
_handleOnPressQueued = (): TimeoutID =>
|
|
setTimeout(
|
|
() =>
|
|
AccessibilityInfo.announceForAccessibilityWithOptions(
|
|
'Queued Announcement Test',
|
|
{queue: true},
|
|
),
|
|
1000,
|
|
);
|
|
|
|
_handleOnPressQueueMultiple = () => {
|
|
setTimeout(
|
|
() =>
|
|
AccessibilityInfo.announceForAccessibilityWithOptions(
|
|
'First Queued Announcement Test',
|
|
{queue: true},
|
|
),
|
|
1000,
|
|
);
|
|
setTimeout(
|
|
() =>
|
|
AccessibilityInfo.announceForAccessibilityWithOptions(
|
|
'Second Queued Announcement Test',
|
|
{queue: true},
|
|
),
|
|
1100,
|
|
);
|
|
setTimeout(
|
|
() =>
|
|
AccessibilityInfo.announceForAccessibilityWithOptions(
|
|
'Third Queued Announcement Test',
|
|
{queue: true},
|
|
),
|
|
1200,
|
|
);
|
|
};
|
|
|
|
render(): React.Node {
|
|
return Platform.OS === 'ios' ? (
|
|
<View>
|
|
<Button
|
|
onPress={this._handleOnPress}
|
|
title="Announce for Accessibility Immediately"
|
|
/>
|
|
<Button
|
|
onPress={this._handleOnPressQueued}
|
|
title="Announce for Accessibility Queued"
|
|
/>
|
|
<Button
|
|
onPress={this._handleOnPressQueueMultiple}
|
|
title="Announce for Accessibility Queue Multiple"
|
|
/>
|
|
</View>
|
|
) : (
|
|
<View>
|
|
<Button
|
|
onPress={this._handleOnPress}
|
|
title="Announce for Accessibility"
|
|
/>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
function SetAccessibilityFocusExample(props: {}): React.Node {
|
|
const myRef = React.useRef<?React.ElementRef<typeof Text>>(null);
|
|
|
|
const onPress = () => {
|
|
if (myRef && myRef.current) {
|
|
AccessibilityInfo.sendAccessibilityEvent(myRef.current, 'focus');
|
|
}
|
|
};
|
|
|
|
return (
|
|
<View>
|
|
<Text ref={myRef}>
|
|
SetAccessibilityFocus on native element. This should get focus after
|
|
clicking the button!
|
|
</Text>
|
|
<Button title={'Click'} onPress={onPress} />
|
|
</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}
|
|
|
|
{Platform.OS === 'android' ? (
|
|
<RNTesterBlock
|
|
title="isAccessibilityServiceEnabled()"
|
|
description={
|
|
'Event emitted whenever an accessibility service is enabled. This includes TalkBack as well as assistive technologies such as "Select to Speak".'
|
|
}>
|
|
<EnabledExample
|
|
test="any accessibility service"
|
|
eventListener="accessibilityServiceChanged"
|
|
/>
|
|
</RNTesterBlock>
|
|
) : null}
|
|
|
|
<RNTesterBlock title="isReduceMotionEnabled()">
|
|
<EnabledExample
|
|
test="reduce motion"
|
|
eventListener="reduceMotionChanged"
|
|
/>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="isScreenReaderEnabled()">
|
|
<EnabledExample
|
|
test="screen reader"
|
|
eventListener="screenReaderChanged"
|
|
/>
|
|
</RNTesterBlock>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class ImportantForAccessibilityExamples extends React.Component<{}> {
|
|
render(): React.Node {
|
|
return (
|
|
<View>
|
|
<RNTesterBlock title="ImageBackground with importantForAccessibility=no-hide-descendants">
|
|
<View style={styles.container}>
|
|
<ImageBackground
|
|
importantForAccessibility="no-hide-descendants"
|
|
source={require('../../assets/trees.jpg')}
|
|
resizeMode="cover"
|
|
style={styles.ImageBackground}>
|
|
<Text style={styles.text}>not accessible</Text>
|
|
</ImageBackground>
|
|
</View>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock title="ImageBackground with importantForAccessibility=no">
|
|
<View style={styles.container}>
|
|
<ImageBackground
|
|
importantForAccessibility="no"
|
|
source={require('../../assets/trees.jpg')}
|
|
resizeMode="cover"
|
|
style={styles.ImageBackground}>
|
|
<Text style={styles.text}>accessible</Text>
|
|
</ImageBackground>
|
|
</View>
|
|
</RNTesterBlock>
|
|
<RNTesterBlock title="Button with importantForAccessibility=no">
|
|
<Button
|
|
title="this is text"
|
|
importantForAccessibility="no"
|
|
onPress={() => console.log('pressed')}
|
|
/>
|
|
</RNTesterBlock>
|
|
</View>
|
|
);
|
|
}
|
|
}
|
|
|
|
class EnabledExample extends React.Component<
|
|
{
|
|
eventListener:
|
|
| 'reduceMotionChanged'
|
|
| 'boldTextChanged'
|
|
| 'grayscaleChanged'
|
|
| 'invertColorsChanged'
|
|
| 'reduceTransparencyChanged'
|
|
| 'reduceMotionChanged'
|
|
| 'screenReaderChanged'
|
|
| 'accessibilityServiceChanged',
|
|
test: string,
|
|
},
|
|
{
|
|
isEnabled: boolean,
|
|
},
|
|
> {
|
|
state: {isEnabled: boolean} = {
|
|
isEnabled: false,
|
|
};
|
|
_subscription: EventSubscription;
|
|
componentDidMount(): null | Promise<mixed> {
|
|
this._subscription = AccessibilityInfo.addEventListener(
|
|
this.props.eventListener,
|
|
this._handleToggled,
|
|
);
|
|
|
|
switch (this.props.eventListener) {
|
|
case 'reduceMotionChanged':
|
|
return AccessibilityInfo.isReduceMotionEnabled().then(state => {
|
|
this.setState({isEnabled: state});
|
|
});
|
|
case 'accessibilityServiceChanged':
|
|
return AccessibilityInfo.isAccessibilityServiceEnabled().then(state => {
|
|
this.setState({isEnabled: state});
|
|
});
|
|
default:
|
|
return null;
|
|
}
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
this._subscription?.remove();
|
|
}
|
|
|
|
_handleToggled = (isEnabled: void | PressEvent | boolean) => {
|
|
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={'Prefer Cross-Fade Transitions'}
|
|
optionChecker={AccessibilityInfo.prefersCrossFadeTransitions}
|
|
notification={'prefersCrossFadeTransitionsChanged'}
|
|
/>
|
|
<DisplayOptionStatusExample
|
|
optionName={'Screen Reader'}
|
|
optionChecker={AccessibilityInfo.isScreenReaderEnabled}
|
|
notification={'screenReaderChanged'}
|
|
/>
|
|
{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,
|
|
}: {
|
|
notification: string,
|
|
optionChecker: () => Promise<boolean>,
|
|
optionName: string,
|
|
}) {
|
|
const [statusEnabled, setStatusEnabled] = React.useState(false);
|
|
React.useEffect(() => {
|
|
const listener = AccessibilityInfo.addEventListener(
|
|
// $FlowFixMe[prop-missing]
|
|
notification,
|
|
setStatusEnabled,
|
|
);
|
|
optionChecker().then(isEnabled => {
|
|
setStatusEnabled(isEnabled);
|
|
});
|
|
return function cleanup() {
|
|
listener.remove();
|
|
};
|
|
}, [optionChecker, notification]);
|
|
return (
|
|
<View>
|
|
<Text>
|
|
{optionName}
|
|
{' is '}
|
|
{statusEnabled ? 'enabled' : 'disabled'}.
|
|
</Text>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function AccessibilityErrorWithButtons(): React.Node {
|
|
const [text, setText] = React.useState('');
|
|
const [error, setError] = React.useState<?string>(null);
|
|
const [accessibilityInvalid, setAccessibilityInvalid] = React.useState(false);
|
|
return (
|
|
<View>
|
|
<RNTesterBlock>
|
|
<TextInput
|
|
accessibilityErrorMessage={error}
|
|
accessibilityInvalid={accessibilityInvalid}
|
|
onChangeText={newText => {
|
|
setText(newText);
|
|
if (newText === 'Error') {
|
|
setError('the newText is: ' + newText);
|
|
setAccessibilityInvalid(true);
|
|
} else {
|
|
setError(null);
|
|
setAccessibilityInvalid(false);
|
|
}
|
|
}}
|
|
value={text}
|
|
style={styles.default}
|
|
/>
|
|
<Button
|
|
onPress={() => {
|
|
setError('This error is set with Button onPress callback');
|
|
}}
|
|
title="Press to set an accessibilityErrorMessage to a non-empty value"
|
|
/>
|
|
<Button
|
|
onPress={() => setAccessibilityInvalid(!accessibilityInvalid)}
|
|
title={`Press to set accessibilityInvalid to ${
|
|
accessibilityInvalid ? 'false' : 'true'
|
|
}`}
|
|
/>
|
|
<Button
|
|
onPress={() => setError('')}
|
|
title={'Press to set an empty error message'}
|
|
/>
|
|
</RNTesterBlock>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function AccessibilityErrorDoesNotClear(): React.Node {
|
|
const [text, setText] = React.useState('');
|
|
const [error, setError] = React.useState('');
|
|
const [accessibilityInvalid, setAccessibilityInvalid] = React.useState(false);
|
|
return (
|
|
<View>
|
|
<RNTesterBlock title="TextView without label">
|
|
<Text>Set an error, change textinput value and then announce it.</Text>
|
|
<TextInput
|
|
accessibilityErrorMessage={error}
|
|
accessibilityInvalid={accessibilityInvalid}
|
|
value={text}
|
|
onChangeText={newText => setText(newText)}
|
|
style={styles.default}
|
|
/>
|
|
<Text>
|
|
The value of the accessibilityErrorMessage prop is{' '}
|
|
{error.length > 0 ? error : 'empty error'}
|
|
</Text>
|
|
<Button
|
|
onPress={() => {
|
|
setError('This is an error');
|
|
}}
|
|
title="sets accessibilityErrorMessage to 'This is an error'"
|
|
/>
|
|
<Button
|
|
onPress={() => setAccessibilityInvalid(!accessibilityInvalid)}
|
|
title={`Press to set accessibilityInvalid to ${
|
|
accessibilityInvalid ? 'false' : 'true'
|
|
}`}
|
|
/>
|
|
</RNTesterBlock>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
function AccessibilityExpandedExample(): React.Node {
|
|
const [expand, setExpanded] = React.useState(false);
|
|
const expandAction = {name: 'expand'};
|
|
const collapseAction = {name: 'collapse'};
|
|
return (
|
|
<>
|
|
<RNTesterBlock title="Collapse/Expanded state change (Paper)">
|
|
<Text>
|
|
The following component announces expanded/collapsed state correctly
|
|
</Text>
|
|
<Button
|
|
onPress={() => setExpanded(!expand)}
|
|
accessibilityState={{expanded: expand}}
|
|
accessibilityActions={expand ? [collapseAction] : [expandAction]}
|
|
onAccessibilityAction={event => {
|
|
switch (event.nativeEvent.actionName) {
|
|
case 'expand':
|
|
setExpanded(true);
|
|
break;
|
|
case 'collapse':
|
|
setExpanded(false);
|
|
break;
|
|
}
|
|
}}
|
|
title="click me to change state"
|
|
/>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="Screenreader announces the visible text">
|
|
<Text>Announcing expanded/collapse and the visible text.</Text>
|
|
<TouchableOpacity
|
|
style={styles.button}
|
|
onPress={() => setExpanded(!expand)}
|
|
accessibilityState={{expanded: expand}}>
|
|
<Text>Click me to change state</Text>
|
|
</TouchableOpacity>
|
|
</RNTesterBlock>
|
|
|
|
<RNTesterBlock title="expanded/collapsed only managed through the accessibility menu">
|
|
<TouchableWithoutFeedback
|
|
accessibilityState={{expanded: true}}
|
|
accessible={true}>
|
|
<View>
|
|
<Text>Clicking me does not change state</Text>
|
|
</View>
|
|
</TouchableWithoutFeedback>
|
|
</RNTesterBlock>
|
|
</>
|
|
);
|
|
}
|
|
|
|
exports.title = 'Accessibility';
|
|
exports.documentationURL = 'https://reactnative.dev/docs/accessibilityinfo';
|
|
exports.description = 'Examples of using Accessibility APIs.';
|
|
exports.examples = [
|
|
{
|
|
title: 'Accessibility expanded',
|
|
render(): React.Element<typeof AccessibilityExpandedExample> {
|
|
return <AccessibilityExpandedExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Accessibility elements',
|
|
render(): React.Element<typeof AccessibilityExample> {
|
|
return <AccessibilityExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Automatic Content Grouping',
|
|
render(): React.Element<typeof AutomaticContentGrouping> {
|
|
return <AutomaticContentGrouping />;
|
|
},
|
|
},
|
|
{
|
|
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: 'Fake Slider Example',
|
|
render(): React.Element<typeof FakeSliderExample> {
|
|
return <FakeSliderExample />;
|
|
},
|
|
},
|
|
{
|
|
title: 'Fake SliderExample For AccessibilityValue',
|
|
render(): React.Element<typeof FakeSliderExampleForAccessibilityValue> {
|
|
return <FakeSliderExampleForAccessibilityValue />;
|
|
},
|
|
},
|
|
{
|
|
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: 'Testing importantForAccessibility',
|
|
render(): React.Element<typeof ImportantForAccessibilityExamples> {
|
|
return <ImportantForAccessibilityExamples />;
|
|
},
|
|
},
|
|
{
|
|
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}
|
|
/>
|
|
);
|
|
},
|
|
},
|
|
{
|
|
title: 'accessibilityErrorMessage onChangeText or Button onPress',
|
|
render: function (): React.Node {
|
|
return <AccessibilityErrorWithButtons />;
|
|
},
|
|
},
|
|
{
|
|
title: 'accessibilityErrorMessage does not clear with text change',
|
|
render: function (): React.Node {
|
|
return <AccessibilityErrorDoesNotClear />;
|
|
},
|
|
},
|
|
{
|
|
title: 'TextInput with aria-labelledby attribute"',
|
|
render(): React.Element<typeof View> {
|
|
return (
|
|
<View>
|
|
<Text nativeID="testAriaLabelledBy">Phone Number</Text>
|
|
<TextInput
|
|
aria-labelledby={'testAriaLabelledBy'}
|
|
style={styles.default}
|
|
/>
|
|
</View>
|
|
);
|
|
},
|
|
},
|
|
];
|