Skip to content

Commit 943db65

Browse files
ceceppaJDMathew
andauthored
Make ScrollView optional (#218)
* Make ScrollView optional * Simplify scrollViewProps * Added BottomSheet docs * fix `topInset` code style * fix example with updated BottomSheet props * update 'children' to use code in docs * Add additional BottomSheet props to docs * fix doc render error * change prop name --------- Co-authored-by: JDMathew <[email protected]>
1 parent 545d60b commit 943db65

File tree

4 files changed

+309
-14
lines changed

4 files changed

+309
-14
lines changed

examples/shared/src/screens/BottomSheet.screen.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ export const BottomSheetScreen: React.FC<TimedActionProps> = () => {
3030
}
3131
scrollViewProps={{
3232
style: styles.modalViewContent,
33-
}}
34-
scrollEnabled={true}>
33+
scrollEnabled: true,
34+
}}>
3535
<FormScreen />
3636
</BottomSheet>
3737
<CTAPressable

packages/extras/docs/BottomSheet.md

Lines changed: 262 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,262 @@
1+
import { Required } from '@site/src/components';
2+
3+
# BottomSheet
4+
5+
AMA Provides an accessible bottom sheet built on top
6+
of [react-native-reanimated](https://github.com/software-mansion/react-native-reanimated)
7+
and [react-native-gesture-handler](https://github.com/software-mansion/react-native-gesture-handler).
8+
9+
## Example
10+
11+
```jsx
12+
import { BottomSheet } from '@react-native-ama/extras
13+
14+
const Component = () => (
15+
<BottomSheet
16+
visible={modalVisible}
17+
onRequestClose={() => {
18+
setModalVisible(!modalVisible);
19+
}}
20+
closeActionAccessibilityLabel="close bottomsheet"
21+
bottomSheetStyle={styles.modalView}
22+
headerComponent={
23+
<View style={{ paddingHorizontal: theme.padding.big }}>
24+
<Header title="This is the bottom sheet" />
25+
</View>
26+
}
27+
scrollViewStyle={styles.modalViewContent}>
28+
<Pressable
29+
onPress={() => setModalVisible(!modalVisible)}
30+
accessibilityRole="button">
31+
<Text>Close bottom sheet</Text>
32+
</Pressable>
33+
</BottomSheet>
34+
);
35+
```
36+
37+
## Accessibility
38+
39+
- Checks that the `closeActionAccessibilityLabel` is a valid [accessibilityLabel](./guidelines/accessibility-label)
40+
- Provides a way [to close the bottom sheet](../guidelines/bottomsheet#2-can-be-dismissed) when the user taps on the overlay
41+
- Makes sure that the overlay is announced as "button"
42+
- Uses slide-in and slide-out animation **only** if the [Reduce Motion] (https://reactnative.dev/docs/accessibilityinfo) preference is turned off
43+
- Prevents the focus from [escaping the bottom sheet](../guidelines/bottomsheet#3-the-focus-stays-inside-it)
44+
- Provides a draggable area respecting the [minimum size](../guidelines/minimum-size)
45+
46+
## Props
47+
48+
### `animationDuration`
49+
50+
The duration in milliseconds of the slide in/out animation.
51+
52+
| Type | Default |
53+
| ------ | ------- |
54+
| number | 300 |
55+
56+
### `autoCloseDelay`
57+
58+
The duration in milliseconds before auto-closing the bottom sheet
59+
60+
| Type | Default |
61+
| ------ | --------- |
62+
| number | undefined |
63+
64+
:::tip
65+
66+
The auto-close will respect the user [Timed action](./guidelines/timed-actions) preference.
67+
68+
:::
69+
70+
### `bottomSheetStyle`
71+
72+
The style to use for the bottom sheet panel
73+
74+
| Type | Default |
75+
| --------- | -------------------------------------------- |
76+
| ViewStyle | `{ width: '100%', backgroundColor: '#fff' }` |
77+
78+
### <Required /> `closeActionAccessibilityLabel`
79+
80+
The accessibility label to use for the overlay button.
81+
82+
| Type |
83+
| ------ |
84+
| string |
85+
86+
### `closeDistance`
87+
88+
A decimal fraction percentage of the content height which represents the minimum distance to swipe before the `onClose` function is run. For example, 0.3 represents 30% of the content height needing to be gesture swiped away before the `BottomSheet` will close and the `onClose` function will run.
89+
90+
| Type | Default |
91+
| ------ | ------- |
92+
| number | 0.3 |
93+
94+
### `footerComponent`
95+
96+
The bottom sheet footer component.
97+
98+
| Type |
99+
| ----------- |
100+
| JSX.Element |
101+
102+
### `handleComponent`
103+
104+
It can be used to either disable the default handle "line" or replace it with a custom component.
105+
106+
| Type |
107+
| ----------------------- |
108+
| JSX.Element \| `'none'` |
109+
110+
### `handleStyle`
111+
112+
The style for the draggable line
113+
114+
| Type | Default |
115+
| --------- | -------------------------------------------------------------------------------------------------------------------------- |
116+
| ViewStyle | `{ width: 48, height: 4, backgroundColor: 'grey', alignSelf: 'center', marginBottom: 24, borderRadius: 2, marginTop: 12 }` |
117+
118+
### `headerComponent`
119+
120+
The bottom sheet header component.
121+
122+
| Type |
123+
| ----------- |
124+
| JSX.Element |
125+
126+
### `maxHeight`
127+
128+
The maximum height of the bottom sheet.
129+
130+
| Type | Default |
131+
| ------ | ------------------------ |
132+
| number | 90% of the screen height |
133+
134+
### `minVelocityToClose`
135+
136+
The minimum velocity needed by quickly swiping down to close the bottom sheet.
137+
138+
| Type | Default |
139+
| ------ | ------------------------ |
140+
| number | 90% of the screen height |
141+
142+
### `onBottomSheetHidden`
143+
144+
The callback to triggered after animations when the BottomSheet is hidden
145+
146+
| Type |
147+
| ---------- |
148+
| () => void |
149+
150+
### <Required /> `onClose`
151+
152+
The callback to trigger when the BottomSheet is dismissed
153+
154+
| Type |
155+
| ---------- |
156+
| () => void |
157+
158+
### `overlayOpacity`
159+
160+
The opacity of the overlay.
161+
162+
| Type | Default |
163+
| ------ | ------- |
164+
| number | 1 |
165+
166+
### `overlayStyle`
167+
168+
The style to use for the overlay
169+
170+
| Type | Default |
171+
| --------- | ---------------------------------------------------- |
172+
| ViewStyle | `{ backgroundColor: 'rgba(0, 0, 0, 0.5)', flex: 1 }` |
173+
174+
### `panGestureEnabled`
175+
176+
Enable or disable the dragging gesture.
177+
178+
| Type | Default |
179+
| ------- | ------- |
180+
| boolean | true |
181+
182+
### `persistent`
183+
184+
If true, the bottom sheet will not be closed when the user taps on the overlay; the dragging gesture is also disabled.
185+
186+
| Type | Default |
187+
| ------- | ------- |
188+
| boolean | true |
189+
190+
### `hasScrollableContent`
191+
192+
If true, `children` of `BottomSheet` are wrapped in a [`<ScrollView />`](https://reactnative.dev/docs/scrollview)
193+
194+
| Type | Default |
195+
| ------- | ------- |
196+
| boolean | true |
197+
198+
### `scrollViewProps`
199+
200+
The props to use for the [`<ScrollView />`](https://reactnative.dev/docs/scrollview) that wraps the BottomSheet content
201+
202+
| Type | Default |
203+
| --------------- | --------- |
204+
| scrollViewProps | undefined |
205+
206+
### `shouldHandleKeyboardEvents`
207+
208+
If true, keyboard events are handled internally by the `BottomSheet` allowing for scrolling and animations to run smoothly
209+
210+
| Type | Default |
211+
| ------- | ------- |
212+
| boolean | true |
213+
214+
### `testID`
215+
216+
A testID to be used by the BottomSheet. The `Modal` component within the `BottomSheet` will receive this base `testID` and other internal components will receive abstractions of the base `testID`. It is best to view the source code to understand the hierarchy of these components and what testID's they will receive.
217+
218+
| Type |
219+
| ------ |
220+
| string |
221+
222+
### <Required /> `topInset`
223+
224+
The value is used to calculate the correct max ScrollView height.
225+
226+
| Type | Default |
227+
| ------ | ------- |
228+
| number | 0 |
229+
230+
### <Required /> `visible`
231+
232+
The BottomSheet visibility
233+
234+
| Type |
235+
| ------- |
236+
| boolean |
237+
238+
### `ref`
239+
240+
A `ref` object used to call `close` and `isVisible` functions on the bottomSheet
241+
242+
| Type |
243+
| ----------------------------------------------------------- |
244+
| `{ close: () => Promise<void>; isVisible: () => boolean; }` |
245+
246+
## Known issues
247+
248+
If the app crashes with the following error:
249+
250+
> Unsupported top level event type "onGestureHandlerStateChange" dispatched
251+
252+
Add the following import at the top of your `App.tsx|jsx` file:
253+
254+
```js
255+
// https://github.com/software-mansion/react-native-gesture-handler/issues/320
256+
import 'react-native-gesture-handler';
257+
```
258+
259+
## Related guidelines
260+
261+
- [BottomSheet](../guidelines/bottomsheet)
262+
- [Focus](../guidelines/focus)

packages/extras/src/components/BottomSheet.tsx

Lines changed: 44 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,11 @@ export type BottomSheetProps = {
5151
overlayStyle?: ViewStyle | ViewStyle[];
5252
panGestureEnabled?: boolean;
5353
persistent?: boolean;
54-
scrollEnabled?: boolean;
55-
scrollViewProps?: Omit<ScrollViewProps, 'scrollEnabled'>;
54+
hasScrollableContent?: boolean;
55+
scrollViewProps?: Omit<
56+
ScrollViewWrapperProps,
57+
'testID' | 'maxScrollViewHeight'
58+
>;
5659
testID?: string;
5760
topInset: number;
5861
visible: boolean;
@@ -85,8 +88,8 @@ export const BottomSheet = React.forwardRef<
8588
animationDuration = 300,
8689
closeDistance = 0.3,
8790
handleComponent,
88-
scrollEnabled = false,
8991
scrollViewProps,
92+
hasScrollableContent = true,
9093
persistent = false,
9194
autoCloseDelay,
9295
panGestureEnabled = true,
@@ -258,6 +261,10 @@ export const BottomSheet = React.forwardRef<
258261
? 1
259262
: 0;
260263

264+
const ContentWrapper = hasScrollableContent
265+
? ScrollViewWrapper
266+
: FragmentWrapper;
267+
261268
return (
262269
<Modal
263270
animationType="none"
@@ -331,16 +338,12 @@ export const BottomSheet = React.forwardRef<
331338
}}>
332339
{headerComponent}
333340
</View>
334-
<ScrollView
341+
<ContentWrapper
335342
{...scrollViewProps}
336-
style={[
337-
{ maxHeight: maxScrollViewHeight },
338-
scrollViewProps?.style,
339-
]}
340-
scrollEnabled={scrollEnabled}
341-
testID={`${testID}-scrollview`}>
343+
testID={`${testID}-scrollview`}
344+
maxScrollViewHeight={maxScrollViewHeight}>
342345
{children}
343-
</ScrollView>
346+
</ContentWrapper>
344347
<View
345348
onLayout={event => {
346349
setFooterHeight(event.nativeEvent.layout.height);
@@ -414,6 +417,36 @@ const GestureHandler = ({
414417
);
415418
};
416419

420+
type ScrollViewWrapperProps = {
421+
testID?: string;
422+
maxScrollViewHeight: number;
423+
} & ScrollViewProps;
424+
425+
const ScrollViewWrapper: React.FC<ScrollViewWrapperProps> = ({
426+
scrollEnabled = false,
427+
testID,
428+
maxScrollViewHeight,
429+
children,
430+
style,
431+
...rest
432+
}) => {
433+
return (
434+
<ScrollView
435+
{...rest}
436+
style={[{ maxHeight: maxScrollViewHeight }, style]}
437+
scrollEnabled={scrollEnabled}
438+
testID={`${testID}-scrollview`}>
439+
{children}
440+
</ScrollView>
441+
);
442+
};
443+
444+
const FragmentWrapper: React.FC<React.PropsWithChildren<any>> = ({
445+
children,
446+
}) => {
447+
return <>{children}</>;
448+
};
449+
417450
const styles = StyleSheet.create({
418451
overlay: {
419452
backgroundColor: 'rgba(0, 0, 0, 0.5)',

website/versioned_docs/version-0.7.x/components/BottomSheet.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ The props to use for the [`<ScrollView />`](https://reactnative.dev/docs/scrollv
183183
| --------------- | --------- |
184184
| scrollViewProps | undefined |
185185

186-
### <Required /> ` `topInset`
186+
### <Required /> `topInset`
187187

188188
The value is used to calculate the correct max ScrollView height.
189189

0 commit comments

Comments
 (0)