Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Return false if gesture recognizers are present but all disabled (#3377)
## Description This PR adjusts `shouldHandleTouch` to check for enabled gesture recognizers instead of returning true if the count of gesture recognizers is greater than zero. The motivation behind this PR is to address a bug where buttons become unresponsive if, I guess, iOS is inserting disabled accessibility gesture recognizers into the child views?? Fixes #3376 ## Test plan I was able to reproduce this reliably on a private repo, and used the debugger to observe `shouldHandleTouch` returning too early from a descendant of the button meant to be tapped. With this fix, I confirmed that the correct button returns to handle to touch event and the buttons all behave as expected I also was able to reproduce the issue with this sample code: 1. Tap `Courses` 2. Tap `Hello World 1` 3. Tap `All Courses` The `All Courses` button has an `onPress` which `alert`s, and in this snippet, I observed no alert occurring <details> <summary>Click to expand</summary> ``` import { StyleSheet, View, Text } from 'react-native'; import { BorderlessButton, FlatList } from 'react-native-gesture-handler'; import Animated from 'react-native-reanimated'; import { useNavigation } from 'expo-router'; import { useState } from 'react'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import {useEffect} from 'react'; import { FadeInLeft, FadeInUp, FadeOutLeft, FadeOutUp, } from 'react-native-reanimated'; const AnimatedBorderlessButton = Animated.createAnimatedComponent(BorderlessButton); const OptionsScreen = ({ options, onSelect }: { options: string[]; onSelect: (option?: string) => void; }) => { const insets = useSafeAreaInsets(); const navigation = useNavigation(); const styles = StyleSheet.create({ container: { paddingTop: insets.top, backgroundColor: 'rgba(0,0,0,0.7)', flex: 1, paddingHorizontal: 24, position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, }, text: { paddingTop: 16, color: 'white', }, }); useEffect(() => { navigation.setOptions({ tabBarStyle: { display: 'none' }, }); return () => { navigation.setOptions({ tabBarStyle: { display: 'flex' }, }); }; }, [navigation]); return ( <Animated.View style={styles.container} entering={FadeInUp} exiting={FadeOutUp} > <FlatList data={options} renderItem={({item}) => ( <AnimatedBorderlessButton onPress={() => onSelect(item)}> <View accessible accessibilityRole="button"> <Text style={styles.text}>{item}</Text> </View> </AnimatedBorderlessButton> )} /> </Animated.View> ); }; function HomeScreen() { const insets = useSafeAreaInsets(); const [selectedCategory, setSelectedCategory] = useState(''); const [options, setOptions] = useState<string[]>([]); const styles = StyleSheet.create({ container: { flex: 1, paddingTop: insets.top, backgroundColor: 'white', }, categoryContainer: { gap: 16, paddingHorizontal: 24, }, }); const dummyData = { categoryOptions: (Array.from({length: 14}, (_, i) => `Hello World ${i + 1}`)), }; return ( <View style={styles.container}> <AnimatedBorderlessButton onPress={() => setOptions(dummyData.categoryOptions)} entering={FadeInLeft} exiting={FadeOutLeft} > <View accessible accessibilityRole="button"> <Text>{selectedCategory.length > 0 ? selectedCategory : 'Courses'}</Text> </View> </AnimatedBorderlessButton> {selectedCategory.length > 0 && ( <AnimatedBorderlessButton onPress={() => alert('It Worked')} entering={FadeInLeft} exiting={FadeOutLeft} > <View accessible accessibilityRole="button"> <Text>All Courses</Text> </View> </AnimatedBorderlessButton> )} {options.length > 0 && ( <OptionsScreen options={options} onSelect={(selectedOption) => { setSelectedCategory(selectedOption ?? ''); setOptions([]); }} /> )} </View> ); } export default function TabTwoScreen() { return ( <HomeScreen /> ); } ``` </details> --------- Co-authored-by: Michał Bert <[email protected]>
- Loading branch information