Skip to content

Commit 2e02fc5

Browse files
jasozhfacebook-github-bot
authored andcommitted
Add ScrollView example in KeyboardAvoidingViewExample
Summary: Add a new RNTester example in KeyboardAvoidingView for using the component with a ScrollView. This reproduces the issue raised in [#16826](#16826) where keyboard avoiding behavior fails for multi-line text fields in a ScrollView on iOS. As this is a common use case, the example acts as a reference to show intended behavior and prevent future regressions. Changelog: [Internal] Differential Revision: D119204439
1 parent ba1b779 commit 2e02fc5

1 file changed

Lines changed: 116 additions & 43 deletions

File tree

‎packages/rn-tester/js/examples/KeyboardAvoidingView/KeyboardAvoidingViewExample.js‎

Lines changed: 116 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
KeyboardAvoidingView,
2121
Modal,
2222
Pressable,
23+
ScrollView,
2324
StyleSheet,
2425
Text,
2526
TextInput,
@@ -57,54 +58,56 @@ const CloseButton = (props: {setModalOpen: boolean => void}) => {
5758
);
5859
};
5960

61+
type KeyboardAvoidingBehavior = 'padding' | 'position' | 'height';
62+
63+
const BEHAVIORS: Array<KeyboardAvoidingBehavior> = [
64+
'padding',
65+
'position',
66+
'height',
67+
];
68+
69+
const BehaviorPicker = (props: {
70+
behavior: KeyboardAvoidingBehavior,
71+
setBehavior: KeyboardAvoidingBehavior => void,
72+
}) => {
73+
return (
74+
<View
75+
style={{
76+
flexDirection: 'row',
77+
justifyContent: 'center',
78+
}}>
79+
{BEHAVIORS.map(behavior => {
80+
const selected = props.behavior === behavior;
81+
return (
82+
<TouchableOpacity
83+
key={behavior}
84+
onPress={() => props.setBehavior(behavior)}
85+
style={[
86+
styles.pillStyle,
87+
{backgroundColor: selected ? 'blue' : 'white'},
88+
]}>
89+
<Text
90+
style={{
91+
textTransform: 'capitalize',
92+
color: selected ? 'white' : 'blue',
93+
}}>
94+
{behavior}
95+
</Text>
96+
</TouchableOpacity>
97+
);
98+
})}
99+
</View>
100+
);
101+
};
102+
60103
const KeyboardAvoidingViewBehaviour = () => {
61104
const [modalOpen, setModalOpen] = useState(false);
62-
const [behavior, setBehavior] = useState('padding');
105+
const [behavior, setBehavior] = useState<KeyboardAvoidingBehavior>('padding');
63106
return (
64107
<View style={styles.outerContainer}>
65108
<Modal animationType="fade" visible={modalOpen}>
66-
{/* $FlowFixMe[incompatible-type] Natural Inference rollout. See
67-
* https://fburl.com/workplace/6291gfvu */}
68109
<KeyboardAvoidingView behavior={behavior} style={styles.container}>
69-
<View
70-
style={{
71-
flexDirection: 'row',
72-
justifyContent: 'center',
73-
}}>
74-
<TouchableOpacity
75-
onPress={() => setBehavior('padding')}
76-
style={[
77-
styles.pillStyle,
78-
{backgroundColor: behavior === 'padding' ? 'blue' : 'white'},
79-
]}>
80-
<Text style={{color: behavior === 'padding' ? 'white' : 'blue'}}>
81-
Padding
82-
</Text>
83-
</TouchableOpacity>
84-
<TouchableOpacity
85-
onPress={() => setBehavior('position')}
86-
style={[
87-
styles.pillStyle,
88-
{backgroundColor: behavior === 'position' ? 'blue' : 'white'},
89-
]}>
90-
<Text style={{color: behavior === 'position' ? 'white' : 'blue'}}>
91-
Position
92-
</Text>
93-
</TouchableOpacity>
94-
<TouchableOpacity
95-
onPress={() => setBehavior('height')}
96-
style={[
97-
styles.pillStyle,
98-
{backgroundColor: behavior === 'height' ? 'blue' : 'white'},
99-
]}>
100-
<Text
101-
style={{
102-
color: behavior === 'height' ? 'white' : 'blue',
103-
}}>
104-
Height
105-
</Text>
106-
</TouchableOpacity>
107-
</View>
110+
<BehaviorPicker behavior={behavior} setBehavior={setBehavior} />
108111
<CloseButton setModalOpen={setModalOpen} />
109112
<TextInputForm />
110113
</KeyboardAvoidingView>
@@ -189,6 +192,46 @@ const KeyboardAvoidingContentContainerStyle = () => {
189192
);
190193
};
191194

195+
const KeyboardAvoidingScrollView = () => {
196+
const [modalOpen, setModalOpen] = useState(false);
197+
const [behavior, setBehavior] = useState<KeyboardAvoidingBehavior>('padding');
198+
return (
199+
<View>
200+
<Modal animationType="fade" visible={modalOpen}>
201+
<KeyboardAvoidingView
202+
behavior={behavior}
203+
style={styles.scrollViewContainer}
204+
contentContainerStyle={{flex: 1}}>
205+
<View style={{paddingHorizontal: 20}}>
206+
<BehaviorPicker behavior={behavior} setBehavior={setBehavior} />
207+
<CloseButton setModalOpen={setModalOpen} />
208+
</View>
209+
<ScrollView
210+
style={{flex: 1}}
211+
contentContainerStyle={styles.scrollViewContent}>
212+
{Array.from({length: 10}, (_, index) => (
213+
<View key={index} style={styles.fillerItem}>
214+
<Text style={{fontSize: 16}}>Item {index + 1}</Text>
215+
</View>
216+
))}
217+
<TextInput placeholder="Name" style={[styles.textInput]} />
218+
<TextInput
219+
placeholder="Message"
220+
multiline
221+
style={[styles.textInput, styles.multilineTextInput]}
222+
/>
223+
</ScrollView>
224+
</KeyboardAvoidingView>
225+
</Modal>
226+
<View>
227+
<Pressable onPress={() => setModalOpen(true)}>
228+
<Text style={styles.touchableText}>Open Example</Text>
229+
</Pressable>
230+
</View>
231+
</View>
232+
);
233+
};
234+
192235
const styles = StyleSheet.create({
193236
outerContainer: {
194237
flex: 1,
@@ -204,13 +247,34 @@ const styles = StyleSheet.create({
204247
paddingTop: 20,
205248
backgroundColor: '#abdebf',
206249
},
250+
scrollViewContainer: {
251+
flex: 1,
252+
paddingTop: 100,
253+
},
254+
scrollViewContent: {
255+
paddingBottom: 60,
256+
paddingHorizontal: 20,
257+
},
258+
fillerItem: {
259+
backgroundColor: '#eeeeee',
260+
borderRadius: 8,
261+
paddingVertical: 16,
262+
paddingHorizontal: 12,
263+
marginBottom: 20,
264+
alignItems: 'center',
265+
},
207266
textInput: {
208267
borderRadius: 5,
209268
borderWidth: 1,
210-
height: 44,
269+
minHeight: 44,
211270
marginBottom: 20,
212271
paddingHorizontal: 10,
213272
},
273+
multilineTextInput: {
274+
minHeight: 88,
275+
paddingVertical: 10,
276+
textAlignVertical: 'top',
277+
},
214278
pillStyle: {
215279
padding: 10,
216280
marginHorizontal: 5,
@@ -267,4 +331,13 @@ exports.examples = [
267331
return <KeyboardAvoidingContentContainerStyle />;
268332
},
269333
},
334+
{
335+
title: 'Keyboard Avoiding View with ScrollView',
336+
description:
337+
('A ScrollView with filler content and TextInputs at the bottom inside the scrollable content. ' +
338+
'TextInputs should still be visible when focused.') as string,
339+
render(): React.Node {
340+
return <KeyboardAvoidingScrollView />;
341+
},
342+
},
270343
] as Array<RNTesterModuleExample>;

0 commit comments

Comments
 (0)