Skip to content

Commit 88f2356

Browse files
kyamashirofacebook-github-bot
authored andcommitted
Added talkback support for TouchableNativeFeedback accessibility: disabled prop (#31224)
Summary: Issue #30952 Add talkback support for TouchableNativeFeedback component. ## Changelog <!-- Help reviewers and the release process by writing your own changelog entry. For an example, see: https://github.com/facebook/react-native/wiki/Changelog --> [Android] [Changed] - TouchableNativeFeedback: sync disabled prop with accessibilityState Pull Request resolved: #31224 Test Plan: I have checked that talkback and disabled works properly on the actual device(Pixel4a Android11). ```jsx /** * Sample React Native App * https://github.com/facebook/react-native * * format * flow strict-local */ import * as React from 'react'; import { Text, View, StyleSheet, TouchableNativeFeedback, Alert, } from 'react-native'; export default function App() { const onPress = () => Alert.alert('test'); return ( <View style={styles.container}> {/*not disabled, voice:double tap to activate*/} <TouchableNativeFeedback onPress={onPress}> <View style={styles.touchable}> <Text style={styles.text}>talkback OK</Text> </View> </TouchableNativeFeedback> {/*disabled, voice:disabled*/} <TouchableNativeFeedback disabled={true} onPress={onPress}> <View style={styles.touchable}> <Text style={styles.text}> should be disabled when disabled is true </Text> </View> </TouchableNativeFeedback> {/*disabled, voice:disabled*/} <TouchableNativeFeedback accessibilityState={{disabled: true}} onPress={onPress}> <View style={styles.touchable}> <Text style={styles.text}> should be disabled when accessibilityState disabled is true </Text> </View> </TouchableNativeFeedback> {/*disabled, voice:disabled*/} <TouchableNativeFeedback disabled={true} accessibilityState={{}} onPress={onPress}> <View style={styles.touchable}> <Text style={styles.text}> should be disabled when disabled is true and accessibilityState is empty </Text> </View> </TouchableNativeFeedback> {/*disabled, voice:disabled*/} <TouchableNativeFeedback disabled={true} accessibilityState={{checked: true}} onPress={onPress}> <View style={styles.touchable}> <Text style={styles.text}> should keep accessibilityState when disabled is true </Text> </View> </TouchableNativeFeedback> {/*disabled, voice:disabled*/} <TouchableNativeFeedback disabled={true} accessibilityState={{disabled: false}} onPress={onPress}> <View style={styles.touchable}> <Text style={styles.text}> should overwrite accessibilityState with value of disabled prop </Text> </View> </TouchableNativeFeedback> {/*not disabled, voice:double tap to activate*/} <TouchableNativeFeedback disabled={false} accessibilityState={{disabled: true}} onPress={onPress}> <View style={styles.touchable}> <Text style={styles.text}> should overwrite accessibilityState with value of disabled prop </Text> </View> </TouchableNativeFeedback> </View> ); } const styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', padding: 16, }, touchable: {flex: 0.5, borderColor: 'black', borderWidth: 1, marginBottom: 8}, text: {alignSelf: 'center'}, }); ``` Reviewed By: yungsters Differential Revision: D27479271 Pulled By: kacieb fbshipit-source-id: 43187839b58dfe8f91afdba91453fb6b98e1a604
1 parent cb028ee commit 88f2356

File tree

3 files changed

+206
-5
lines changed

3 files changed

+206
-5
lines changed

Libraries/Components/Touchable/TouchableNativeFeedback.js

+13-2
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,10 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
168168
_createPressabilityConfig(): PressabilityConfig {
169169
return {
170170
cancelable: !this.props.rejectResponderTermination,
171-
disabled: this.props.disabled,
171+
disabled:
172+
this.props.disabled != null
173+
? this.props.disabled
174+
: this.props.accessibilityState?.disabled,
172175
hitSlop: this.props.hitSlop,
173176
delayLongPress: this.props.delayLongPress,
174177
delayPressIn: this.props.delayPressIn,
@@ -255,6 +258,14 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
255258
...eventHandlersWithoutBlurAndFocus
256259
} = this.state.pressability.getEventHandlers();
257260

261+
const accessibilityState =
262+
this.props.disabled != null
263+
? {
264+
...this.props.accessibilityState,
265+
disabled: this.props.disabled,
266+
}
267+
: this.props.accessibilityState;
268+
258269
return React.cloneElement(
259270
element,
260271
{
@@ -269,7 +280,7 @@ class TouchableNativeFeedback extends React.Component<Props, State> {
269280
accessibilityHint: this.props.accessibilityHint,
270281
accessibilityLabel: this.props.accessibilityLabel,
271282
accessibilityRole: this.props.accessibilityRole,
272-
accessibilityState: this.props.accessibilityState,
283+
accessibilityState: accessibilityState,
273284
accessibilityActions: this.props.accessibilityActions,
274285
onAccessibilityAction: this.props.onAccessibilityAction,
275286
accessibilityValue: this.props.accessibilityValue,

Libraries/Components/Touchable/__tests__/TouchableNativeFeedback-test.js

+83-3
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010

1111
'use strict';
1212

13-
const React = require('react');
14-
const Text = require('../../../Text/Text');
15-
const TouchableNativeFeedback = require('../TouchableNativeFeedback');
13+
import * as React from 'react';
14+
import ReactTestRenderer from 'react-test-renderer';
15+
import Text from '../../../Text/Text';
16+
import TouchableNativeFeedback from '../TouchableNativeFeedback';
17+
import View from '../../View/View';
1618

1719
const render = require('../../../../jest/renderer');
1820

@@ -33,3 +35,81 @@ describe('TouchableWithoutFeedback', () => {
3335
);
3436
});
3537
});
38+
39+
describe('<TouchableNativeFeedback />', () => {
40+
it('should render as expected', () => {
41+
const instance = ReactTestRenderer.create(
42+
<TouchableNativeFeedback>
43+
<View />
44+
</TouchableNativeFeedback>,
45+
);
46+
47+
expect(instance.toJSON()).toMatchSnapshot();
48+
});
49+
});
50+
51+
describe('<TouchableNativeFeedback disabled={true}>', () => {
52+
it('should be disabled when disabled is true', () => {
53+
expect(
54+
ReactTestRenderer.create(
55+
<TouchableNativeFeedback disabled={true}>
56+
<View />
57+
</TouchableNativeFeedback>,
58+
),
59+
).toMatchSnapshot();
60+
});
61+
});
62+
63+
describe('<TouchableNativeFeedback disabled={true} accessibilityState={{}}>', () => {
64+
it('should be disabled when disabled is true and accessibilityState is empty', () => {
65+
expect(
66+
ReactTestRenderer.create(
67+
<TouchableNativeFeedback disabled={true} accessibilityState={{}}>
68+
<View />
69+
</TouchableNativeFeedback>,
70+
),
71+
).toMatchSnapshot();
72+
});
73+
});
74+
75+
describe('<TouchableNativeFeedback disabled={true} accessibilityState={{checked: true}}>', () => {
76+
it('should keep accessibilityState when disabled is true', () => {
77+
expect(
78+
ReactTestRenderer.create(
79+
<TouchableNativeFeedback
80+
disabled={true}
81+
accessibilityState={{checked: true}}>
82+
<View />
83+
</TouchableNativeFeedback>,
84+
),
85+
).toMatchSnapshot();
86+
});
87+
});
88+
89+
describe('<TouchableNativeFeedback disabled={true} accessibilityState={{disabled:false}}>', () => {
90+
it('should overwrite accessibilityState with value of disabled prop', () => {
91+
expect(
92+
ReactTestRenderer.create(
93+
<TouchableNativeFeedback
94+
disabled={true}
95+
accessibilityState={{disabled: false}}>
96+
<View />
97+
</TouchableNativeFeedback>,
98+
),
99+
).toMatchSnapshot();
100+
});
101+
});
102+
103+
describe('<TouchableNativeFeedback disabled={false} accessibilityState={{disabled:true}}>', () => {
104+
it('should overwrite accessibilityState with value of disabled prop', () => {
105+
expect(
106+
ReactTestRenderer.create(
107+
<TouchableNativeFeedback
108+
disabled={false}
109+
accessibilityState={{disabled: true}}>
110+
<View />
111+
</TouchableNativeFeedback>,
112+
),
113+
).toMatchSnapshot();
114+
});
115+
});

Libraries/Components/Touchable/__tests__/__snapshots__/TouchableNativeFeedback-test.js.snap

+110
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,113 @@ exports[`TouchableWithoutFeedback renders correctly 1`] = `
1515
Touchable
1616
</Text>
1717
`;
18+
19+
exports[`<TouchableNativeFeedback /> should render as expected 1`] = `
20+
<View
21+
accessible={true}
22+
focusable={false}
23+
onClick={[Function]}
24+
onResponderGrant={[Function]}
25+
onResponderMove={[Function]}
26+
onResponderRelease={[Function]}
27+
onResponderTerminate={[Function]}
28+
onResponderTerminationRequest={[Function]}
29+
onStartShouldSetResponder={[Function]}
30+
/>
31+
`;
32+
33+
exports[`<TouchableNativeFeedback disabled={true}> should be disabled when disabled is true 1`] = `
34+
<View
35+
accessibilityState={
36+
Object {
37+
"disabled": true,
38+
}
39+
}
40+
accessible={true}
41+
focusable={false}
42+
onClick={[Function]}
43+
onResponderGrant={[Function]}
44+
onResponderMove={[Function]}
45+
onResponderRelease={[Function]}
46+
onResponderTerminate={[Function]}
47+
onResponderTerminationRequest={[Function]}
48+
onStartShouldSetResponder={[Function]}
49+
/>
50+
`;
51+
52+
exports[`<TouchableNativeFeedback disabled={true} accessibilityState={{}}> should be disabled when disabled is true and accessibilityState is empty 1`] = `
53+
<View
54+
accessibilityState={
55+
Object {
56+
"disabled": true,
57+
}
58+
}
59+
accessible={true}
60+
focusable={false}
61+
onClick={[Function]}
62+
onResponderGrant={[Function]}
63+
onResponderMove={[Function]}
64+
onResponderRelease={[Function]}
65+
onResponderTerminate={[Function]}
66+
onResponderTerminationRequest={[Function]}
67+
onStartShouldSetResponder={[Function]}
68+
/>
69+
`;
70+
71+
exports[`<TouchableNativeFeedback disabled={true} accessibilityState={{checked: true}}> should keep accessibilityState when disabled is true 1`] = `
72+
<View
73+
accessibilityState={
74+
Object {
75+
"checked": true,
76+
"disabled": true,
77+
}
78+
}
79+
accessible={true}
80+
focusable={false}
81+
onClick={[Function]}
82+
onResponderGrant={[Function]}
83+
onResponderMove={[Function]}
84+
onResponderRelease={[Function]}
85+
onResponderTerminate={[Function]}
86+
onResponderTerminationRequest={[Function]}
87+
onStartShouldSetResponder={[Function]}
88+
/>
89+
`;
90+
91+
exports[`<TouchableNativeFeedback disabled={true} accessibilityState={{disabled:false}}> should overwrite accessibilityState with value of disabled prop 1`] = `
92+
<View
93+
accessibilityState={
94+
Object {
95+
"disabled": true,
96+
}
97+
}
98+
accessible={true}
99+
focusable={false}
100+
onClick={[Function]}
101+
onResponderGrant={[Function]}
102+
onResponderMove={[Function]}
103+
onResponderRelease={[Function]}
104+
onResponderTerminate={[Function]}
105+
onResponderTerminationRequest={[Function]}
106+
onStartShouldSetResponder={[Function]}
107+
/>
108+
`;
109+
110+
exports[`<TouchableNativeFeedback disabled={false} accessibilityState={{disabled:true}}> should overwrite accessibilityState with value of disabled prop 1`] = `
111+
<View
112+
accessibilityState={
113+
Object {
114+
"disabled": false,
115+
}
116+
}
117+
accessible={true}
118+
focusable={false}
119+
onClick={[Function]}
120+
onResponderGrant={[Function]}
121+
onResponderMove={[Function]}
122+
onResponderRelease={[Function]}
123+
onResponderTerminate={[Function]}
124+
onResponderTerminationRequest={[Function]}
125+
onStartShouldSetResponder={[Function]}
126+
/>
127+
`;

0 commit comments

Comments
 (0)