React Native Tab Navigator: empty space at bottom of tab bar - reactjs

I'm using react-navigation#4.0.10 and react-native#0.63.5 in my React Native app, and when I use createBottomTabNavigator, there's a gap underneath the tab labels on iPhone 11 Pro. It does not do the same on iPhone 12. My code for the TabNavigatorOptions is as follows
const TabNavigatorOptions = {
tabBarPosition: 'bottom',
lazy: true,
tabBarOptions: {
activeTintColor: TabColors.activeColor,
inactiveTintColor: TabColors.labelColor,
bottomNavigationOptions: {
labelColor: TabColors.labelColor,
rippleColor: "white",
shifting: false,
activeLabelColor: TabColors.activeColor,
backgroundColor: TabColors.backgroundColor
},
style: {
height: 56, elevation: 8, position: 'absolute', left: 0, bottom: 0, right: 0
}
}
}
I've tried adding paddingBottom: 0 to the style object, but it makes no difference.
Does anyone know how I can approach this?
UPDATE:
If I add a red background in tabBarOptions -> style, with SafeAreaView I get this:
and if I remove SafeAreaView I get this

The only solution I could find for now is to remove the bottom inset from SafeAreaView. It's not a good solution in my opinion but at least it works:
import * as React from "react";
import { SafeAreaView } from "react-navigation";
export default function App() {
return (
<SafeAreaView
style={{ flex: 1 }}
forceInset={{ top: "always", bottom: "never" }}
>
<AppNavigator />
</SafeAreaView>
)
}
And if you are using react-native-safe-area-context:
import * as React from "react";
import { SafeAreaView } from 'react-native-safe-area-context';
export default function App() {
return (
<SafeAreaView
style={{ flex: 1 }}
edges={["right", "top", "left"]}
>
<AppNavigator />
</SafeAreaView>
);
}

Related

NativeBase showing error while using in Jest testing

I want to test a basic component name TitleHeader which uses HStack and VStack from native base.
The component:-
import {Center, HStack, VStack} from 'native-base';
import React from 'react';
import {View, Text} from 'react-native';
import appColors from '../../constants/appColors';
//Icon Imports
import Ionicons from 'react-native-vector-icons/Ionicons';
import {TouchableOpacity} from 'react-native-gesture-handler';
import {useNavigation} from '#react-navigation/core';
const TitleHeader = ({
title = 'Header Title',
navigationBack = false,
center = false,
onBackPress,
drawer = false,
style = {},
}) => {
console.log(title);
const navigation = useNavigation();
const handleGoBack = () => {
if (onBackPress && typeof onBackPress === 'function') {
onBackPress();
} else if (drawer) {
navigation.goBack();
} else {
navigation.pop();
}
};
return (
<HStack
bg={appColors.primaryBlue}
style={[
{
height: 55,
position: 'relative',
},
style,
]}
alignItems="center"
px={3}>
{navigationBack ? (
<View style={{position: 'absolute', zIndex: 10, left: 10}}>
<TouchableOpacity
onPress={handleGoBack}
style={{
width: 35,
height: 35,
justifyContent: 'center',
alignItems: 'center',
}}>
<Ionicons name="arrow-back" size={26} color="white" />
</TouchableOpacity>
</View>
) : null}
<VStack flex={1} alignItems="center" pl={2}>
<Text
color="white"
fontSize="lg"
numberOfLines={1}
ellipsizeMode="tail"
style={
center
? {}
: {
width: '80%',
}
}>
{title}
</Text>
</VStack>
</HStack>
);
};
export default TitleHeader;
Following is my test case which uses jest for testing:-
import React from 'react';
import {render} from '#testing-library/react-native';
import TitleHeader from '../src/components/AppHeaders/TitleHeader';
import renderer from 'react-test-renderer';
import {NavigationContainer} from '#react-navigation/native';
import {NativeBaseProvider} from 'native-base';
jest.mock('native-base');
const wrapper = ({children}) => (
<NativeBaseProvider
initialWindowMetrics={{
frame: {x: 0, y: 0, width: 0, height: 0},
insets: {top: 0, left: 0, right: 0, bottom: 0},
}}>
{children}
</NativeBaseProvider>
);
describe('Testing Title Header for screens', () => {
test('should render title header', () => {
const tree = renderer
.create(
<NavigationContainer>
<TitleHeader title={'HEADER TEST'} />
</NavigationContainer>,
{wrapper},
)
.toJSON();
expect(tree).toMatchSnapshot();
});
});
But I am getting this error inspite of using jest.mock('native-base') in my jest.setup.js.
I am new to testing please help me render this component first and test it using jest. Also, one more thing to add is that if I do not use the wrapper function it throws error telling me that "theme is not defined. Did you forget to wrap your app inside NativeBaseProvider?".
If the component that you trying to test has the theme coming from NativeBase you need to provide the <ThemeProvider/> (Spelling might differ depending on the library, in your case <NativeBaseProvider/>
If you do jest.mock('native-base') and you do not return anything will cause Nothing was returned from render error.
Did you add native-base library to transformIgnorePatterns of jest.config as suggested in this issue?

React navigation bar header has a margin on the left

I am using React-Navigation and StackNavigator to implement a navigation bar at the top of my react native app, which shows the app logo centered and a menu button on the right. I cannot get it to take up the complete space of the header container though, there is always a margin of ~20px on the left.
As you can see in my code, I have already applied all kinds of style properties such as margin: 0, padding: 0, alignment: fill and width:100% to headerStyle, headerContainerStyle and the navigation bar component itself, but none of them helped.
App.js:
import React from 'react';
import { StyleSheet, View } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import {Home} from "components/Home";
import {NavigationBar} from "components/NavigationBar";
const Stack = createStackNavigator();
export default function App() {
return (
<NavigationContainer headerStyle={styles.container}>
<Stack.Navigator
initialRouteName="Home" headerStyle={styles.container}
screenOptions={{
headerTitleContainerStyle: styles.container,
headerTitleStyle: styles.title,
headerStyle: styles.header,
headerTitle: props => <NavigationBar {...props} />
}}>
<Stack.Screen name="Home" component={Home} />
</Stack.Navigator>
</NavigationContainer>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignment: 'fill',
width: '100%',
height:'100%',
// useless
margin: 0
},
title: {
},
header: {
flex: 1,
justifyContent: 'center',
backgroundColor: 'red',
// useless
padding: 0,
margin: 0,
marginLeft: 0,
alignment: 'fill',
alignItems: 'center',
alignContent: 'center'
}
});
NavigationBar.jsx:
import React from 'react';
import { Button, StyleSheet, View } from 'react-native';
import { CenteredView } from 'shared/view/CenteredView'; // just a wrapper for View
import { FlexImage } from "shared/image/FlexImage"; // just a wrapper for Image
import { PLACEHOLDER } from "assets/images";
export const NavigationBar = (props) => {
return (
<View style={styles.barContainer}>
<View style={{flex: 1, backgroundColor: 'green'}} />
<CenteredView style={{flex: 2, backgroundColor: 'yellow'}}>
<FlexImage source={PLACEHOLDER} />
</CenteredView>
<CenteredView style={{flex: 1, backgroundColor: 'green'}}>
<Button title="Menu" color="#000" />
</CenteredView>
</View>
);
}
const styles = StyleSheet.create({
barContainer: {
flex: 1,
flexDirection: 'row',
backgroundColor: 'orange',
justifyContent: 'center',
// useless
alignment: 'fill',
width: '100%',
height: '100%',
padding: 0,
margin: 0,
marginLeft: 0
}
});
I assigned different colors to the containers and views to demonstrate the issue. The red column is what is wrong:
UPDATE: I noticed that I can swipe the complete page to the left so that the margin disappears and the very left of the content container (in aqua blue) disappears, leaving a blank area on the right (see picture below). This only works in the mobile device simulator in Chrome. On my Android phone I still have the margin but cannot swipe.
I fixed the issue by using the header property rather than headerTitle to declare NavigationBar as my custom header component.
export default function App() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Home"
screenOptions={{
headerStyle: styles.header,
header: props => <NavigationBar {...props} /> // <------- here
}}>
<Stack.Screen name="Home" component={Home} />
</Stack.Navigator>
</NavigationContainer>
);
}
I find the documentation of StackNavigator options a bit confusing. From my understanding, the difference between header and headerTitle is that headerTitle may also be a string and defaults to the scene title if the header component is not available. But that doesn't really explain the differences in layout. In the code example headerTitle is used.
Tried header Component but content went to the next line, would be glad if someone could suggest a better way to handle this case
Solution: I used headerTitle Component but with a little modification on right padding to workaround this issue.
I would like to suggest React navigation Team to provide more control over styles please.

How can i access my state variables in StyleSheet?

I calculate various margin's based on the images i'm displaying in my app, which range in different pixel widths each time images are selected, so my need us quite dynamic. Once i calculate the margin widths, i save them in state variables marginL and marginR.
However i can't seem to access these in StyleSheet, i just get an error message to say marginLeft is undefined.
const styles = StyleSheet.create({
container: {
flex: 1,
},
gap: {
flex: 0.5,
marginLeft: this.state.marginL,
marginRight: this.state.marginR
}
})
How do i get access to my variables?
I'm not sure about dynamically changing in StyleSheet value. But when you calculate the margin widths you can override your style something like below
<View style={[styles.gap, {marginLeft: this.state.marginL, marginRight: this.state.marginR}]} />
Check complete example
import React, { Component } from "react";
import { View, StyleSheet } from "react-native";
export default class App extends Component {
state = {
marginL: 10,
marginR: 20
};
render() {
return (
<View style={styles.container}>
<View style={[ styles.gap, { marginLeft: this.state.marginL, marginRight: this.state.marginR }]} />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "red"
},
gap: {
flex: 0.5,
marginLeft: 0,
marginRight: 0,
backgroundColor: "green"
}
});
This not be the optimal solution but hope this helps you. Feel free for doubts.

React-circular-progressBar not showing initialAnimation

I have a progressBar component. The initial animation refuses to work in spite of setting initialAnimation to true. What am I missing?
Are there any extra CSS needed to fix it?
I just want the initialAnimation on the graph and number need not change
import React from 'react'
import CircularProgressbar from 'react-circular-progressbar'
import { withStyles } from '#material-ui/core/styles'
import Typography from '#material-ui/core/Typography'
import PropTypes from 'prop-types'
const styles = theme => ({
progressBar: {
width: '80px',
margin: 'auto',
marginTop: theme.spacing.unit + 4,
marginBottom: theme.spacing.unit + 4,
position: 'relative'
},
text: {
position: 'absolute',
top: 20,
left: 5,
paddingRight: '5px'
}
});
function StyledProgressbar(props) {
const { classes, palette } = props;
return (
<div className={classes.progressBar}>
<Typography variant="caption" align='center' className={classes.text}>
{props.percentage}% complete
</Typography>
<CircularProgressbar
percentage={props.percentage}
strokeWidth={10}
initialAnimation={true}
styles={{
path: {
stroke: palette.secondary.main,
strokeLinecap: 'round',
transition: 'stroke-dashoffset 0.5s ease 0s',
},
trail: {
stroke: palette.secondary.light,
strokeLinecap: 'round',
}
}}>
</CircularProgressbar>
</div>
);
}
StyledProgressbar.propTypes = {
classes: PropTypes.object,
percentage: PropTypes.number,
palette: PropTypes.object
}
export default withStyles(styles)(StyledProgressbar);
you just need to change this:
import CircularProgressbar from 'react-circular-progressbar';
from this:
import { CircularProgressbar } from 'react-circular-progressbar';
for more information refrence

React Native ScrollView with single child component

I want to have a fullscreen ScrollView which contains all of the contents of my Home screen in my app. The issue I'm facing is that it works as expected when I have two child components for the ScrollView (I added a Text component just to try it), but not if I just have the single content View that I want.
The contents of <View style={style.content}> are taller than the screen size, so it should scroll. But when I remove <Text style={{margin: 60, marginTop: 800}}>Here is some text</Text> the entire screen goes white.
Here's what I've heard about ScrollView:
It needs a limited height (but I should have that since all of the content is currently static (no dynamic data) and all of my components there have a fixed height)
You need to use {flex: 1} on the components inside the ScrollView (I have this style set on my Container component)
Here is my Home component:
import React, { Component } from "react";
import {
StatusBar, StyleSheet, View, ScrollView, Text
} from "react-native";
import { LinearGradient } from "expo";
import MaterialCommunityIcon from "react-native-vector-icons/MaterialCommunityIcons"
import Color from "color";
import globalStyles from "../config/globalStyles";
import Container from "../components/Container/Container";
import Header from "../components/Header/Header";
import FullWidthImage from "../components/Images/FullWidthImage";
import MainFeedbackText from "../components/Text/MainFeedbackText";
import MainStatistics from "../components/Statistics/MainStatistics";
import Button from "../components/Buttons/Button"
export default class Home extends Component {
render() {
return (
<ScrollView style={{borderColor:"red", borderWidth: 2}}>
<Container>
<View style={style.content}>
<MainFeedbackText/>
<Button
text="START NEW WORKOUT"
textStyle={{fontSize: 24}}
buttonStyle={{
backgroundColor: globalStyles.colors.green,
paddingHorizontal: 20,
}}
icon={<MaterialCommunityIcon style={style.settings} name="dumbbell" color="white" size={30}/>}
/>
<Button
text="My workouts"
textStyle={{fontSize: 24, paddingRight: 40}}
buttonStyle={{
backgroundColor: Color("black").fade(0.8),
paddingHorizontal: 20,
margin: 20
}}
icon={<MaterialCommunityIcon style={style.settings} name="format-list-bulleted" color="white" size={20}/>}
/>
<MainStatistics/>
</View>
{/**TODO: ScrollView breaks when removing this line*/}
<Text style={{margin: 60, marginTop: 800}}>Here is some text</Text>
<StatusBar translucent={false}/>
<LinearGradient colors={["transparent", globalStyles.colors.dark]} style={style.gradient}>
<View style={style.image}>
<FullWidthImage requireSource={require("./lifting.jpg")}/>
</View>
</LinearGradient>
<Header/>
</Container>
</ScrollView>
);
}
}
const style = StyleSheet.create({
image: {
opacity: 0.3,
zIndex: -1,
},
gradient: {
position: "absolute",
top: 0,
zIndex: -1
},
content: {
flex: 1,
position: "absolute",
top: 80,
height: 4000,
}
});
Set contentContainerStyle={{ flex: 1 }} for ScrollView to become fullscreen.

Resources