-
-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Show GitHub projects on new About tab, add Home screen content #53
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import { execSync } from 'child_process'; | ||
|
||
// copies files over from a sample app generated with `bin/belt.js` | ||
// See CONTRIBUTING.md for more info | ||
function run() { | ||
const appDir = process.argv[2]; | ||
|
||
if (!appDir || appDir.includes('builds')) { | ||
console.error('Please provide an app directory, relative to `builds`'); | ||
console.error('Usage: node bin/sync-from-app.js MyApp --dry-run'); | ||
process.exit(1); | ||
} | ||
|
||
const excludes = [ | ||
'node_modules', | ||
'.cache', | ||
'.expo', | ||
'.vscode', | ||
'assets', | ||
'.git', | ||
'.gitignore', | ||
]; | ||
|
||
const excludesStr = excludes.map((e) => `--exclude ${e}`).join(' '); | ||
|
||
// provide additional flags, eg. --dry-run | ||
const flags = `-avp ${process.argv[3] || ''}`; | ||
|
||
const command = `rsync ${flags} ${excludesStr} builds/${appDir}/ templates/boilerplate/`; | ||
console.log(command); | ||
execSync(command, { | ||
stdio: 'inherit', | ||
}); | ||
|
||
console.log( | ||
"\n\n🎉 Success! Ensure that all files have copied over correctly, remove any unwanted modifications (eg. app.json, package.json, etc), and manually remove any files that need to be deleted (these don't sync)", | ||
); | ||
} | ||
|
||
run(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,7 +30,7 @@ | |
"msw": "^2.2.14", | ||
"react": "18.2.0", | ||
"react-native": "0.74.5", | ||
"react-native-keyboard-aware-scrollview": "^2.1.0", | ||
"react-native-keyboard-aware-scroll-view": "^0.9.5", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Since we are adding There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good catch! scroll-view is actually what we want. It must have been a mistake to originally add scrollview. I removed |
||
"react-native-safe-area-context": "4.10.5", | ||
"react-native-screens": "3.31.1" | ||
}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { screen, userEvent } from '@testing-library/react-native'; | ||
|
||
import mock from 'src/test/mock'; | ||
import { renderApplication } from 'src/test/render'; | ||
import { GithubProjectsResponse } from 'src/util/api/api'; | ||
|
||
// Testing philosophy: | ||
// - Tests that render the entire application with `renderApplication` go in the | ||
// top level `src/__tests__` directory and are named with `mytest.integration.test.tsx`. | ||
// These are ideal for when you need to test flows that include navigation between screens | ||
// - Tests that render a single screen or component are colocated in | ||
// `__tests__/MyComponent.test.tsx`. These call `render` and are not able to | ||
// navigate between screens, since the Navigator is not mounted | ||
test('renders app, can navigate between screens', async () => { | ||
jest.useFakeTimers(); | ||
|
||
const mocks = [mockGitHubProjects()]; | ||
|
||
// load the app on the Home screen | ||
renderApplication({ mocks }); | ||
expect( | ||
await screen.findByRole('header', { name: /Welcome to your new app/ }), | ||
).toBeDefined(); | ||
|
||
// go to About tab | ||
await userEvent.press(screen.getByRole('button', { name: /About/ })); | ||
expect( | ||
await screen.findByRole('header', { name: 'Open Source' }), | ||
).toBeDefined(); | ||
|
||
// expect GitHub project loaded via API | ||
expect(await screen.findByText(/Belt is a CLI/)).toBeDefined(); | ||
}); | ||
|
||
// TODO: sample data, remove | ||
// creates a mock for a GET request to the GitHub projects API. | ||
// Pass this mock to `render` or `renderApplication` to register it with MSW. | ||
// Recommended to place these mocks in a central location like `src/test/mocks` | ||
function mockGitHubProjects() { | ||
return mock.get<GithubProjectsResponse, null>( | ||
'https://github-projects-api.vercel.app/api/projects', | ||
{ | ||
response: { | ||
projects: [ | ||
{ | ||
id: 635980144, | ||
name: 'belt', | ||
description: | ||
'Belt is a CLI for starting a new React Native Expo app and will even keep your pants secure as you continue development.', | ||
url: 'https://github.com/thoughtbot/belt', | ||
stars: 8, | ||
forks: 0, | ||
}, | ||
], | ||
}, | ||
}, | ||
); | ||
} |
This file was deleted.
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
import { useNavigation } from '@react-navigation/native'; | ||
import { ReactNode } from 'react'; | ||
import { StyleSheet, View } from 'react-native'; | ||
import { | ||
KeyboardAwareScrollView, | ||
KeyboardAwareScrollViewProps, | ||
} from 'react-native-keyboard-aware-scroll-view'; | ||
import { | ||
SafeAreaView, | ||
useSafeAreaInsets, | ||
} from 'react-native-safe-area-context'; | ||
|
||
type Props = KeyboardAwareScrollViewProps & { | ||
/** | ||
* If true (default), horizontal padding is added to the screen content | ||
*/ | ||
padHorizontal?: boolean; | ||
/** | ||
* If true, the screen will be scrollable. If false, the screen will not scroll. | ||
* Set to false if screen includes a scrollable component like a FlatList | ||
*/ | ||
scroll?: boolean; | ||
/** | ||
* If true, a safe area view is not added for the top of the screen, since it is | ||
* handled instead by React Navigation | ||
*/ | ||
hasHeader?: boolean; | ||
/** A React component to render fixed to the bottom of the screen. It is not | ||
* positioned absolutely and would show above a tab bar. If your screen does | ||
* not have a tab bar, set fixedBottomAddSafeArea to ensure a safe area view | ||
* is used on the bottom */ | ||
FixedBottomComponent?: ReactNode; | ||
fixedBottomAddSafeArea?: boolean; | ||
}; | ||
|
||
export default function Screen({ | ||
style, | ||
padHorizontal = true, | ||
scroll = true, | ||
testID, | ||
hasHeader = false, | ||
children, | ||
FixedBottomComponent, | ||
fixedBottomAddSafeArea = false, | ||
...props | ||
}: Props) { | ||
const navigation = useNavigation(); | ||
const insets = useSafeAreaInsets(); | ||
|
||
return ( | ||
<SafeAreaView | ||
style={styles.wrapper} | ||
edges={{ | ||
top: hasHeader ? 'off' : 'additive', | ||
bottom: 'off', | ||
}} | ||
> | ||
<View | ||
testID={testID} | ||
style={[ | ||
styles.contentContainer, | ||
padHorizontal && styles.horizontalPadding, | ||
style, | ||
]} | ||
> | ||
{scroll ? ( | ||
<KeyboardAwareScrollView | ||
keyboardShouldPersistTaps="handled" | ||
contentInsetAdjustmentBehavior="automatic" | ||
bounces | ||
onAccessibilityEscape={() => navigation.goBack()} | ||
testID={`${testID || 'Screen'}ScrollView`} | ||
showsVerticalScrollIndicator={false} | ||
{...props} | ||
> | ||
{children} | ||
</KeyboardAwareScrollView> | ||
) : ( | ||
children | ||
)} | ||
</View> | ||
{!!FixedBottomComponent && ( | ||
<View | ||
style={[ | ||
fixedBottomAddSafeArea && { | ||
paddingBottom: insets.bottom, | ||
}, | ||
]} | ||
> | ||
{FixedBottomComponent} | ||
</View> | ||
)} | ||
</SafeAreaView> | ||
); | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
wrapper: { | ||
flex: 1, | ||
}, | ||
contentContainer: { | ||
flex: 1, | ||
}, | ||
horizontalPadding: { | ||
paddingHorizontal: 20, | ||
}, | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { createNativeStackNavigator } from '@react-navigation/native-stack'; | ||
import React from 'react'; | ||
import AboutScreen from 'src/screens/AboutScreen/AboutScreen'; | ||
import { AboutTabParamList } from './navigatorTypes'; | ||
|
||
const About = createNativeStackNavigator<AboutTabParamList>(); | ||
|
||
export default function AboutStack() { | ||
return ( | ||
<About.Navigator screenOptions={{ headerShown: false }}> | ||
<About.Screen name="About" component={AboutScreen} /> | ||
</About.Navigator> | ||
); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you think of automating this process as part of a command? Are there parts of the rsync process that would be reliant on the specifics of each new feature?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call! I added a script
bin/sync-from-app.js
which syncs files back from the app into Belt, and I updated the CONTRIBUTING.md doc.