Skip to content

Commit 6606189

Browse files
authored
fix: StoryView was interfering with scroll gestures (#440)
1 parent f71d95c commit 6606189

File tree

2 files changed

+120
-8
lines changed

2 files changed

+120
-8
lines changed

app/react-native/src/preview/components/StoryView/StoryView.tsx

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,36 @@
11
import React from 'react';
22

3-
import { Text, TouchableWithoutFeedback, Keyboard } from 'react-native';
3+
import { Text, Keyboard } from 'react-native';
44
import { useStoryContext } from '../../../hooks';
55
import { Box } from '../Shared/layout';
66

7+
/**
8+
* This is a handler for `onStartShouldSetResponder`, which dismisses the
9+
* keyboard as a side effect but then responds with `false` to the responder
10+
* system, so as not to start actually handling the touch.
11+
*
12+
* The objective here is to dismiss the keyboard when the story view is tapped,
13+
* but in a way that won't interfere with presses or swipes. Using a
14+
* `Touchable...` component as a wrapper will start to handle the touch, which
15+
* will swallow swipe gestures that should have gone on to a child view, such
16+
* as `ScrollView`.
17+
*/
18+
function dismissOnStartResponder() {
19+
Keyboard.dismiss();
20+
return false;
21+
}
22+
723
const StoryView = () => {
824
const context = useStoryContext();
925
const id = context?.id;
1026

1127
if (context && context.unboundStoryFn) {
1228
const { unboundStoryFn: StoryComponent } = context;
1329

14-
// Wrapped in `TouchableWithoutFeedback` so that a tap in the story view,
15-
// outside of something that handles the press, will dismiss the keyboard.
1630
return (
17-
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
18-
<Box flex key={id} testID={id}>
19-
{StoryComponent && <StoryComponent {...context} />}
20-
</Box>
21-
</TouchableWithoutFeedback>
31+
<Box flex key={id} testID={id} onStartShouldSetResponder={dismissOnStartResponder}>
32+
{StoryComponent && <StoryComponent {...context} />}
33+
</Box>
2234
);
2335
}
2436

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import React from 'react';
2+
import { ComponentMeta, ComponentStory } from '@storybook/react-native';
3+
import { View, ScrollView, Text, TextInput, TouchableOpacity } from 'react-native';
4+
5+
export default {
6+
title: 'Interaction Example',
7+
parameters: {
8+
notes: `
9+
Use these example to test that tapping the story view will dismiss the keyboard,
10+
but won't interfere with scrolling or other touch interactions.
11+
`,
12+
},
13+
} as ComponentMeta<any>;
14+
15+
type InteractionExampleStory = ComponentStory<any>;
16+
17+
function ExampleItem({ children }) {
18+
return (
19+
<View
20+
style={{
21+
flex: 1,
22+
borderRadius: 8,
23+
backgroundColor: '#dee2e3',
24+
marginBottom: 8,
25+
height: 44,
26+
alignItems: 'center',
27+
justifyContent: 'center',
28+
}}
29+
>
30+
<Text style={{ color: '#001a23' }}>{children}</Text>
31+
</View>
32+
);
33+
}
34+
35+
function ExampleInput() {
36+
return (
37+
<TextInput
38+
style={{
39+
borderWidth: 1.25,
40+
borderColor: '#a3c1e0',
41+
marginBottom: 8,
42+
borderRadius: 8,
43+
height: 40,
44+
paddingHorizontal: 8,
45+
}}
46+
placeholder="Type something"
47+
/>
48+
);
49+
}
50+
51+
export const Static: InteractionExampleStory = () => (
52+
<>
53+
<ExampleInput />
54+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
55+
<Text>
56+
Opening the keyboard and then tapping somewhere in a story view will dismiss the keyboard.
57+
</Text>
58+
</View>
59+
</>
60+
);
61+
62+
export const Touchable: InteractionExampleStory = () => {
63+
const [count, increment] = React.useReducer((state) => state + 1, 0);
64+
return (
65+
<>
66+
<ExampleInput />
67+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
68+
<Text>Pressed {count} time(s)</Text>
69+
<TouchableOpacity
70+
style={{
71+
borderWidth: 1,
72+
borderRadius: 8,
73+
padding: 8,
74+
marginVertical: 16,
75+
borderColor: '#b2cbe6',
76+
backgroundColor: '#dcebf9',
77+
}}
78+
onPress={() => increment()}
79+
>
80+
<Text>This button can be tapped without the story view interfering.</Text>
81+
</TouchableOpacity>
82+
</View>
83+
</>
84+
);
85+
};
86+
87+
export const Scrolling: InteractionExampleStory = () => (
88+
<>
89+
<ExampleInput />
90+
<ScrollView>
91+
<ExampleItem>This can scroll when the keyboard is closed.</ExampleItem>
92+
<ExampleItem>And also when the keyboard is open.</ExampleItem>
93+
{Array(25)
94+
.fill(true)
95+
.map((_ignored, idx) => (
96+
<ExampleItem key={idx}>Item #{idx}</ExampleItem>
97+
))}
98+
</ScrollView>
99+
</>
100+
);

0 commit comments

Comments
 (0)