Move SafeAreaView out of DrawerNavigatorItems to DrawerSidebar. (#2967)

* Move SafeAreaView out of DrawerNavigatorItems to DrawerSidebar.

The SafeAreaView was breaking custom contentContainers in drawers. Fixes #2963.

* Fix SafeAreaView in DrawerItems and Drawer contentComponent

* Fix flow error and tests.

* Update Drawer documentation with SafeAreaView recommendation. Remove one of the examples for `contentComponent`.

* Put the View back.

* Update snapshot.
This commit is contained in:
Dave Pack
2017-11-14 20:09:59 +00:00
parent 5757af9f25
commit b349d700b7
6 changed files with 185 additions and 135 deletions

View File

@@ -94,21 +94,6 @@ The route configs object is a mapping from route name to a route config, which t
- `useNativeAnimations` - Enable native animations. Default is `true`.
- `drawerBackgroundColor` - Use the Drawer background for some color. The Default is `white`.
#### Example:
Default the `DrawerView` isn't scrollable.
To achieve a scrollable `View`, you have to use the `contentComponent` to customize the container,
as you can see in the example below.
```js
{
drawerWidth: 200,
drawerPosition: 'right',
contentComponent: props => <ScrollView><DrawerItems {...props} /></ScrollView>,
drawerBackgroundColor: 'transparent'
}
```
Several options get passed to the underlying router to modify navigation logic:
- `initialRouteName` - The routeName for the initial route.
@@ -118,15 +103,17 @@ Several options get passed to the underlying router to modify navigation logic:
### Providing a custom `contentComponent`
You can easily override the default component used by `react-navigation`:
The default component for the drawer is scrollable and only contains links for the routes in the RouteConfig. You can easily override the default component to add a header, footer, or other content to the drawer. By default the drawer is scrollable and supports iPhone X safe area. If you customize the content, be sure to wrap the content in a SafeAreaView:
```js
import { DrawerItems } from 'react-navigation';
import { DrawerItems, SafeAreaView } from 'react-navigation';
const CustomDrawerContentComponent = (props) => (
<View style={styles.container}>
<DrawerItems {...props} />
</View>
<ScrollView>
<SafeAreaView style={styles.container} forceInset={{ top: 'always', horizontal: 'never' }}>
<DrawerItems {...props} />
</SafeAreaView>
</ScrollView>
);
const styles = StyleSheet.create({
@@ -145,7 +132,6 @@ const styles = StyleSheet.create({
- `inactiveTintColor` - label and icon color of the inactive label
- `inactiveBackgroundColor` - background color of the inactive label
- `onItemPress(route)` - function to be invoked when an item is pressed
- `itemsContainerForceInset` - override default forceInset on the SafeAreaView that wraps the items container component
- `itemsContainerStyle` - style object for the content section
- `itemStyle` - style object for the single item, which can contain an Icon and/or a Label
- `labelStyle` - style object to overwrite `Text` style inside content section, when your label is a string

View File

@@ -1,7 +1,7 @@
/* @flow */
import React from 'react';
import { Dimensions, Platform } from 'react-native';
import { Dimensions, Platform, ScrollView } from 'react-native';
import createNavigator from './createNavigator';
import createNavigationContainer from '../createNavigationContainer';
@@ -9,6 +9,7 @@ import TabRouter from '../routers/TabRouter';
import DrawerScreen from '../views/Drawer/DrawerScreen';
import DrawerView from '../views/Drawer/DrawerView';
import DrawerItems from '../views/Drawer/DrawerNavigatorItems';
import SafeAreaView from '../views/SafeAreaView';
import NavigatorTypes from './NavigatorTypes';
@@ -25,13 +26,21 @@ export type DrawerNavigatorConfig = {
const { height, width } = Dimensions.get('window');
const defaultContentComponent = (props: *) => (
<ScrollView alwaysBounceVertical={false}>
<SafeAreaView forceInset={{ top: 'always', horizontal: 'never' }}>
<DrawerItems {...props} />
</SafeAreaView>
</ScrollView>
);
const DefaultDrawerConfig = {
/*
* Default drawer width is screen width - header width
* https://material.io/guidelines/patterns/navigation-drawer.html
*/
drawerWidth: Math.min(height, width) - (Platform.OS === 'android' ? 56 : 64),
contentComponent: DrawerItems,
contentComponent: defaultContentComponent,
drawerPosition: 'left',
drawerBackgroundColor: 'white',
useNativeAnimations: true,

View File

@@ -91,99 +91,146 @@ exports[`DrawerNavigator renders successfully 1`] = `
]
}
>
<View
onLayout={[Function]}
<RCTScrollView
DEPRECATED_sendUpdatedChildFrames={false}
alwaysBounceHorizontal={undefined}
alwaysBounceVertical={false}
onContentSizeChange={null}
onMomentumScrollBegin={[Function]}
onMomentumScrollEnd={[Function]}
onResponderGrant={[Function]}
onResponderReject={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={undefined}
onResponderTerminationRequest={[Function]}
onScroll={[Function]}
onScrollBeginDrag={[Function]}
onScrollEndDrag={[Function]}
onScrollShouldSetResponder={[Function]}
onStartShouldSetResponder={[Function]}
onStartShouldSetResponderCapture={[Function]}
onTouchEnd={[Function]}
onTouchMove={[Function]}
onTouchStart={[Function]}
scrollEventThrottle={undefined}
sendMomentumEvents={false}
style={
Object {
"paddingBottom": 0,
"paddingLeft": 0,
"paddingRight": 0,
"paddingTop": 20,
}
Array [
Object {
"flexDirection": "column",
"flexGrow": 1,
"flexShrink": 1,
"overflow": "scroll",
},
undefined,
]
}
>
<View
<RCTScrollContentView
collapsable={false}
removeClippedSubviews={undefined}
style={
Array [
Object {
"paddingVertical": 4,
},
undefined,
undefined,
]
}
>
<View
accessibilityComponentType={undefined}
accessibilityLabel={undefined}
accessibilityTraits={undefined}
accessible={true}
collapsable={undefined}
hitSlop={undefined}
isTVSelectable={true}
nativeID={undefined}
onLayout={undefined}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
onLayout={[Function]}
style={
Object {
"opacity": 1,
"paddingBottom": 0,
"paddingLeft": 0,
"paddingRight": 0,
"paddingTop": 20,
}
}
testID={undefined}
tvParallaxProperties={undefined}
>
<View
onLayout={[Function]}
style={
Object {
"backgroundColor": "rgba(0, 0, 0, .04)",
"paddingBottom": 0,
"paddingLeft": 0,
"paddingRight": 0,
"paddingTop": 20,
}
Array [
Object {
"paddingVertical": 4,
},
undefined,
]
}
>
<View
accessibilityComponentType={undefined}
accessibilityLabel={undefined}
accessibilityTraits={undefined}
accessible={true}
collapsable={undefined}
hitSlop={undefined}
isTVSelectable={true}
nativeID={undefined}
onLayout={undefined}
onResponderGrant={[Function]}
onResponderMove={[Function]}
onResponderRelease={[Function]}
onResponderTerminate={[Function]}
onResponderTerminationRequest={[Function]}
onStartShouldSetResponder={[Function]}
style={
Array [
Object {
"alignItems": "center",
"flexDirection": "row",
},
undefined,
]
Object {
"opacity": 1,
}
}
testID={undefined}
tvParallaxProperties={undefined}
>
<Text
accessible={true}
allowFontScaling={true}
disabled={false}
ellipsizeMode="tail"
<View
onLayout={[Function]}
style={
Array [
Object {
"fontWeight": "bold",
"margin": 16,
},
Object {
"color": "#2196f3",
},
undefined,
]
Object {
"backgroundColor": "rgba(0, 0, 0, .04)",
"paddingBottom": 0,
"paddingLeft": 0,
"paddingRight": 0,
"paddingTop": 0,
}
}
>
Welcome anonymous
</Text>
<View
style={
Array [
Object {
"alignItems": "center",
"flexDirection": "row",
},
undefined,
]
}
>
<Text
accessible={true}
allowFontScaling={true}
disabled={false}
ellipsizeMode="tail"
style={
Array [
Object {
"fontWeight": "bold",
"margin": 16,
},
Object {
"color": "#2196f3",
},
undefined,
]
}
>
Welcome anonymous
</Text>
</View>
</View>
</View>
</View>
</View>
</View>
</View>
</RCTScrollContentView>
</RCTScrollView>
</View>
</View>
</View>

View File

@@ -31,6 +31,7 @@ type Props = {
itemStyle?: ViewStyleProp,
labelStyle?: TextStyleProp,
iconContainerStyle?: ViewStyleProp,
drawerPosition: 'left' | 'right',
};
/**
@@ -47,61 +48,63 @@ const DrawerNavigatorItems = ({
getLabel,
renderIcon,
onItemPress,
itemsContainerForceInset = { horizontal: 'never', top: 'always' },
itemsContainerStyle,
itemStyle,
labelStyle,
iconContainerStyle,
drawerPosition,
}: Props) => (
<SafeAreaView forceInset={itemsContainerForceInset}>
<View style={[styles.container, itemsContainerStyle]}>
{items.map((route: NavigationRoute, index: number) => {
const focused = activeItemKey === route.key;
const color = focused ? activeTintColor : inactiveTintColor;
const backgroundColor = focused
? activeBackgroundColor
: inactiveBackgroundColor;
const scene = { route, index, focused, tintColor: color };
const icon = renderIcon(scene);
const label = getLabel(scene);
return (
<TouchableItem
key={route.key}
onPress={() => {
onItemPress({ route, focused });
<View style={[styles.container, itemsContainerStyle]}>
{items.map((route: NavigationRoute, index: number) => {
const focused = activeItemKey === route.key;
const color = focused ? activeTintColor : inactiveTintColor;
const backgroundColor = focused
? activeBackgroundColor
: inactiveBackgroundColor;
const scene = { route, index, focused, tintColor: color };
const icon = renderIcon(scene);
const label = getLabel(scene);
return (
<TouchableItem
key={route.key}
onPress={() => {
onItemPress({ route, focused });
}}
delayPressIn={0}
>
<SafeAreaView
style={{ backgroundColor }}
forceInset={{
[drawerPosition]: 'always',
[drawerPosition === 'left' ? 'right' : 'left']: 'never',
vertical: 'never',
}}
delayPressIn={0}
>
<SafeAreaView
style={{ backgroundColor }}
forceInset={{ horizontal: 'always' }}
>
<View style={[styles.item, itemStyle]}>
{icon ? (
<View
style={[
styles.icon,
focused ? null : styles.inactiveIcon,
iconContainerStyle,
]}
>
{icon}
</View>
) : null}
{typeof label === 'string' ? (
<Text style={[styles.label, { color }, labelStyle]}>
{label}
</Text>
) : (
label
)}
</View>
</SafeAreaView>
</TouchableItem>
);
})}
</View>
</SafeAreaView>
<View style={[styles.item, itemStyle]}>
{icon ? (
<View
style={[
styles.icon,
focused ? null : styles.inactiveIcon,
iconContainerStyle,
]}
>
{icon}
</View>
) : null}
{typeof label === 'string' ? (
<Text style={[styles.label, { color }, labelStyle]}>
{label}
</Text>
) : (
label
)}
</View>
</SafeAreaView>
</TouchableItem>
);
})}
</View>
);
/* Material design specs - https://material.io/guidelines/patterns/navigation-drawer.html#navigation-drawer-specs */

View File

@@ -7,6 +7,8 @@ import withCachedChildNavigation from '../../withCachedChildNavigation';
import NavigationActions from '../../NavigationActions';
import invariant from '../../utils/invariant';
import SafeAreaView from '../SafeAreaView';
import type {
NavigationScreenProp,
NavigationRoute,
@@ -34,6 +36,7 @@ type Props = {
contentOptions?: {},
screenProps?: {},
style?: ViewStyleProp,
drawerPosition?: 'left' | 'right',
};
/**
@@ -123,6 +126,7 @@ class DrawerSidebar extends React.PureComponent<Props> {
renderIcon={this._renderIcon}
onItemPress={this._onItemPress}
router={this.props.router}
drawerPosition={this.props.drawerPosition}
/>
</View>
);

View File

@@ -134,6 +134,7 @@ export default class DrawerView<T: NavigationRoute> extends React.PureComponent<
router={this.props.router}
contentComponent={this.props.contentComponent}
contentOptions={this.props.contentOptions}
drawerPosition={this.props.drawerPosition}
style={this.props.style}
/>
);