diff --git a/packages/app/fixtures/Pressable.fixture.tsx b/packages/app/fixtures/Pressable.fixture.tsx new file mode 100644 index 0000000000..6dd4a1a123 --- /dev/null +++ b/packages/app/fixtures/Pressable.fixture.tsx @@ -0,0 +1,93 @@ +import { Pressable, Text, View } from '@tloncorp/ui'; +import { useValue } from 'react-cosmos/client'; +import { Alert, SafeAreaView, Switch } from 'react-native'; + +export default { + Nested() { + const [innerPressableEnabled, setInnerPressableEnabled] = useValue( + 'Enable inner press handler', + { defaultValue: true } + ); + const [siblingPressableEnabled, setSiblingPressableEnabled] = useValue( + 'Enable sibling press handler', + { defaultValue: true } + ); + return ( + + + { + Alert.alert('Outer: onPress fired'); + }} + onTouchEndCapture={(event) => { + event.stopPropagation(); + }} + > + Outer view (has onPress handler) + { + event.persist(); + console.log(event); + Alert.alert('Inner: onPress fired'); + } + : undefined + } + > + + Inner view{' '} + {innerPressableEnabled + ? '(has onPress handler)' + : '(does not have onPress handler)'}{' '} + + + + + {/* Sibling floating above the other pressables */} + { + Alert.alert('Sibling: onPress fired'); + } + : undefined + } + > + + Sibling (press handler{' '} + {siblingPressableEnabled ? 'enabled' : 'disabled'}) + + + + + {/* Settings panel */} + + + + Enable inner press handler + + + + Enable sibling press handler + + + + ); + }, +}; diff --git a/packages/ui/src/components/Pressable.tsx b/packages/ui/src/components/Pressable.tsx index a56c5482a7..c2386575fd 100644 --- a/packages/ui/src/components/Pressable.tsx +++ b/packages/ui/src/components/Pressable.tsx @@ -62,6 +62,9 @@ export default function Pressable({ action, }); + const hasInteractionHandler = + (action == null ? onPress : onPressLink) || onLongPress; + if (action && !to) { throw new Error( 'The `to` prop is required when `action` is specified in `Pressable`' @@ -76,6 +79,11 @@ export default function Pressable({ group onPress={onPressLink ?? onPress} onLongPress={longPressHandler} + // Pressable always blocks touches from bubbling to ancestors, even if + // no handlers are attached. + // To allow bubbling, disable the Pressable (mixin) when no handlers + // are attached. + disabled={!hasInteractionHandler} > {children} @@ -87,6 +95,7 @@ export default function Pressable({ {...stackProps} onPress={onPress} onLongPress={longPressHandler} + disabled={!hasInteractionHandler} > {children}