Skip to content

Commit 9db2302

Browse files
alexeulerjustin808
authored andcommitted
React Native Tutorial (shakacode#346)
* added react native * android sdk update * basic structure * added basic router * better logging * some styling and navigation * added api calls * api posting * added list view styling * sorting * added spinner * pull to refresh * button styling * allowed react rails for http calls * eslint * colors * flow * android support * refactored hocs into containers * added component tests * sagas first tests * replaced sagas with thunks * replaced thunks with effects * added reducers tests * added hocs tests * created common bundle * added selectors tests * flow * moved dispatch out of try * migrated to local npm * update for compatibility with thunk * external lib redux-thunk-effects * updated name, icon, bundle id, launch screen * removed outdated code * ios release * android app icon + https * android release * iOS build 2 * eslint additions * fixes for review * returned thunk * added readme.md * review comments * first docs * images fix * redux doc * Selectors docs
1 parent 175dbb3 commit 9db2302

File tree

127 files changed

+4393
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

127 files changed

+4393
-0
lines changed

mobile/ReactNativeTutorial/.babelrc

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"presets": ["react-native"]
3+
}
+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
[android]
3+
target = Google Inc.:Google APIs:23
4+
5+
[maven_repositories]
6+
central = https://repo1.maven.org/maven2
+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
ios
2+
android
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
---
2+
extends:
3+
- "eslint-config-shakacode"
4+
- "plugin:lodash-fp/recommended"
5+
- "plugin:flowtype/recommended"
6+
plugins:
7+
- "react-native"
8+
- "flowtype"
9+
- "babel"
10+
- "lodash-fp"
11+
12+
env:
13+
node: true
14+
settings:
15+
import/resolver:
16+
node:
17+
extensions:
18+
- ".js"
19+
- ".android.js"
20+
- ".ios.js"
21+
moduleDirectory:
22+
- "."
23+
- "node_modules"
24+
flowtype:
25+
onlyFilesWithFlowAnnotation: true
26+
globals:
27+
__DEV__: true
28+
fetch: true
29+
rules:
30+
new-cap: 0
31+
react/sort-comp: 2
32+
no-console: 2
33+
34+
babel/no-await-in-loop: 1
35+
36+
generator-star-spacing: 0
37+
38+
react-native/no-unused-styles: 2
39+
react-native/split-platform-components: 2
40+
react-native/no-inline-styles: 2
41+
react-native/no-color-literals: 2
42+
43+
react/jsx-filename-extension:
44+
- 1
45+
- extensions: [".js", ".jsx"]
+67
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
[ignore]
2+
3+
# We fork some components by platform.
4+
.*/*[.]android.js
5+
6+
# Ignore templates with `@flow` in header
7+
.*/local-cli/generator.*
8+
9+
# Ignore malformed json
10+
.*/node_modules/y18n/test/.*\.json
11+
12+
# Ignore the website subdir
13+
<PROJECT_ROOT>/website/.*
14+
15+
# Ignore BUCK generated dirs
16+
<PROJECT_ROOT>/\.buckd/
17+
18+
# Ignore unexpected extra @providesModule
19+
.*/node_modules/commoner/test/source/widget/share.js
20+
21+
# Ignore duplicate module providers
22+
# For RN Apps installed via npm, "Libraries" folder is inside node_modules/react-native but in the source repo it is in the root
23+
.*/Libraries/react-native/React.js
24+
.*/Libraries/react-native/ReactNative.js
25+
.*/node_modules/jest-runtime/build/__tests__/.*
26+
.*/node_modules/react/node_modules/.*
27+
.*/node_modules/react-native-experimental-navigation/.*
28+
.*/node_modules/react-native/Libraries/Components/StaticContainer.js
29+
30+
# Json lint doesn't pass flow
31+
.*/node_modules/jsonlint/.*
32+
33+
[include]
34+
35+
[libs]
36+
node_modules/react-native/Libraries/react-native/react-native-interface.js
37+
node_modules/react-native/flow
38+
flow/
39+
40+
[options]
41+
module.system=haste
42+
43+
esproposal.class_static_fields=enable
44+
esproposal.class_instance_fields=enable
45+
46+
experimental.strict_type_args=true
47+
48+
munge_underscores=true
49+
50+
module.name_mapper='^image![a-zA-Z0-9$_-]+$' -> 'GlobalImageStub'
51+
module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
52+
53+
suppress_type=$FlowIssue
54+
suppress_type=$FlowFixMe
55+
suppress_type=$FixMe
56+
57+
suppress_type=$FlowIgnore
58+
suppress_comment=\\(.\\|\n\\)*\\$FlowIgnore
59+
60+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-2]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
61+
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-2]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
62+
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
63+
64+
unsafe.enable_getters_and_setters=true
65+
66+
[version]
67+
^0.33.0

mobile/ReactNativeTutorial/.gitignore

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# OSX
2+
#
3+
.DS_Store
4+
5+
# Xcode
6+
#
7+
build/
8+
*.pbxuser
9+
!default.pbxuser
10+
*.mode1v3
11+
!default.mode1v3
12+
*.mode2v3
13+
!default.mode2v3
14+
*.perspectivev3
15+
!default.perspectivev3
16+
xcuserdata
17+
*.xccheckout
18+
*.moved-aside
19+
DerivedData
20+
*.hmap
21+
*.ipa
22+
*.xcuserstate
23+
project.xcworkspace
24+
25+
# Android/IJ
26+
#
27+
*.iml
28+
.idea
29+
.gradle
30+
local.properties
31+
32+
# node.js
33+
#
34+
node_modules/
35+
npm-debug.log
36+
37+
# BUCK
38+
buck-out/
39+
\.buckd/
40+
android/app/libs
41+
android/keystores/debug.keystore

mobile/ReactNativeTutorial/.jest.js

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Mocking the global.fetch included in React Native
2+
global.fetch = jest.fn();
3+
4+
// Helper to mock a success response (only once)
5+
fetch.mockResponseSuccess = (body) => {
6+
fetch.mockImplementationOnce (
7+
() => Promise.resolve({
8+
json: () => Promise.resolve(body),
9+
text: () => Promise.resolve(JSON.stringify(body)),
10+
})
11+
);
12+
};
13+
14+
// Helper to mock a failure response (only once)
15+
fetch.mockResponseFailure = (error) => {
16+
fetch.mockImplementationOnce(
17+
() => Promise.reject(error)
18+
);
19+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{}

mobile/ReactNativeTutorial/Readme.md

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
## React Native Tutorial
2+
This is a simple mobile app for posting comments in React Native.
3+
This tutorial shows how to connect to the the https://www.reactrails.com API for a sample microblog.
4+
5+
### Setup
6+
1. Install the latest version of Xcode from AppStore or https://developer.apple.com/download/ (Apple ID required)
7+
2. Install the latest version of Android Studio from https://developer.android.com/studio/index.html
8+
3. Install nvm (Node Version Manager)
9+
10+
```
11+
curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.32.1/install.sh | bash
12+
```
13+
14+
4. Install NodeJS stable
15+
16+
```
17+
nvm install node
18+
```
19+
20+
5. Install React Native and recommended packages
21+
22+
```
23+
npm install -g react-native-cli
24+
brew install watchman
25+
brew install flow
26+
```
27+
28+
6. Install npm dependencies
29+
30+
```
31+
npm i
32+
```
33+
34+
### Backend API
35+
36+
* Currently connecting by default to https://www.reactrails.com/. Be aware of that!
37+
* The url can be changed app/api/index.js. Keep in mind, that Android emulator is
38+
a separate Virtual Machine with its own localhost binding. To make the api available under emulator,
39+
you have to use ip address of your computer, that could be seen by running `ifconfig` in the shell
40+
41+
### Running IOS
42+
```
43+
react-native run-ios
44+
```
45+
46+
### Running Android
47+
1. Check that installed build tools match gradle config of android project:
48+
- In gradle config (app > android > build.gradle), search `buildToolsVersion`
49+
- Run `android sdk` from bash and find installed build tools version there
50+
2. Run emulator from Android studio or `emulator @<version>` from bash (you can find installed version by running `emulator -list-avds` from bash)
51+
3. From project folder run
52+
```
53+
react-native run-android
54+
```
55+
56+
### Testing
57+
Testing framework uses mocha + enzyme, to run tests type
58+
```
59+
npm test
60+
```
61+
62+
### Linters
63+
This projects uses Eslint with React and React Native rules. To run linters type
64+
```
65+
npm run lint
66+
```
67+
68+
69+
### Flow
70+
This projects uses Eslint with React and React Native rules. To run linters type
71+
```
72+
npm run flow
73+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import React from 'react';
2+
import _ from 'lodash/fp';
3+
4+
export default (props) => React.createElement(
5+
'Mock',
6+
_.omit('store', props),
7+
props.children,
8+
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
class MockCall {
2+
constructor() {
3+
this.queue = [];
4+
}
5+
6+
setMocks(mocks) {
7+
this.queue = mocks;
8+
}
9+
10+
getNextMock() {
11+
if (!this.queue || !this.queue.length) return undefined;
12+
return this.queue.shift();
13+
}
14+
15+
reset() {
16+
this.queue = [];
17+
}
18+
}
19+
20+
const mockCall = new MockCall();
21+
22+
// Mocks calls inside a function under test. This function takes several args and
23+
// stubs the return from call in order of occurence. If no mock were specified
24+
// it returns underfined
25+
export const mockCalls = (...args) => mockCall.setMocks(args);
26+
27+
// Clears all mocks for calls
28+
export const resetMockCalls = () => mockCall.reset();
29+
30+
// The mock call dispatches a fake action to the store with type: 'CALL' and
31+
// function name and args as parameters.
32+
const call = ({ dispatch }) => (f, ...args) => {
33+
dispatch({ type: 'CALL', name: f.name, args });
34+
const mock = mockCall.getNextMock();
35+
const result = typeof mock === 'function' ? mock() : mock;
36+
return f.then ? Promise.resolve(result) : result;
37+
};
38+
39+
export default call;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import call from './mockCall';
2+
3+
export const thunkMiddlewareCreator = (callEffect) =>
4+
({ dispatch, getState }) => next => action => {
5+
if (typeof action === 'function') {
6+
return action(dispatch, getState, callEffect({ dispatch }));
7+
}
8+
return next(action);
9+
};
10+
11+
export default thunkMiddlewareCreator(call);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import configureMockStore from 'redux-mock-store';
2+
import { initialState as reduxInitialState } from 'ReactNativeTutorial/app/reducers';
3+
4+
import mockThunkMiddleware from './mockThunkMiddleware';
5+
6+
export const createStoreFromState = configureMockStore([mockThunkMiddleware]);
7+
export const initialState = reduxInitialState;
8+
9+
export default () => createStoreFromState(initialState);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react';
2+
import Add from 'ReactNativeTutorial/app/bundles/comments/components/Add/Add';
3+
4+
import renderer from 'react-test-renderer';
5+
6+
const actions = {
7+
fetch: jest.fn(),
8+
updateForm: jest.fn(),
9+
createComment: jest.fn(),
10+
};
11+
12+
describe('Add', () => {
13+
it('renders correctly', () => {
14+
const props = {
15+
author: 'Alexey',
16+
text: 'Some random comment',
17+
actions,
18+
};
19+
const tree = renderer.create(
20+
<Add {...props} />
21+
);
22+
expect(tree).toMatchSnapshot();
23+
});
24+
});

0 commit comments

Comments
 (0)