diff --git a/src/cli.ts b/src/cli.ts index 4d53a25..ccb38a4 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -45,11 +45,6 @@ export default function runCli() { .description('Install and configure Jest and Testing Library') .action(buildAction(import('./commands/testingLibrary'))); - program - .command('navigation') - .description('Install and configure React Navigation') - .action(buildAction(import('./commands/navigation'))); - printWelcome(); program.parse(); } diff --git a/src/commands/__tests__/navigation.test.ts b/src/commands/__tests__/navigation.test.ts deleted file mode 100644 index f2715e7..0000000 --- a/src/commands/__tests__/navigation.test.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { vol } from 'memfs'; -import { expect, test, vi } from 'vitest'; -import addDependency from '../../util/addDependency'; -import copyTemplateDirectory from '../../util/copyTemplateDirectory'; -import addNavigation from '../navigation'; - -vi.mock('../../util/addDependency'); -vi.mock('../../util/copyTemplateDirectory'); - -test('installs React Navigation', async () => { - vol.fromJSON({ - 'package.json': JSON.stringify({ - scripts: {}, - dependencies: { - expo: '1.0.0', - }, - devDependencies: {}, - }), - 'yarn.lock': '', - }); - - await addNavigation(); - - expect(addDependency).toHaveBeenCalledWith( - '@react-navigation/native @react-navigation/native-stack', - ); - expect(copyTemplateDirectory).toHaveBeenCalledWith({ - templateDir: 'reactNavigation', - }); -}); diff --git a/src/commands/createApp.ts b/src/commands/createApp.ts index 5d6afb5..46aae54 100644 --- a/src/commands/createApp.ts +++ b/src/commands/createApp.ts @@ -27,7 +27,6 @@ export async function createApp( options: Options = {}, ) { const { interactive = true } = options; - globals.interactive = interactive; const appName = await validateAndSanitizeAppName(name); @@ -38,6 +37,7 @@ export async function createApp( const spinner = ora('Creating app with Belt').start(); await exec(`mkdir ${appName}`); + await copyTemplateDirectory({ templateDir: 'boilerplate', destinationDir: appName, @@ -59,7 +59,6 @@ export async function createApp( spinner.start('Installing dependencies'); const packageManager = getPackageManager(options); await exec(`${packageManager} install`); - await exec('git init'); await commit('Initial commit'); spinner.succeed('Installed dependencies'); diff --git a/src/commands/navigation.ts b/src/commands/navigation.ts deleted file mode 100644 index bf9bd8d..0000000 --- a/src/commands/navigation.ts +++ /dev/null @@ -1,30 +0,0 @@ -import ora from 'ora'; -import addDependency from '../util/addDependency'; -import copyTemplateDirectory from '../util/copyTemplateDirectory'; -import exec from '../util/exec'; -import isExpo from '../util/isExpo'; - -export default async function addNavigation() { - const spinner = ora().start('Installing React Navigation'); - const expo = await isExpo(); - - if (expo) { - await exec( - 'npx expo install react-native-screens react-native-safe-area-context', - ); - } else { - await addDependency('react-native-screens react-native-safe-area-context'); - } - - await addDependency( - '@react-navigation/native @react-navigation/native-stack', - ); - - await copyTemplateDirectory({ - templateDir: 'reactNavigation', - }); - - spinner.succeed( - 'Successfully installed React Navigation and Native Stack navigator', - ); -} diff --git a/templates/boilerplate/jest.setup.js b/templates/boilerplate/jest.setup.js index e1f30ff..7d448c7 100644 --- a/templates/boilerplate/jest.setup.js +++ b/templates/boilerplate/jest.setup.js @@ -7,6 +7,9 @@ beforeEach(() => { jest.clearAllMocks(); }); +jest.mock('expo-font'); +jest.mock('expo-asset'); + jest.mock('react-native-safe-area-context', () => mockSafeAreaContext); jest.mock('react-native/Libraries/Alert/Alert', () => ({ diff --git a/templates/boilerplate/package.json b/templates/boilerplate/package.json index 40af946..99103ba 100644 --- a/templates/boilerplate/package.json +++ b/templates/boilerplate/package.json @@ -18,7 +18,9 @@ "test:all": "npm run lint && npm run test:cov" }, "dependencies": { + "@expo/vector-icons": "^13.0.0", "@react-native-async-storage/async-storage": "1.21.0", + "@react-navigation/bottom-tabs": "^6.5.20", "@react-navigation/native": "^6.1.10", "@react-navigation/native-stack": "^6.9.18", "expo": "^50.0.14", diff --git a/templates/boilerplate/src/__tests__/App.test.tsx b/templates/boilerplate/src/__tests__/App.test.tsx index 7fc6b2d..0e388cb 100644 --- a/templates/boilerplate/src/__tests__/App.test.tsx +++ b/templates/boilerplate/src/__tests__/App.test.tsx @@ -3,6 +3,7 @@ import RootNavigator from 'src/navigators/RootNavigator'; import render from 'src/test/render'; test('renders', async () => { + jest.useFakeTimers(); render(); expect(await screen.findByText(/Open up App.tsx/)).toBeDefined(); }); diff --git a/templates/boilerplate/src/navigators/DashboardStack.tsx b/templates/boilerplate/src/navigators/DashboardStack.tsx new file mode 100644 index 0000000..610788b --- /dev/null +++ b/templates/boilerplate/src/navigators/DashboardStack.tsx @@ -0,0 +1,16 @@ +import { createNativeStackNavigator } from '@react-navigation/native-stack'; +import React from 'react'; +import HomeScreen from 'src/screens/HomeScreen/HomeScreen'; +import InformationScreen from '../screens/InformationScreen/InformationScreen'; +import { DashboardTabParamList } from './navigatorTypes'; + +const Dashboard = createNativeStackNavigator(); + +export default function DashboardStack() { + return ( + + + + + ); +} diff --git a/templates/boilerplate/src/navigators/RootNavigator.tsx b/templates/boilerplate/src/navigators/RootNavigator.tsx index 0602444..39b738b 100644 --- a/templates/boilerplate/src/navigators/RootNavigator.tsx +++ b/templates/boilerplate/src/navigators/RootNavigator.tsx @@ -1,12 +1,30 @@ -import { createNativeStackNavigator } from '@react-navigation/native-stack'; -import HomeScreen from 'src/screens/HomeScreen/HomeScreen'; +import { + NativeStackNavigationOptions, + createNativeStackNavigator, +} from '@react-navigation/native-stack'; +import TabNavigator from './TabNavigator'; + +const navigatorScreenOptions: NativeStackNavigationOptions = { + headerShadowVisible: false, +}; const Stack = createNativeStackNavigator(); export default function RootNavigator() { return ( - - + + + + {/* + screens that are navigable outside of tabs go here. This can include: + - authentication screens (only render tab navigator conditionally) + - screens that should not display bottom tab bar and that might be + navigated to from a screen from multiple tabs + */} ); } diff --git a/templates/boilerplate/src/navigators/TabNavigator.tsx b/templates/boilerplate/src/navigators/TabNavigator.tsx new file mode 100644 index 0000000..be54a60 --- /dev/null +++ b/templates/boilerplate/src/navigators/TabNavigator.tsx @@ -0,0 +1,50 @@ +import { MaterialCommunityIcons } from '@expo/vector-icons'; +import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; +import SettingsScreen from '../screens/SettingsScreen/SettingsScreen'; +import DashboardStack from './DashboardStack'; +import { TabsParamList } from './navigatorTypes'; + +const Tab = createBottomTabNavigator(); + +function HomeIcon({ focused = false, color = 'gray' }) { + return ; +} + +function AccountIcon({ focused = false, color = 'gray' }) { + return ( + + ); +} + +export default function TabNavigator() { + return ( + + + + + ); +} diff --git a/templates/boilerplate/src/navigators/navigatorTypes.tsx b/templates/boilerplate/src/navigators/navigatorTypes.tsx index 5695f70..cd97dbf 100644 --- a/templates/boilerplate/src/navigators/navigatorTypes.tsx +++ b/templates/boilerplate/src/navigators/navigatorTypes.tsx @@ -1,6 +1,40 @@ -// add types for navigation here -// key: screen name -// value: params (use undefined if accepts none) +import { NavigatorScreenParams } from '@react-navigation/native'; +import { NativeStackScreenProps } from '@react-navigation/native-stack'; + export type RootStackParamList = { + Tabs: NavigatorScreenParams; +}; + +export type TabsParamList = { + DashboardTab: NavigatorScreenParams; + SettingsTab: NavigatorScreenParams; +}; + +export type DashboardTabParamList = { Home: undefined; + Information: { owner: string } | undefined; +}; + +export type SettingsTabParamList = { + Settings: undefined; }; + +/* ---------------------------------------------------------------- + Derived types -- these should not need to be frequently modified + -------------------------------------------------------------*/ +export type TabName = keyof TabsParamList; +export type RootRouteName = keyof RootStackParamList; +export type AppRouteName = + | keyof RootStackParamList + | keyof DashboardTabParamList + | keyof SettingsTabParamList; + +export type HomeScreenProp = NativeStackScreenProps< + DashboardTabParamList, + 'Home' +>; + +export type InformationScreenProp = NativeStackScreenProps< + DashboardTabParamList, + 'Information' +>; diff --git a/templates/boilerplate/src/screens/HomeScreen/HomeScreen.tsx b/templates/boilerplate/src/screens/HomeScreen/HomeScreen.tsx index 8fde09e..ba298d5 100644 --- a/templates/boilerplate/src/screens/HomeScreen/HomeScreen.tsx +++ b/templates/boilerplate/src/screens/HomeScreen/HomeScreen.tsx @@ -1,10 +1,18 @@ +import { useNavigation } from '@react-navigation/native'; import { StatusBar } from 'expo-status-bar'; -import { StyleSheet, Text, View } from 'react-native'; +import { Button, StyleSheet, Text, View } from 'react-native'; +import { HomeScreenProp } from 'src/navigators/navigatorTypes'; export default function HomeScreen() { + const navigation = useNavigation(); + return ( Open up App.tsx to start working on your app! +