Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 10bda69

Browse files
vanGalileastevegalili
and
stevegalili
authoredJul 16, 2024··
chore: revamp the cookbook app (#1635)
* create a Jotai examples dir in cookbook with related utils, add state management recipes dir * add docs with examples * extract state from component to state, utils, simplify types and custom render func. * refactor: tweaks & cleanup * make cookbook app runnable * update yarn.lock file * fix TS issue and spelling * remove duplicate files and adjust testMatch in config to ensure no utils test run --------- Co-authored-by: stevegalili <steve.galili@mywheels.nl>
1 parent d31c05a commit 10bda69

29 files changed

+1022
-217
lines changed
 

‎examples/cookbook/App.tsx

-8
This file was deleted.

‎examples/cookbook/README.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
# RNTL Cookbook
22

3-
This example app gathers recipes from the [RNTL Cookbook](https://callstack.github.io/react-native-testing-library/cookbook).
3+
This example app gathers recipes from
4+
the [RNTL Cookbook](https://callstack.github.io/react-native-testing-library/cookbook).
45

5-
Each recipe described in the Cookbook should have a corresponding code example in this repo.
6+
Each recipe described in the Cookbook should have a corresponding code example screen in this repo.
67

78
Note:
8-
Since examples will showcase usage of different dependencies, the dependencies in `package.json` fill will grow much larger that in an normal React Native. This is fine 🐶☕️🔥.
9+
Since examples will showcase usage of different dependencies, the dependencies in `package.json`
10+
file will grow much larger that in a normal React Native. This is fine 🐶☕️🔥.

‎examples/cookbook/custom-render/WelcomeScreen.test.tsx ‎examples/cookbook/__tests__/app/custom-render/index.test.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import { screen } from '@testing-library/react-native';
33
import { renderWithProviders } from './test-utils';
4-
import { WelcomeScreen } from './WelcomeScreen';
4+
import WelcomeScreen from "../../../app/custom-render";
55

66
test('renders WelcomeScreen in light theme', () => {
77
renderWithProviders(<WelcomeScreen />, { theme: 'light' });

‎examples/cookbook/custom-render/test-utils.tsx ‎examples/cookbook/__tests__/app/custom-render/test-utils.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as React from 'react';
22
import { render } from '@testing-library/react-native';
3-
import { User, UserProvider } from './providers/user-provider';
4-
import { Theme, ThemeProvider } from './providers/theme-provider';
3+
import { User, UserProvider } from '../../../app/custom-render/providers/user-provider';
4+
import { Theme, ThemeProvider } from '../../../app/custom-render/providers/theme-provider';
55

66
interface RenderWithProvidersProps {
77
user?: User | null;

‎examples/cookbook/jotai/TaskList.test.tsx ‎examples/cookbook/__tests__/app/jotai/index.test.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import * as React from 'react';
22
import { render, screen, userEvent } from '@testing-library/react-native';
3+
import TaskList from '../../../app/jotai';
34
import { renderWithAtoms } from './test-utils';
4-
import { TaskList } from './TaskList';
5-
import { addTask, getAllTasks, newTaskTitleAtom, store, tasksAtom } from './state';
6-
import { Task } from './types';
5+
import { Task } from '../../../app/jotai/types';
6+
import { addTask, getAllTasks, newTaskTitleAtom, store, tasksAtom } from '../../../app/jotai/state';
77

88
jest.useFakeTimers();
99

‎examples/cookbook/app.json

+19-4
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,22 @@
22
"expo": {
33
"name": "RNTL Cookbook App",
44
"slug": "rntl-cookbook",
5+
"scheme": "rntlcookbook",
56
"version": "1.0.0",
67
"orientation": "portrait",
78
"icon": "./assets/icon.png",
89
"userInterfaceStyle": "light",
910
"splash": {
1011
"image": "./assets/splash.png",
11-
"resizeMode": "contain",
12-
"backgroundColor": "#ffffff"
12+
"resizeMode": "cover",
13+
"backgroundColor": "#FFFFFF"
1314
},
1415
"updates": {
1516
"fallbackToCacheTimeout": 0
1617
},
17-
"assetBundlePatterns": ["**/*"],
18+
"assetBundlePatterns": [
19+
"**/*"
20+
],
1821
"ios": {
1922
"supportsTablet": true
2023
},
@@ -26,6 +29,18 @@
2629
},
2730
"web": {
2831
"favicon": "./assets/favicon.png"
29-
}
32+
},
33+
"plugins": [
34+
"expo-router",
35+
[
36+
"expo-font",
37+
{
38+
"fonts": [
39+
"./assets/fonts/OpenSans-Regular.ttf",
40+
"./assets/fonts/OpenSans-Bold.ttf"
41+
]
42+
}
43+
]
44+
]
3045
}
3146
}

‎examples/cookbook/app/_layout.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { Stack } from 'expo-router';
2+
import theme from '../theme';
3+
4+
export default function RootLayout() {
5+
return (
6+
<Stack
7+
screenOptions={{
8+
headerStyle: {
9+
backgroundColor: theme.colors.primary,
10+
},
11+
headerTintColor: '#fff',
12+
headerTitleStyle: {
13+
fontWeight: 'bold',
14+
},
15+
headerBackTitleVisible: false,
16+
}}
17+
>
18+
<Stack.Screen name="index" options={{ headerTitle: '' }} />
19+
</Stack>
20+
);
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { Slot } from 'expo-router';
2+
import { UserProvider } from './providers/user-provider';
3+
import { ThemeProvider } from './providers/theme-provider';
4+
5+
export default function CustomRenderLayout() {
6+
return (
7+
<UserProvider.Provider value={null}>
8+
<ThemeProvider.Provider value={'light'}>
9+
<Slot />
10+
</ThemeProvider.Provider>
11+
</UserProvider.Provider>
12+
);
13+
}

‎examples/cookbook/custom-render/WelcomeScreen.tsx ‎examples/cookbook/app/custom-render/index.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
import * as React from 'react';
2-
import { View, Text } from 'react-native';
2+
import { Text, View } from 'react-native';
33
import { useUser } from './providers/user-provider';
44
import { useTheme } from './providers/theme-provider';
55

6-
export function WelcomeScreen() {
6+
export default function WelcomeScreen() {
77
const theme = useTheme();
88
const user = useUser();
99

1010
return (
11-
<View>
11+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
1212
<Text>Hello {user ? user.name : 'Stranger'}</Text>
1313
<Text>Theme: {theme}</Text>
1414
</View>

‎examples/cookbook/app/index.tsx

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import React, {useCallback, useEffect} from 'react';
2+
import { FlatList, Image, Pressable, StyleSheet, Text, View } from 'react-native';
3+
import { useRouter } from 'expo-router';
4+
import * as SplashScreen from 'expo-splash-screen';
5+
import { useFonts } from 'expo-font';
6+
import theme from '../theme';
7+
8+
void SplashScreen.preventAutoHideAsync();
9+
10+
export default function Home() {
11+
const router = useRouter();
12+
const [loaded, error] = useFonts({
13+
'OpenSans-Bold': require('../assets/fonts/OpenSans-Bold.ttf'),
14+
'OpenSans-Regular': require('../assets/fonts/OpenSans-Regular.ttf'),
15+
});
16+
17+
useEffect(() => {
18+
if (loaded || error) {
19+
void SplashScreen.hideAsync();
20+
}
21+
}, [loaded, error]);
22+
23+
if (!loaded && !error) {
24+
return null;
25+
}
26+
const renderItem = useCallback(({ item }: {item: Recipe}) => (
27+
<Pressable style={styles.pressable} onPress={() => router.push(item.path)}>
28+
<Text style={styles.pressableText}>{item.title}</Text>
29+
</Pressable>
30+
),[]);
31+
32+
return (
33+
<View style={styles.container}>
34+
<View style={styles.bannerContainer}>
35+
<Image source={require('../assets/icon.png')} style={styles.logo} />
36+
<Image
37+
resizeMode={'contain'}
38+
source={require('../assets/gradientRNBanner.png')}
39+
style={styles.banner}
40+
/>
41+
<Text style={styles.title}>Testing Library</Text>
42+
<Text style={styles.subTitle}>Cookbook App</Text>
43+
</View>
44+
<FlatList<Recipe>
45+
data={recipes}
46+
renderItem={renderItem}
47+
keyExtractor={(item) => item.id.toString()}
48+
/>
49+
</View>
50+
);
51+
}
52+
53+
const styles = StyleSheet.create({
54+
container: {
55+
flex: 1,
56+
justifyContent: 'center',
57+
alignItems: 'center',
58+
backgroundColor: '#F5F5F5',
59+
paddingVertical: 20,
60+
},
61+
bannerContainer: {
62+
alignItems: 'center',
63+
marginBottom: 20,
64+
},
65+
title: {
66+
fontSize: 20,
67+
fontFamily: 'OpenSans-Bold',
68+
color: theme.colors.black,
69+
},
70+
subTitle: {
71+
fontSize: 14,
72+
fontFamily: 'OpenSans-Regular',
73+
color: theme.colors.gray,
74+
},
75+
banner: {
76+
height: 40,
77+
},
78+
logo: {
79+
width: 80,
80+
height: 80,
81+
marginBottom: 20,
82+
},
83+
pressable: {
84+
backgroundColor: '#9b6dff',
85+
padding: 12,
86+
marginBottom: 8,
87+
borderRadius: 16,
88+
},
89+
pressableText: {
90+
color: '#fff',
91+
fontSize: 14,
92+
fontFamily: 'OpenSans-Bold',
93+
textAlign: 'center',
94+
},
95+
});
96+
97+
type Recipe = {
98+
id: number;
99+
title: string;
100+
path: string;
101+
};
102+
const recipes: Recipe[] = [
103+
{ id: 2, title: 'Welcome Screen with Custom Render', path: 'custom-render/' },
104+
{ id: 1, title: 'Task List with Jotai', path: 'jotai/' },
105+
];

‎examples/cookbook/jotai/TaskList.tsx ‎examples/cookbook/app/jotai/index.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1+
import 'react-native-get-random-values';
2+
import { nanoid } from 'nanoid';
13
import * as React from 'react';
24
import { Pressable, Text, TextInput, View } from 'react-native';
35
import { useAtom } from 'jotai';
4-
import { nanoid } from 'nanoid';
56
import { newTaskTitleAtom, tasksAtom } from './state';
67

7-
export function TaskList() {
8+
export default function TaskList() {
89
const [tasks, setTasks] = useAtom(tasksAtom);
910
const [newTaskTitle, setNewTaskTitle] = useAtom(newTaskTitleAtom);
1011

@@ -20,7 +21,7 @@ export function TaskList() {
2021
};
2122

2223
return (
23-
<View>
24+
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
2425
{tasks.map((task) => (
2526
<Text key={task.id} testID="task-item">
2627
{task.title}
File renamed without changes.
File renamed without changes.
499 KB
Loading

‎examples/cookbook/assets/favicon.png

1.82 KB
Loading
Binary file not shown.
Binary file not shown.
62.9 KB
Loading

‎examples/cookbook/assets/icon.png

494 KB
Loading

‎examples/cookbook/assets/splash.png

150 KB
Loading

‎examples/cookbook/jest.config.js

+1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,5 @@ module.exports = {
22
preset: '@testing-library/react-native',
33
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
44
setupFilesAfterEnv: ['./jest-setup.ts'],
5+
testMatch: ['**/*.test.{ts,tsx}'],
56
};

‎examples/cookbook/package.json

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"main": "node_modules/expo/AppEntry.js",
2+
"main": "expo-router/entry",
33
"scripts": {
44
"start": "expo start",
55
"android": "expo start --android",
@@ -12,20 +12,29 @@
1212
},
1313
"dependencies": {
1414
"expo": "^50.0.4",
15+
"expo-constants": "~15.4.6",
16+
"expo-font": "~11.10.3",
17+
"expo-linking": "~6.2.2",
18+
"expo-router": "~3.4.10",
1519
"expo-status-bar": "~1.11.1",
1620
"jotai": "^2.8.4",
1721
"nanoid": "^3.3.7",
1822
"react": "18.2.0",
1923
"react-dom": "18.2.0",
2024
"react-native": "0.73.2",
25+
"react-native-get-random-values": "~1.8.0",
26+
"react-native-safe-area-context": "4.8.2",
27+
"react-native-screens": "~3.29.0",
2128
"react-native-web": "~0.19.6"
2229
},
2330
"devDependencies": {
2431
"@babel/core": "^7.20.0",
32+
"@expo/metro-runtime": "~3.1.3",
2533
"@testing-library/react-native": "^12.4.0",
2634
"@types/eslint": "^8.56.10",
2735
"@types/jest": "^29.5.12",
2836
"@types/react": "~18.2.45",
37+
"@types/react-native-get-random-values": "^1",
2938
"eslint": "^8.57.0",
3039
"jest": "^29.7.0",
3140
"react-test-renderer": "18.2.0",

‎examples/cookbook/theme.ts

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export default {
2+
colors: {
3+
primary: '#9b6dff',
4+
secondary: '#58baad',
5+
black: '#323232',
6+
gray: '#5b5a5b',
7+
},
8+
};

‎examples/cookbook/yarn.lock

+821-184
Large diffs are not rendered by default.

‎website/docs/12.x/cookbook/basics/custom-render.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export function renderWithProviders<T>(
2626
}
2727
```
2828

29-
```tsx title=WelcomeScreen.test.tsx
29+
```tsx title=custom-render/index.test.tsx
3030
import { screen } from '@testing-library/react-native';
3131
import { renderWithProviders } from '../test-utils';
3232
// ...

‎website/docs/12.x/cookbook/state-management/jotai.md

+5-4
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ the developer experience.
1212
Let's assume we have a simple task list component that uses Jotai for state management. The
1313
component has a list of tasks, a text input for typing new task name and a button to add a new task to the list.
1414

15-
```tsx title=TaskList.tsx
15+
```tsx title=jotai/index.test.tsx
1616
import * as React from 'react';
1717
import { Pressable, Text, TextInput, View } from 'react-native';
1818
import { useAtom } from 'jotai';
@@ -65,7 +65,7 @@ We can test our `TaskList` component using React Native Testing Library's (RNTL)
6565
function. Although it is sufficient to test the empty state of the `TaskList` component, it is not
6666
enough to test the component with initial tasks present in the list.
6767

68-
```tsx title=TaskList.test.tsx
68+
```tsx title=jotai/index.test.tsx
6969
import * as React from 'react';
7070
import { render, screen, userEvent } from '@testing-library/react-native';
7171
import { renderWithAtoms } from './test-utils';
@@ -144,7 +144,8 @@ We can now use the `renderWithAtoms` function to render the `TaskList` component
144144
In our test, we populated only one atom and its initial value, but you can add other Jotai atoms and their corresponding values to the initialValues array as needed.
145145
:::
146146

147-
```tsx title=TaskList.test.tsx
147+
```tsx title=jotai/index.test.tsx
148+
=======
148149
const INITIAL_TASKS: Task[] = [{ id: '1', title: 'Buy bread' }];
149150

150151
test('renders a to do list with 1 items initially, and adds a new item', async () => {
@@ -200,7 +201,7 @@ the initial to-do items in the store and then checking if the functions work as
200201
No special setup is required to test these functions, as `store.set` is available by default by
201202
Jotai.
202203

203-
```tsx title=TaskList.test.tsx
204+
```tsx title=jotai/index.test.tsx
204205
import { addTask, getAllTasks, store, tasksAtom } from './state';
205206

206207
//...

0 commit comments

Comments
 (0)
Please sign in to comment.