Skip to content

Commit 1c7d9c8

Browse files
kaciebfacebook-github-bot
authored andcommitted
Fix disabled prop not disabling onPress for voice assistant
Summary: It is currently possible to activate a disabled Pressable with VoiceOver/TalkBack. This fixes this. Changelog: [General][Fixed] Fix disabled prop not disabling onPress for voice assistant Reviewed By: blavalla Differential Revision: D26225448 fbshipit-source-id: 67fa10f9e5c50143d986dc709a2fb639fdc3e718
1 parent 35b6d2e commit 1c7d9c8

File tree

4 files changed

+352
-2
lines changed

4 files changed

+352
-2
lines changed

Libraries/Components/Pressable/Pressable.js

+6
Original file line numberDiff line numberDiff line change
@@ -168,10 +168,16 @@ function Pressable(props: Props, forwardedRef): React.Node {
168168

169169
const hitSlop = normalizeRect(props.hitSlop);
170170

171+
const accessibilityState =
172+
disabled != null
173+
? {...props.accessibilityState, disabled}
174+
: props.accessibilityState;
175+
171176
const restPropsWithDefaults: React.ElementConfig<typeof View> = {
172177
...restProps,
173178
...android_rippleConfig?.viewProps,
174179
accessible: accessible !== false,
180+
accessibilityState,
175181
focusable: focusable !== false,
176182
hitSlop,
177183
};

Libraries/Components/Pressable/__tests__/Pressable-test.js

+64
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,67 @@ describe('<Pressable />', () => {
3030
);
3131
});
3232
});
33+
34+
describe('<Pressable disabled={true} />', () => {
35+
it('should be disabled when disabled is true', () => {
36+
expectRendersMatchingSnapshot(
37+
'Pressable',
38+
() => (
39+
<Pressable disabled={true}>
40+
<View />
41+
</Pressable>
42+
),
43+
() => {
44+
jest.dontMock('../Pressable');
45+
},
46+
);
47+
});
48+
});
49+
50+
describe('<Pressable disabled={true} accessibilityState={{}} />', () => {
51+
it('should be disabled when disabled is true and accessibilityState is empty', () => {
52+
expectRendersMatchingSnapshot(
53+
'Pressable',
54+
() => (
55+
<Pressable disabled={true} accessibilityState={{}}>
56+
<View />
57+
</Pressable>
58+
),
59+
() => {
60+
jest.dontMock('../Pressable');
61+
},
62+
);
63+
});
64+
});
65+
66+
describe('<Pressable disabled={true} accessibilityState={{checked: true}} />', () => {
67+
it('should keep accessibilityState when disabled is true', () => {
68+
expectRendersMatchingSnapshot(
69+
'Pressable',
70+
() => (
71+
<Pressable disabled={true} accessibilityState={{checked: true}}>
72+
<View />
73+
</Pressable>
74+
),
75+
() => {
76+
jest.dontMock('../Pressable');
77+
},
78+
);
79+
});
80+
});
81+
82+
describe('<Pressable disabled={true} accessibilityState={{disabled: false}} />', () => {
83+
it('should overwrite accessibilityState with value of disabled prop', () => {
84+
expectRendersMatchingSnapshot(
85+
'Pressable',
86+
() => (
87+
<Pressable disabled={true} accessibilityState={{disabled: false}}>
88+
<View />
89+
</Pressable>
90+
),
91+
() => {
92+
jest.dontMock('../Pressable');
93+
},
94+
);
95+
});
96+
});

Libraries/Components/Pressable/__tests__/__snapshots__/Pressable-test.js.snap

+280
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,283 @@ exports[`<Pressable /> should render as expected: should shallow render as <Pres
4949
<View />
5050
</Memo(Pressable)>
5151
`;
52+
53+
exports[`<Pressable disabled={true} /> should be disabled when disabled is true: should deep render when mocked (please verify output manually) 1`] = `
54+
<View
55+
accessibilityState={
56+
Object {
57+
"disabled": true,
58+
}
59+
}
60+
accessible={true}
61+
collapsable={false}
62+
focusable={true}
63+
onBlur={[Function]}
64+
onClick={[Function]}
65+
onFocus={[Function]}
66+
onResponderGrant={[Function]}
67+
onResponderMove={[Function]}
68+
onResponderRelease={[Function]}
69+
onResponderTerminate={[Function]}
70+
onResponderTerminationRequest={[Function]}
71+
onStartShouldSetResponder={[Function]}
72+
>
73+
<View />
74+
</View>
75+
`;
76+
77+
exports[`<Pressable disabled={true} /> should be disabled when disabled is true: should deep render when not mocked (please verify output manually) 1`] = `
78+
<View
79+
accessibilityState={
80+
Object {
81+
"disabled": true,
82+
}
83+
}
84+
accessible={true}
85+
collapsable={false}
86+
focusable={true}
87+
onBlur={[Function]}
88+
onClick={[Function]}
89+
onFocus={[Function]}
90+
onResponderGrant={[Function]}
91+
onResponderMove={[Function]}
92+
onResponderRelease={[Function]}
93+
onResponderTerminate={[Function]}
94+
onResponderTerminationRequest={[Function]}
95+
onStartShouldSetResponder={[Function]}
96+
>
97+
<View />
98+
</View>
99+
`;
100+
101+
exports[`<Pressable disabled={true} /> should be disabled when disabled is true: should shallow render as <Pressable /> when mocked 1`] = `
102+
<Memo(Pressable)
103+
disabled={true}
104+
>
105+
<View />
106+
</Memo(Pressable)>
107+
`;
108+
109+
exports[`<Pressable disabled={true} /> should be disabled when disabled is true: should shallow render as <Pressable /> when not mocked 1`] = `
110+
<Memo(Pressable)
111+
disabled={true}
112+
>
113+
<View />
114+
</Memo(Pressable)>
115+
`;
116+
117+
exports[`<Pressable disabled={true} accessibilityState={{}} /> should be disabled when disabled is true and accessibilityState is empty: should deep render when mocked (please verify output manually) 1`] = `
118+
<View
119+
accessibilityState={
120+
Object {
121+
"disabled": true,
122+
}
123+
}
124+
accessible={true}
125+
collapsable={false}
126+
focusable={true}
127+
onBlur={[Function]}
128+
onClick={[Function]}
129+
onFocus={[Function]}
130+
onResponderGrant={[Function]}
131+
onResponderMove={[Function]}
132+
onResponderRelease={[Function]}
133+
onResponderTerminate={[Function]}
134+
onResponderTerminationRequest={[Function]}
135+
onStartShouldSetResponder={[Function]}
136+
>
137+
<View />
138+
</View>
139+
`;
140+
141+
exports[`<Pressable disabled={true} accessibilityState={{}} /> should be disabled when disabled is true and accessibilityState is empty: should deep render when not mocked (please verify output manually) 1`] = `
142+
<View
143+
accessibilityState={
144+
Object {
145+
"disabled": true,
146+
}
147+
}
148+
accessible={true}
149+
collapsable={false}
150+
focusable={true}
151+
onBlur={[Function]}
152+
onClick={[Function]}
153+
onFocus={[Function]}
154+
onResponderGrant={[Function]}
155+
onResponderMove={[Function]}
156+
onResponderRelease={[Function]}
157+
onResponderTerminate={[Function]}
158+
onResponderTerminationRequest={[Function]}
159+
onStartShouldSetResponder={[Function]}
160+
>
161+
<View />
162+
</View>
163+
`;
164+
165+
exports[`<Pressable disabled={true} accessibilityState={{}} /> should be disabled when disabled is true and accessibilityState is empty: should shallow render as <Pressable /> when mocked 1`] = `
166+
<Memo(Pressable)
167+
accessibilityState={Object {}}
168+
disabled={true}
169+
>
170+
<View />
171+
</Memo(Pressable)>
172+
`;
173+
174+
exports[`<Pressable disabled={true} accessibilityState={{}} /> should be disabled when disabled is true and accessibilityState is empty: should shallow render as <Pressable /> when not mocked 1`] = `
175+
<Memo(Pressable)
176+
accessibilityState={Object {}}
177+
disabled={true}
178+
>
179+
<View />
180+
</Memo(Pressable)>
181+
`;
182+
183+
exports[`<Pressable disabled={true} accessibilityState={{checked: true}} /> should keep accessibilityState when disabled is true: should deep render when mocked (please verify output manually) 1`] = `
184+
<View
185+
accessibilityState={
186+
Object {
187+
"checked": true,
188+
"disabled": true,
189+
}
190+
}
191+
accessible={true}
192+
collapsable={false}
193+
focusable={true}
194+
onBlur={[Function]}
195+
onClick={[Function]}
196+
onFocus={[Function]}
197+
onResponderGrant={[Function]}
198+
onResponderMove={[Function]}
199+
onResponderRelease={[Function]}
200+
onResponderTerminate={[Function]}
201+
onResponderTerminationRequest={[Function]}
202+
onStartShouldSetResponder={[Function]}
203+
>
204+
<View />
205+
</View>
206+
`;
207+
208+
exports[`<Pressable disabled={true} accessibilityState={{checked: true}} /> should keep accessibilityState when disabled is true: should deep render when not mocked (please verify output manually) 1`] = `
209+
<View
210+
accessibilityState={
211+
Object {
212+
"checked": true,
213+
"disabled": true,
214+
}
215+
}
216+
accessible={true}
217+
collapsable={false}
218+
focusable={true}
219+
onBlur={[Function]}
220+
onClick={[Function]}
221+
onFocus={[Function]}
222+
onResponderGrant={[Function]}
223+
onResponderMove={[Function]}
224+
onResponderRelease={[Function]}
225+
onResponderTerminate={[Function]}
226+
onResponderTerminationRequest={[Function]}
227+
onStartShouldSetResponder={[Function]}
228+
>
229+
<View />
230+
</View>
231+
`;
232+
233+
exports[`<Pressable disabled={true} accessibilityState={{checked: true}} /> should keep accessibilityState when disabled is true: should shallow render as <Pressable /> when mocked 1`] = `
234+
<Memo(Pressable)
235+
accessibilityState={
236+
Object {
237+
"checked": true,
238+
}
239+
}
240+
disabled={true}
241+
>
242+
<View />
243+
</Memo(Pressable)>
244+
`;
245+
246+
exports[`<Pressable disabled={true} accessibilityState={{checked: true}} /> should keep accessibilityState when disabled is true: should shallow render as <Pressable /> when not mocked 1`] = `
247+
<Memo(Pressable)
248+
accessibilityState={
249+
Object {
250+
"checked": true,
251+
}
252+
}
253+
disabled={true}
254+
>
255+
<View />
256+
</Memo(Pressable)>
257+
`;
258+
259+
exports[`<Pressable disabled={true} accessibilityState={{disabled: false}} /> should overwrite accessibilityState with value of disabled prop: should deep render when mocked (please verify output manually) 1`] = `
260+
<View
261+
accessibilityState={
262+
Object {
263+
"disabled": true,
264+
}
265+
}
266+
accessible={true}
267+
collapsable={false}
268+
focusable={true}
269+
onBlur={[Function]}
270+
onClick={[Function]}
271+
onFocus={[Function]}
272+
onResponderGrant={[Function]}
273+
onResponderMove={[Function]}
274+
onResponderRelease={[Function]}
275+
onResponderTerminate={[Function]}
276+
onResponderTerminationRequest={[Function]}
277+
onStartShouldSetResponder={[Function]}
278+
>
279+
<View />
280+
</View>
281+
`;
282+
283+
exports[`<Pressable disabled={true} accessibilityState={{disabled: false}} /> should overwrite accessibilityState with value of disabled prop: should deep render when not mocked (please verify output manually) 1`] = `
284+
<View
285+
accessibilityState={
286+
Object {
287+
"disabled": true,
288+
}
289+
}
290+
accessible={true}
291+
collapsable={false}
292+
focusable={true}
293+
onBlur={[Function]}
294+
onClick={[Function]}
295+
onFocus={[Function]}
296+
onResponderGrant={[Function]}
297+
onResponderMove={[Function]}
298+
onResponderRelease={[Function]}
299+
onResponderTerminate={[Function]}
300+
onResponderTerminationRequest={[Function]}
301+
onStartShouldSetResponder={[Function]}
302+
>
303+
<View />
304+
</View>
305+
`;
306+
307+
exports[`<Pressable disabled={true} accessibilityState={{disabled: false}} /> should overwrite accessibilityState with value of disabled prop: should shallow render as <Pressable /> when mocked 1`] = `
308+
<Memo(Pressable)
309+
accessibilityState={
310+
Object {
311+
"disabled": false,
312+
}
313+
}
314+
disabled={true}
315+
>
316+
<View />
317+
</Memo(Pressable)>
318+
`;
319+
320+
exports[`<Pressable disabled={true} accessibilityState={{disabled: false}} /> should overwrite accessibilityState with value of disabled prop: should shallow render as <Pressable /> when not mocked 1`] = `
321+
<Memo(Pressable)
322+
accessibilityState={
323+
Object {
324+
"disabled": false,
325+
}
326+
}
327+
disabled={true}
328+
>
329+
<View />
330+
</Memo(Pressable)>
331+
`;

Libraries/Pressability/Pressability.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,8 @@ export default class Pressability {
543543
},
544544

545545
onClick: (event: PressEvent): void => {
546-
const {onPress} = this._config;
547-
if (onPress != null) {
546+
const {onPress, disabled} = this._config;
547+
if (onPress != null && disabled !== true) {
548548
onPress(event);
549549
}
550550
},

0 commit comments

Comments
 (0)