NativeBase showing error while using in Jest testing - reactjs

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?

Related

React Native - React Navigation Cannot use Hooks in DrawerContent

I am developing an e-commerce application using React Native and I am trying to use useState in the drawerContent and it tells me this
Error: Invalid hook call. Hooks can only be called inside of the body
of a function component. This could happen for one of the following
reasons:
You might have mismatching versions of React and the renderer (such as React DOM)
You might be breaking the Rules of Hooks
You might have more than one copy of React in the same app
Thank you in advance for your answers.
Here's the code
import React, { useState } from 'react'
import { View, Text, TouchableOpacity, FlatList, StyleSheet, StatusBar } from 'react-native'
import IonIcons from "react-native-vector-icons/Ionicons"
import { categories } from '../../../services/DataTest'
import DrawerSearch from './DrawerSearch'
import DrawerItem from './DrawerItem'
export default function DrawerContent (props) {
const [search, setSearch] = useState("");
return (
<View>
<TouchableOpacity
style={styles.customDrawerTouch}
>
<View style={styles.backButtonRow}>
<IonIcons
name="ios-arrow-back"
size={25}
style={styles.customDrawerIcon}
color="#666666"
/>
<Text style={{ color: '#666666' }}>Back to Components</Text>
</View>
</TouchableOpacity>
<DrawerSearch value={search} setValue={setSearch}/>
<FlatList
data={categories}
keyExtractor={(item, index) => index.toString()}
renderItem={DrawerItem}
/>
</View>
);
}
const styles = StyleSheet.create({
customDrawerTouch: {
marginTop: StatusBar.currentHeight,
paddingLeft: 13,
paddingTop: 15,
},
customDrawerIcon: {
paddingRight: 10
},
backButtonRow: {
flexDirection: 'row',
alignItems: 'center',
paddingBottom: 17,
paddingLeft: 3,
borderBottomColor: '#F0F0F0',
borderBottomWidth: 1,
},
});
I'm using this component here
import * as React from 'react';
import { View, StyleSheet, StatusBar } from 'react-native';
import { createDrawerNavigator } from '#react-navigation/drawer';
import HeaderCategorie from '../../components/categories/index/HeaderCategorie';
import SearchBar from '../../components/home/index/SearchBar';
import DrawerContent from '../../components/categories/index/DrawerContent';
const Drawer = createDrawerNavigator();
function CategoriesScreen({ navigation }) {
return (
<View style={styles.container}>
<HeaderCategorie navigation={navigation}/>
<View style={styles.headerSearch}>
<SearchBar />
</View>
</View>
)
}
export default function Categories() {
return (
<Drawer.Navigator initialRouteName="Categories"
drawerContent={DrawerContent}
screenOptions={{headerShown:false}}
>
<Drawer.Screen name="Categories" component={CategoriesScreen} />
</Drawer.Navigator>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "flex-start",
alignItems: "center",
marginTop: StatusBar.currentHeight,
},
headerSearch: {
marginVertical:10
},
headerSearchText: {
fontWeight:"bold",
fontSize:35,
marginLeft:20,
marginVertical:15,
}
});
Reason: By using drawerContent={DrawerContent}, you are actually passing the reference of the DrawerContent function, which ends up breaking rules of hooks.
So to resolve this, change the following line:
<Drawer.Navigator initialRouteName="Categories"
drawerContent={DrawerContent}
screenOptions={{headerShown:false}}
>
to this
<Drawer.Navigator initialRouteName="Categories"
drawerContent={(props)=> <DrawerContent {...props}/>} // here
screenOptions={{headerShown:false}}
>
demo snack

React Native Remove Item and button

I want when I click button inside access file remove the same item in months array inside Home file.
How can I solve this?
App.js
this file conncet screen.
import {StyleSheet, View} from 'react-native';
import {createStackNavigator} from '#react-navigation/stack';
import Home from './src/components/Home';
import {NavigationContainer} from '#react-navigation/native';
import Access from './src/components/access';
const Stack = createStackNavigator();
const App = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen name="Home" component={Home} />
<Stack.Screen name="access" component={Access} />
</Stack.Navigator>
</NavigationContainer>
);
};
export default App;
ButtonCicle.js
this file make circle.
import { View, Text,TouchableOpacity } from 'react-native';
import styles from './Styles/ButtonCircleStyles';
export default function ButtonCicle({BtnName, onPress}) {
return (
<TouchableOpacity style={styles.buttonContainer} onPress={onPress}>
<Text style={styles.button}>{BtnName}</Text>
</TouchableOpacity>
);
}
ButtonCircleStyles.js
this file make style
import {StyleSheet, Dimensions} from 'react-native';
const height = Dimensions.get('window').height;
const width = Dimensions.get('window').width;
const styles = StyleSheet.create({
buttonContainer:
{
backgroundColor: 'red',
borderRadius: 50,
width: width / 5,
height: width / 5,
justifyContent: 'center',
marginStart: '4%',
marginBottom: '4%',
},
button:
{
fontSize: 25,
textAlign: 'center',
},
});
export default styles;
Home.js
this file make array and onPress to another screen
import React from 'react';
import {StyleSheet, View} from 'react-native';
import ButtonCicle from './ButtonCircle';
const Home = ({navigation}) => {
const months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
const listbutton = months.map((i, key) => (
<ButtonCicle
BtnName={i}
key={key}
onPress={() => navigation.navigate('access') || alert(i)}
/>
));
return (
<>
<View style={styles.container}>{listbutton}</View>
</>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#ecf0f1',
padding: 8,
marginTop: '10%',
flexDirection: 'row',
height: 300,
flexWrap: 'wrap',
},
});
export default Home;
access.js
import React from 'react'
import { View, Text, Button } from 'react-native'
export default function access({navigation}) {
return (
<View>
<Text>Home</Text>
<Button title='Click' onPress={() => navigation.navigate('Home')} />
</View>
)
}
I want when I click button inside access file remove the same item in months array inside Home file.
How can I solve this?
How create it ?
You can try with a callback. Define a function inside Home.js that remove the element from the array. Then you could pass this callback as a route param
navigation.navigate('access', {deleteItem: //your callback})
And in the access.js file you get this callback
const deleteItem = navigation.getParam('deleteItem');
And call it before navigate back to home
const onPress = () => {
deleteItem();
navigation.navigate('Home');
}
<Button title='Click' onPress={onPress} />

React Native Navigation Open Draw From Header Button

I'm trying to open a navigation draw from a stack navigation header button. The header button is showing up fine but when I click the button I am getting
undefined is not an object (evaluating '_this.props.navigate')
I can't seem to find a solid example of how to do this or if its even possible with react navigation.
import React, { Component } from 'react';
import { createStackNavigator, NavigationActions } from 'react-navigation'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { Draw } from './DrawNav.js'
export const RootStack = createStackNavigator (
{
DrawNav: {
screen: Draw,
navigationOptions: ({ navigation }) => ({
//Hide the shadow of the header
headerStyle: {
elevation:0,
shadowColor: 'transparent',
shadowRadius: 0,
shadowOffset: {
height: 0,
}
},
headerLeft: (
<View style={{marginLeft: 10}}>
<Icon
name="menu"
size={25}
color="#D4AF37"
onPress={() => this.props.navigation.openDrawer()}
/>
</View>
),
})
},
},
);
this.props is only used in a react class. I assume you're using react-navigation v2 then you should dispatch DrawerAction like below
import React, { Component } from 'react';
import { createStackNavigator, NavigationActions } from 'react-navigation'
import Icon from 'react-native-vector-icons/MaterialCommunityIcons';
import { DrawerActions } from 'react-navigation';
import { Draw } from './DrawNav.js'
export const RootStack = createStackNavigator (
{
DrawNav: {
screen: Draw,
navigationOptions: ({ navigation }) => ({
//Hide the shadow of the header
headerStyle: {
elevation:0,
shadowColor: 'transparent',
shadowRadius: 0,
shadowOffset: {
height: 0,
}
},
headerLeft: (
<View style={{marginLeft: 10}}>
<Icon
name="menu"
size={25}
color="#D4AF37"
onPress={() => navigation.dispatch(DrawerActions.openDrawer())}
/>
</View>
),
})
},
},
);

How to add stateless component to a touchable component

I am trying to add a stateless component to my button.
const button = ({onButtonPress, buttonText}) => {
return (
<TouchableHighlight
onPress={() => onButtonPress()}>
<ButtonContent text={buttonText}/>
</TouchableHighlight>
)
};
and get this error:
Warning: Stateless function components cannot be given refs (See ref "childRef"
in StatelessComponent created by TouchableHighlight).
Attempts to access this ref will fail.
I have read up on the issue but I am still new to javascript and RN and have not found a solution. Any help would be appreciated.
full code:
GlossaryButtonContent:
import React from 'react';
import {
View,
Text,
Image,
StyleSheet
} from 'react-native';
import Colours from '../constants/Colours';
import {
arrowForwardDark,
starDarkFill
} from '../assets/icons';
type Props = {
text: string,
showFavButton?: boolean
}
export default ({text, showFavButton} : Props) => {
return (
<View style={styles.container}>
{showFavButton &&
<Image
style={styles.star}
source={starDarkFill}/>}
<Text style={[styles.text, showFavButton && styles.favButton]}>
{showFavButton ? 'Favourites' : text}
</Text>
<Image
style={styles.image}
source={arrowForwardDark}
opacity={showFavButton ? .5 : 1}/>
</View>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
alignItems: 'center'
},
favButton: {
marginLeft: 10,
color: Colours.darkTextHalf
},
text: {
flex: 1,
paddingTop: 5,
marginLeft: 20,
fontFamily: 'Bariol-Bold',
fontSize: 24,
color: Colours.darkText
},
image: {
marginRight: 20
},
star: {
marginLeft: 10
}
});
GlossaryButton:
import React from 'react';
import {
TouchableHighlight,
StyleSheet
} from 'react-native';
import Colours from '../constants/Colours';
import ShadowedBox from './ShadowedBox';
import GlossaryButtonContent from './GlossaryButtonContent';
type Props = {
buttonText: string,
onButtonPress: Function,
rowID: number,
sectionID?: string,
showFavButton?: boolean
}
export default ({buttonText, onButtonPress, rowID, sectionID, showFavButton} : Props) => {
return (
<ShadowedBox
style={styles.container}
backColor={showFavButton && Colours.yellow}>
<TouchableHighlight
style={styles.button}
underlayColor={Colours.green}
onPress={() => onButtonPress(rowID, sectionID)}>
<GlossaryButtonContent
text={buttonText}
showFavButton={showFavButton}/>
</TouchableHighlight>
</ShadowedBox>
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
height: 60,
marginBottom: 10,
borderRadius: 5
},
button: {
flex: 1,
borderRadius: 5
}
});
Basically, Stateless components cannot have refs.
So, having a stateless component in the middle of the render tree will create a void in the ref chain, meaning you cannot access lower-down components.
So, the problem comes from trying to do this:
let Stateless = (props) => (
<div />
);
let Wrapper = React.createClass({
render() {
return <Stateless ref="stateeee" />
}
});
TouchableHighlight needs to give a ref to its child. And this triggers that warning.
Answer:
You can't actually make a stateless component a child of TouchableHighlight
Solution:
Use createClass or class to create the child of TouchableHighlight, that is GlossaryButtonContent.
See this github issue for more info and this one

Show DrawerLayoutAndroid via ToolbarAndroid using Navigator (React Native)

I'm new to react native and I'm doing a simple android app.
I added a DrawerLayoutAndroid that I can drag from the left of my screen. However I'd like to open it when I click on my menu icon in my ToolbarAndroid having a Navigator gave me the error
"undefined is not an object (evaluating 'this.refs['DRAWER']')"
Then I solved this mistake but I got another one being
"undefined is not an object (evaluating 'this.props.sidebarRef').
My code is this:
MyToolbar.js
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text
} from 'react-native';
var ToolbarAndroid = require('ToolbarAndroid');
class MyToolbar extends Component {
render() {
var navigator = this.props.navigator;
return (
<ToolbarAndroid
title={this.props.title}
navIcon={require('./icons/ic_menu_white_24dp.png')}
style = {styles.toolbar}
titleColor={'white'}
onIconClicked={this._onIconClicked}/>
);
}
_onIconClicked(){
this.props.sidebarRef.refs['DRAWER'].openDrawer();
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
toolbar: {
height: 56,
backgroundColor: '#08AE9E',
width: 370,
alignItems: 'center'
}
});
module.exports = MyToolbar;
OpenDrawerFromToolbar.js
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
Navigator,
TouchableHighlight,
TouchableOpacity,
DrawerLayoutAndroid,
ScrollView,
} from 'react-native';
var ToolbarAndroid = require('ToolbarAndroid');
var MyToolbar = require('./MyToolbar');
var MenuItem = require('./MenuItem');
class OpenDraweFromToolbar extends Component {
render() {
var navigationView = (
<View style={{flex: 1, backgroundColor: '#fff'}}>
<Text style={{margin: 10, fontSize: 15, textAlign: 'left'}}>I'm in the Drawer!</Text>
<MenuItem
title="Calendar"
selected={this.props.activeTab === 'Calendar'}
//onPress={this.onTabSelect.bind(this, 'schedule')}
icon={require('./icons/ic_today_black_24dp.png')}
//selectedIcon={scheduleIconSelected}
/>
<MenuItem
title="Offers"
selected={this.props.activeTab === 'Offers'}
//onPress={this.onTabSelect.bind(this, 'schedule')}
icon={require('./icons/ic_today_black_24dp.png')}
//selectedIcon={scheduleIconSelected}
/>
<MenuItem
title="Boats"
selected={this.props.activeTab === 'Boats'}
//onPress={this.onTabSelect.bind(this, 'schedule')}
icon={require('./icons/ic_directions_boat_black_24dp.png')}
//selectedIcon={scheduleIconSelected}
/>
<MenuItem
title="Profile"
selected={this.props.activeTab === 'Profile'}
//onPress={this.onTabSelect.bind(this, 'schedule')}
icon={require('./icons/ic_account_circle_black_24dp.png')}
//selectedIcon={scheduleIconSelected}
/>
</View>
);
return (
<DrawerLayoutAndroid
drawerWidth={300}
drawerPosition={DrawerLayoutAndroid.positions.Left}
renderNavigationView={() => navigationView}
ref={'DRAWER'}>
<MyToolbar style={styles.toolbar}
title={'Calendar'}
navigator={this.props.navigator}
sidebarRef={this}/>
<View style={{flex: 1, alignItems: 'center'}}>
<Text style={{margin: 10, fontSize: 15, textAlign: 'right'}}>Hello</Text>
<Text style={{margin: 10, fontSize: 15, textAlign: 'right'}}>World!</Text>
</View>
</DrawerLayoutAndroid>
);
}
gotoPersonPage() {
this.props.navigator.push({
id: 'PersonPage',
name: 'hola',
});
}
_setDrawer() {
this.refs['DRAWER'].openDrawer();
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
toolbar: {
height: 200,
backgroundColor: '#08AE9E',
width: 370,
alignItems: 'center'
}
});
module.exports = OpenDraweFromToolbar;
and calendarpage.js
'use strict';
import React, { Component } from 'react';
import {
StyleSheet,
View,
Text,
Navigator,
TouchableHighlight,
TouchableOpacity,
DrawerLayoutAndroid,
ScrollView,
MenuItem,
} from 'react-native';
var ToolbarAndroid = require('ToolbarAndroid');
var MyToolbar = require('./MyToolbar');
var OpenDrawerFromToolbar = require('./OpenDrawerFromToolbar');
class CalendarPage extends Component {
render() {
return (
<Navigator
initialRoute = {{ name: 'OpenDrawerFromToolbar', index: 0 }}
renderScene={this.renderScene.bind(this)}
configureScene={ () => { return Navigator.SceneConfigs.PushFromRight; }}
/>
);
}
renderScene(route, navigator) {
//_navigator = navigator;
return (
<OpenDrawerFromToolbar
route={route}
navigator={navigator}
//data={route.data}
/>
);
}
}
module.exports = CalendarPage;
Does someone know what should i try to solve this mistake? i checked this same forum and found similar answers but none of them worked for me.
Thanks.
you should pass drawerlayout opening method as props to toolbar like this:
sidebarRef={()=>this._setDrawer()}
And in your toolbar component you should call sidebarRef as props, which automatically call the drawerlayout opening method of previous OpenDraweFromToolbar.js like this:
onIconClicked={this.props.sidebarRef}
Finally your toolbar icon will be called. This might help you.

Resources