Reputation: 908
React native App, testing in IOS. Desired behaviour: Inputing an invalid value in TextInput and clicking a submit button. Screen reader shell pronounce for the error and the focus shell return to the textInput. Setting the focus shell not open the keyboard.
Problem in the implementation: The return of the focus to the textInput seems to be before accessibility label is updated to the error message. Screen reader pronounce for the old accessibility Label. If touching the textInput after the pronounce of the Voice Over the updated accessibility Label with the error message is pronounce.
The code also available in expo snack, snack
import * as React from 'react';
import { AccessibilityInfo, TextInput, Button, Text, View, StyleSheet } from 'react-native';
import Constants from 'expo-constants';
import AssetExample from './components/AssetExample';
import { Card } from 'react-native-paper';
const errorBtn = "Click to simulate invalid text input";
const clearBtn = "Click to clear the error";
const errorLabel = "Wrong Number has being typed, try again";
const clearLabel = "Phone Number";
const description =
"Enable Voice Over, If Error hapens on textInput, screen reader shell pronounce the error message and set focus to the textInput where the error happend"
const placeholder = "Type 10 digit phone number"
export default class App extends React.Component {
state = {
error: false,
text: "",
buttonTitle: errorBtn
}
errorMsg= () => {this.setState({error: true})}
clearMsg= () => {this.setState({error: false})}
onPress = () => {
const TextInputNativeTag =
this
.textOInputRef
._inputRef
._nativeTag
if (this.state.buttonTitle == errorBtn)
this.errorMsg();
if (this.state.buttonTitle == clearBtn)
this.clearMsg();
if (this.textOInputRef)
AccessibilityInfo
.setAccessibilityFocus(TextInputNativeTag);
this.state.buttonTitle == errorBtn &&
this.setState({buttonTitle:clearBtn});
this.state.buttonTitle == clearBtn &&
this.setState({buttonTitle:errorBtn}) ;
}
render() {
return (
<View>
<View >
<Text accessibilityRole='header'
style={styles.header}
>
Accessabiity Focuse Tester
</Text>
<Text style={styles.paragraph}>
{description}
</Text>
</View>
<View style={styles.body}>
<View style={styles.sectionContainer} >
<Text
accessibilityRole='header'
style={styles.sectionTitle}
>
Accessability Label
</Text>
<Text style={
styles.sectionDescription}
>
{this.state.error
? errorLabel
: clearLabel
}
</Text>
</View>
<View style={
styles.sectionContainer}
>
<TextInput
ref={r =>
this.textOInputRef = r
}
style={
{height: 40,
borderColor: 'gray',
borderWidth: 1}
}
accessibilityLabel={
this.state.error
? errorLabel
: clearLabel
}
underlineColorAndroid=
"transparent"
keyboardType="numeric"
returnKeyType="done"
onChangeText={
text =>
this.setState({text})
}
placeholder={placeholder}
placeholderTextColor=
"rgb(143,143,143)"
value={this.state.text}
/>
</View>
<View style={styles.sectionContainer}>
<Button
onPress={this.onPress}
title={this.state.buttonTitle}
color="#841584"
accessibilityLabel={this
.state
.buttonTitle}
/>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
header: {
margin: 24,
fontSize: 18,
textAlign: 'center',
fontWeight: 'bold',
},
paragraph: {
margin: 24,
fontSize: 18,
textAlign: 'center',
},
body: {
backgroundColor: '#fff',
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: '#000',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
});
Upvotes: 1
Views: 2044
Reputation: 908
When pressing a button, screen reader reads the button label during the operation of the onPress method. Therefore there was a need to schedule the render a time ahead. in addition, there is a need to schedule the setfocus to the textInput after the label is updated to the new value. following is the code and snack
import * as React from 'react';
import { AccessibilityInfo, TextInput, TouchableWithoutFeedback, Text, View, StyleSheet } from 'react-native';
const errorBtn = "Click to simulate invalid text input";
const clearBtn = "Click to clear the error";
const errorLabel = "Wrong Number has being typed, try again";
const clearLabel = "Phone Number";
const description =
"Enable Voice Over, If Error hapens on textInput, screen reader shell pronounce the error message and set focus to the textInput where the error happend"
const placeholder = "Type 10 digit phone number"
export default class App extends React.Component {
state = {
error: false,
text: "",
buttonTitle: errorBtn
}
label=clearLabel;
accessabilityMsg=null;
textInputRef=null;
onPress = () => {
const textInput = this.textInputRef && this.textInputRef._inputRef._nativeTag;
if (this.state.buttonTitle == errorBtn) {
console.log('onPress set label & state to error');
setTimeout(()=>{
this.setState({error: true,buttonTitle:clearBtn});
setTimeout(()=>{textInput && AccessibilityInfo.setAccessibilityFocus(textInput)},500);
},6000);
this.label=errorLabel;
} else {
console.log('onPress set label & state to normal')
this.setState({error: false,buttonTitle:errorBtn});
this.label=clearLabel;
}
}
render() {
return (
<View >
<View >
<Text accessibilityRole='header'
style={styles.header}
>
Accessabiity Focus Tester
</Text>
<Text style={styles.paragraph}>
{description}
</Text>
</View>
<View style={styles.body}>
<View style={styles.sectionContainer} >
<Text
accessibilityRole='header'
style={styles.sectionTitle}
>
Accessability Label
</Text>
<Text style={
styles.sectionDescription}
>
{this.label}
</Text>
</View>
<View style={
styles.sectionContainer}
>
<TextInput
ref={r =>
this.textInputRef = r
}
style={
{height: 40,
borderColor: 'gray',
borderWidth: 1}
}
accessibilityLabel={this.label}
underlineColorAndroid=
"transparent"
keyboardType="numeric"
returnKeyType="done"
onChangeText={
text =>
this.setState({text})
}
placeholder={placeholder}
placeholderTextColor=
"rgb(143,143,143)"
value={this.state.text}
/>
</View>
<View style={styles.button}>
<TouchableWithoutFeedback
accessible
accessibilityRole='button'
accessibilityLabel={this.state.buttonTitle}
onPress={this.onPress}
>
<Text> {this.state.buttonTitle} </Text>
</TouchableWithoutFeedback>
</View>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
header: {
margin: 24,
fontSize: 18,
textAlign: 'center',
fontWeight: 'bold',
},
paragraph: {
margin: 24,
fontSize: 18,
textAlign: 'center',
},
body: {
backgroundColor: '#fff',
},
sectionContainer: {
marginTop: 32,
paddingHorizontal: 24,
},
sectionTitle: {
fontSize: 24,
fontWeight: '600',
color: '#000',
},
sectionDescription: {
marginTop: 8,
fontSize: 18,
fontWeight: '400',
},
button: {
margin: 24,
alignItems: 'center',
backgroundColor: '#DDDDDD',
padding: 10,
borderColor: '#000',
borderWidth: 1
},
});
Finally, it is still not a completed mission. We can't design an application based on a setTimout. Still a better approach need to be found. Thanks for any comment and post.
Upvotes: 1