mirror of
https://github.com/zhigang1992/react-navigation.git
synced 2026-02-11 09:20:54 +08:00
Navigation Event Subscriptions (#3345)
* Add fbemitter, keep flow passing * Begin support for event emitter - Adds emitter to navigation prop - Emits top level onAction event - stub getChildEventSubscriber for child events * Support navigationState.isNavigating * Focus and blur events starting to work - Navigation completion action wired up - Event chaining logic built in getChildEventSubscriber - Renamed onAction evt to ‘action’ * Wrap up events progress and testing * Rename to isTransitioning and COMPLETE_TRANSITION * rm accidental dependency * Suppoert event payload type
This commit is contained in:
@@ -1,81 +0,0 @@
|
||||
[ignore]
|
||||
; We fork some components by platform
|
||||
.*/*[.]android.js
|
||||
|
||||
; Ignore "BUCK" generated dirs
|
||||
<PROJECT_ROOT>/\.buckd/
|
||||
|
||||
; Ignore unexpected extra "@providesModule"
|
||||
.*/node_modules/.*/node_modules/fbjs/.*
|
||||
|
||||
; Ignore duplicate module providers
|
||||
; For RN Apps installed via npm, "Libraries" folder is inside
|
||||
; "node_modules/react-native" but in the source repo it is in the root
|
||||
.*/Libraries/react-native/React.js
|
||||
|
||||
; Ignore polyfills
|
||||
.*/Libraries/polyfills/.*
|
||||
|
||||
.*/react-navigation/node_modules/.*
|
||||
|
||||
; Additional create-react-native-app ignores
|
||||
|
||||
; Ignore duplicate module providers
|
||||
.*/node_modules/fbemitter/lib/*
|
||||
|
||||
; Ignore misbehaving dev-dependencies
|
||||
.*/node_modules/xdl/build/*
|
||||
.*/node_modules/reqwest/tests/*
|
||||
|
||||
; Ignore missing expo-sdk dependencies (temporarily)
|
||||
; https://github.com/expo/expo/issues/162
|
||||
.*/node_modules/expo/src/*
|
||||
|
||||
; Ignore react-native-fbads dependency of the expo sdk
|
||||
.*/node_modules/react-native-fbads/*
|
||||
|
||||
.*/react-navigation/lib-rn/.*
|
||||
.*/react-navigation/lib/.*
|
||||
|
||||
.*/react-navigation/examples/ReduxExample/.*
|
||||
.*/react-navigation/website/.*
|
||||
|
||||
; This package is required by Expo and causes Flow errors
|
||||
.*/node_modules/react-native-gesture-handler/.*
|
||||
|
||||
[include]
|
||||
|
||||
[libs]
|
||||
node_modules/react-native/Libraries/react-native/react-native-interface.js
|
||||
node_modules/react-native/flow/
|
||||
|
||||
[options]
|
||||
module.system=haste
|
||||
|
||||
emoji=true
|
||||
|
||||
experimental.strict_type_args=true
|
||||
|
||||
munge_underscores=true
|
||||
|
||||
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'
|
||||
module.file_ext=.js
|
||||
module.file_ext=.jsx
|
||||
module.file_ext=.json
|
||||
module.file_ext=.native.js
|
||||
|
||||
suppress_type=$FlowIssue
|
||||
suppress_type=$FlowFixMe
|
||||
suppress_type=$FlowFixMeProps
|
||||
suppress_type=$FlowFixMeState
|
||||
suppress_type=$FixMe
|
||||
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(5[0-6]\\|[1-4][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
[version]
|
||||
^0.56.0
|
||||
@@ -7,38 +7,101 @@ import { Button, ScrollView, StatusBar } from 'react-native';
|
||||
import { StackNavigator, SafeAreaView } from 'react-navigation';
|
||||
import SampleText from './SampleText';
|
||||
|
||||
const MyNavScreen = ({ navigation, banner }) => (
|
||||
<SafeAreaView>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Profile', { name: 'Jane' })}
|
||||
title="Go to a profile screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Photos', { name: 'Jane' })}
|
||||
title="Go to a photos screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
class MyNavScreen extends React.Component {
|
||||
render() {
|
||||
const { navigation, banner } = this.props;
|
||||
return (
|
||||
<SafeAreaView>
|
||||
<SampleText>{banner}</SampleText>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Profile', { name: 'Jane' })}
|
||||
title="Go to a profile screen"
|
||||
/>
|
||||
<Button
|
||||
onPress={() => navigation.navigate('Photos', { name: 'Jane' })}
|
||||
title="Go to a photos screen"
|
||||
/>
|
||||
<Button onPress={() => navigation.goBack(null)} title="Go back" />
|
||||
<StatusBar barStyle="default" />
|
||||
</SafeAreaView>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const MyHomeScreen = ({ navigation }) => (
|
||||
<MyNavScreen banner="Home Screen" navigation={navigation} />
|
||||
);
|
||||
MyHomeScreen.navigationOptions = {
|
||||
title: 'Welcome',
|
||||
};
|
||||
class MyHomeScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Welcome',
|
||||
};
|
||||
componentDidMount() {
|
||||
this._s0 = this.props.navigation.addListener('willFocus', this._onWF);
|
||||
this._s1 = this.props.navigation.addListener('didFocus', this._onDF);
|
||||
this._s2 = this.props.navigation.addListener('willBlur', this._onWB);
|
||||
this._s3 = this.props.navigation.addListener('didBlur', this._onDB);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this._s0.remove();
|
||||
this._s1.remove();
|
||||
this._s2.remove();
|
||||
this._s3.remove();
|
||||
}
|
||||
_onWF = a => {
|
||||
console.log('_willFocus HomeScreen', a);
|
||||
};
|
||||
_onDF = a => {
|
||||
console.log('_didFocus HomeScreen', a);
|
||||
};
|
||||
_onWB = a => {
|
||||
console.log('_willBlur HomeScreen', a);
|
||||
};
|
||||
_onDB = a => {
|
||||
console.log('_didBlur HomeScreen', a);
|
||||
};
|
||||
|
||||
const MyPhotosScreen = ({ navigation }) => (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params.name}'s Photos`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
MyPhotosScreen.navigationOptions = {
|
||||
title: 'Photos',
|
||||
};
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return <MyNavScreen banner="Home Screen" navigation={navigation} />;
|
||||
}
|
||||
}
|
||||
|
||||
class MyPhotosScreen extends React.Component {
|
||||
static navigationOptions = {
|
||||
title: 'Photos',
|
||||
};
|
||||
componentDidMount() {
|
||||
this._s0 = this.props.navigation.addListener('willFocus', this._onWF);
|
||||
this._s1 = this.props.navigation.addListener('didFocus', this._onDF);
|
||||
this._s2 = this.props.navigation.addListener('willBlur', this._onWB);
|
||||
this._s3 = this.props.navigation.addListener('didBlur', this._onDB);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this._s0.remove();
|
||||
this._s1.remove();
|
||||
this._s2.remove();
|
||||
this._s3.remove();
|
||||
}
|
||||
_onWF = a => {
|
||||
console.log('_willFocus PhotosScreen', a);
|
||||
};
|
||||
_onDF = a => {
|
||||
console.log('_didFocus PhotosScreen', a);
|
||||
};
|
||||
_onWB = a => {
|
||||
console.log('_willBlur PhotosScreen', a);
|
||||
};
|
||||
_onDB = a => {
|
||||
console.log('_didBlur PhotosScreen', a);
|
||||
};
|
||||
|
||||
render() {
|
||||
const { navigation } = this.props;
|
||||
return (
|
||||
<MyNavScreen
|
||||
banner={`${navigation.state.params.name}'s Photos`}
|
||||
navigation={navigation}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const MyProfileScreen = ({ navigation }) => (
|
||||
<MyNavScreen
|
||||
|
||||
@@ -114,4 +114,35 @@ const SimpleTabs = TabNavigator(
|
||||
}
|
||||
);
|
||||
|
||||
export default SimpleTabs;
|
||||
class SimpleTabsContainer extends React.Component {
|
||||
static router = SimpleTabs.router;
|
||||
componentDidMount() {
|
||||
this._s0 = this.props.navigation.addListener('willFocus', this._onWF);
|
||||
this._s1 = this.props.navigation.addListener('didFocus', this._onDF);
|
||||
this._s2 = this.props.navigation.addListener('willBlur', this._onWB);
|
||||
this._s3 = this.props.navigation.addListener('didBlur', this._onDB);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this._s0.remove();
|
||||
this._s1.remove();
|
||||
this._s2.remove();
|
||||
this._s3.remove();
|
||||
}
|
||||
_onWF = a => {
|
||||
console.log('_onWillFocus tabsExample ', a);
|
||||
};
|
||||
_onDF = a => {
|
||||
console.log('_onDidFocus tabsExample ', a);
|
||||
};
|
||||
_onWB = a => {
|
||||
console.log('_onWillBlur tabsExample ', a);
|
||||
};
|
||||
_onDB = a => {
|
||||
console.log('_onDidBlur tabsExample ', a);
|
||||
};
|
||||
render() {
|
||||
return <SimpleTabs navigation={this.props.navigation} />;
|
||||
}
|
||||
}
|
||||
|
||||
export default SimpleTabsContainer;
|
||||
|
||||
@@ -2399,7 +2399,7 @@ glob@^7.0.3, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
global@^4.3.0:
|
||||
global@^4.3.0, global@^4.3.2:
|
||||
version "4.3.2"
|
||||
resolved "https://registry.yarnpkg.com/global/-/global-4.3.2.tgz#e76989268a6c74c38908b1305b10fc0e394e9d0f"
|
||||
dependencies:
|
||||
@@ -4690,15 +4690,8 @@ react-native@^0.51.0:
|
||||
yargs "^9.0.0"
|
||||
|
||||
"react-navigation@link:../..":
|
||||
version "1.0.0-beta.27"
|
||||
dependencies:
|
||||
babel-plugin-transform-define "^1.3.0"
|
||||
clamp "^1.0.1"
|
||||
hoist-non-react-statics "^2.2.0"
|
||||
path-to-regexp "^1.7.0"
|
||||
prop-types "^15.5.10"
|
||||
react-native-drawer-layout-polyfill "^1.3.2"
|
||||
react-native-tab-view "^0.0.74"
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
||||
react-proxy@^1.1.7:
|
||||
version "1.1.8"
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"test": "npm run lint && npm run jest",
|
||||
"codecov": "codecov",
|
||||
"jest": "jest",
|
||||
"test-update-snapshot": "jest --updateSnapshot",
|
||||
"lint": "eslint .",
|
||||
"format": "eslint --fix .",
|
||||
"precommit": "lint-staged"
|
||||
@@ -56,7 +57,7 @@
|
||||
"eslint-plugin-prettier": "^2.1.2",
|
||||
"eslint-plugin-react": "^7.1.0",
|
||||
"husky": "^0.14.3",
|
||||
"jest": "^20.0.4",
|
||||
"jest": "^22.1.3",
|
||||
"lint-staged": "^4.2.1",
|
||||
"prettier": "^1.5.3",
|
||||
"prettier-eslint": "^6.4.2",
|
||||
|
||||
4
packages/react-navigation/prettier.config.js
Normal file
4
packages/react-navigation/prettier.config.js
Normal file
@@ -0,0 +1,4 @@
|
||||
module.exports = {
|
||||
trailingComma: 'es5',
|
||||
singleQuote: true,
|
||||
};
|
||||
@@ -4,6 +4,7 @@ const NAVIGATE = 'Navigation/NAVIGATE';
|
||||
const RESET = 'Navigation/RESET';
|
||||
const SET_PARAMS = 'Navigation/SET_PARAMS';
|
||||
const URI = 'Navigation/URI';
|
||||
const COMPLETE_TRANSITION = 'Navigation/COMPLETE_TRANSITION';
|
||||
|
||||
const createAction = (type, fn) => {
|
||||
fn.toString = () => type;
|
||||
@@ -57,6 +58,10 @@ const uri = createAction(URI, payload => ({
|
||||
uri: payload.uri,
|
||||
}));
|
||||
|
||||
const completeTransition = createAction(COMPLETE_TRANSITION, payload => ({
|
||||
type: COMPLETE_TRANSITION,
|
||||
}));
|
||||
|
||||
const mapDeprecatedNavigateAction = action => {
|
||||
if (action.type === 'Navigate') {
|
||||
const payload = {
|
||||
@@ -118,6 +123,7 @@ export default {
|
||||
RESET,
|
||||
SET_PARAMS,
|
||||
URI,
|
||||
COMPLETE_TRANSITION,
|
||||
|
||||
// Action creators
|
||||
back,
|
||||
@@ -126,6 +132,7 @@ export default {
|
||||
reset,
|
||||
setParams,
|
||||
uri,
|
||||
completeTransition,
|
||||
|
||||
// TODO: Remove once old actions are deprecated
|
||||
mapDeprecatedActionAndWarn,
|
||||
|
||||
@@ -5,7 +5,11 @@ const routeName = 'Anything';
|
||||
describe('StateUtils', () => {
|
||||
// Getters
|
||||
it('gets route', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(NavigationStateUtils.get(state, 'a')).toEqual({
|
||||
key: 'a',
|
||||
routeName,
|
||||
@@ -17,6 +21,7 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(NavigationStateUtils.indexOf(state, 'a')).toBe(0);
|
||||
expect(NavigationStateUtils.indexOf(state, 'b')).toBe(1);
|
||||
@@ -27,6 +32,7 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(NavigationStateUtils.has(state, 'b')).toBe(true);
|
||||
expect(NavigationStateUtils.has(state, 'c')).toBe(false);
|
||||
@@ -34,9 +40,14 @@ describe('StateUtils', () => {
|
||||
|
||||
// Push
|
||||
it('pushes a route', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
};
|
||||
expect(NavigationStateUtils.push(state, { key: 'b', routeName })).toEqual(
|
||||
@@ -45,7 +56,11 @@ describe('StateUtils', () => {
|
||||
});
|
||||
|
||||
it('does not push duplicated route', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(() =>
|
||||
NavigationStateUtils.push(state, { key: 'a', routeName })
|
||||
).toThrow();
|
||||
@@ -56,13 +71,22 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = { index: 0, routes: [{ key: 'a', routeName }] };
|
||||
expect(NavigationStateUtils.pop(state)).toEqual(newState);
|
||||
});
|
||||
|
||||
it('does not pop route if not applicable', () => {
|
||||
const state = { index: 0, routes: [{ key: 'a', routeName }] };
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(NavigationStateUtils.pop(state)).toBe(state);
|
||||
});
|
||||
|
||||
@@ -71,10 +95,12 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(NavigationStateUtils.jumpToIndex(state, 0)).toBe(state);
|
||||
expect(NavigationStateUtils.jumpToIndex(state, 1)).toEqual(newState);
|
||||
@@ -84,6 +110,7 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(() => NavigationStateUtils.jumpToIndex(state, 2)).toThrow();
|
||||
});
|
||||
@@ -92,10 +119,12 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(NavigationStateUtils.jumpTo(state, 'a')).toBe(state);
|
||||
expect(NavigationStateUtils.jumpTo(state, 'b')).toEqual(newState);
|
||||
@@ -105,6 +134,7 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(() => NavigationStateUtils.jumpTo(state, 'c')).toThrow();
|
||||
});
|
||||
@@ -113,10 +143,12 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(NavigationStateUtils.back(state)).toEqual(newState);
|
||||
expect(NavigationStateUtils.back(newState)).toBe(newState);
|
||||
@@ -126,10 +158,12 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(NavigationStateUtils.forward(state)).toEqual(newState);
|
||||
expect(NavigationStateUtils.forward(newState)).toBe(newState);
|
||||
@@ -140,10 +174,12 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'c', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.replaceAt(state, 'b', { key: 'c', routeName })
|
||||
@@ -154,10 +190,12 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'a', routeName }, { key: 'c', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.replaceAtIndex(state, 1, { key: 'c', routeName })
|
||||
@@ -168,6 +206,7 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.replaceAtIndex(state, 1, state.routes[1])
|
||||
@@ -179,10 +218,12 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = {
|
||||
index: 1,
|
||||
routes: [{ key: 'x', routeName }, { key: 'y', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.reset(state, [
|
||||
@@ -200,10 +241,12 @@ describe('StateUtils', () => {
|
||||
const state = {
|
||||
index: 0,
|
||||
routes: [{ key: 'a', routeName }, { key: 'b', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
const newState = {
|
||||
index: 0,
|
||||
routes: [{ key: 'x', routeName }, { key: 'y', routeName }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(
|
||||
NavigationStateUtils.reset(
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
import NavigationActions from '../NavigationActions';
|
||||
import addNavigationHelpers from '../addNavigationHelpers';
|
||||
|
||||
const dummyEventSubscriber = (name: string, handler: (*) => void) => ({
|
||||
remove: () => {},
|
||||
});
|
||||
|
||||
describe('addNavigationHelpers', () => {
|
||||
it('handles Back action', () => {
|
||||
const mockedDispatch = jest
|
||||
@@ -10,6 +14,7 @@ describe('addNavigationHelpers', () => {
|
||||
addNavigationHelpers({
|
||||
state: { key: 'A', routeName: 'Home' },
|
||||
dispatch: mockedDispatch,
|
||||
addListener: dummyEventSubscriber,
|
||||
}).goBack('A')
|
||||
).toEqual(true);
|
||||
expect(mockedDispatch).toBeCalledWith({
|
||||
@@ -27,6 +32,7 @@ describe('addNavigationHelpers', () => {
|
||||
addNavigationHelpers({
|
||||
state: { routeName: 'Home' },
|
||||
dispatch: mockedDispatch,
|
||||
addListener: dummyEventSubscriber,
|
||||
}).goBack()
|
||||
).toEqual(true);
|
||||
expect(mockedDispatch).toBeCalledWith({ type: NavigationActions.BACK });
|
||||
@@ -41,6 +47,7 @@ describe('addNavigationHelpers', () => {
|
||||
addNavigationHelpers({
|
||||
state: { routeName: 'Home' },
|
||||
dispatch: mockedDispatch,
|
||||
addListener: dummyEventSubscriber,
|
||||
}).navigate('Profile', { name: 'Matt' })
|
||||
).toEqual(true);
|
||||
expect(mockedDispatch).toBeCalledWith({
|
||||
@@ -59,6 +66,7 @@ describe('addNavigationHelpers', () => {
|
||||
addNavigationHelpers({
|
||||
state: { key: 'B', routeName: 'Settings' },
|
||||
dispatch: mockedDispatch,
|
||||
addListener: dummyEventSubscriber,
|
||||
}).setParams({ notificationsEnabled: 'yes' })
|
||||
).toEqual(true);
|
||||
expect(mockedDispatch).toBeCalledWith({
|
||||
|
||||
@@ -17,6 +17,8 @@ export default function createNavigationContainer(Component) {
|
||||
static router = Component.router;
|
||||
static navigationOptions = null;
|
||||
|
||||
_actionEventSubscribers = new Set();
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
@@ -150,13 +152,27 @@ export default function createNavigationContainer(Component) {
|
||||
const oldNav = this._nav;
|
||||
invariant(oldNav, 'should be set in constructor if stateful');
|
||||
const nav = Component.router.getStateForAction(action, oldNav);
|
||||
const dispatchActionEvents = () => {
|
||||
this._actionEventSubscribers.forEach(subscriber =>
|
||||
// $FlowFixMe - Payload should probably understand generic state type
|
||||
subscriber({
|
||||
type: 'action',
|
||||
action,
|
||||
state: nav,
|
||||
lastState: oldNav,
|
||||
})
|
||||
);
|
||||
};
|
||||
if (nav && nav !== oldNav) {
|
||||
// Cache updates to state.nav during the tick to ensure that subsequent calls will not discard this change
|
||||
this._nav = nav;
|
||||
this.setState({ nav }, () =>
|
||||
this._onNavigationStateChange(oldNav, nav, action)
|
||||
);
|
||||
this.setState({ nav }, () => {
|
||||
this._onNavigationStateChange(oldNav, nav, action);
|
||||
dispatchActionEvents();
|
||||
});
|
||||
return true;
|
||||
} else {
|
||||
dispatchActionEvents();
|
||||
}
|
||||
return false;
|
||||
};
|
||||
@@ -170,6 +186,17 @@ export default function createNavigationContainer(Component) {
|
||||
this._navigation = addNavigationHelpers({
|
||||
dispatch: this.dispatch,
|
||||
state: nav,
|
||||
addListener: (eventName, handler) => {
|
||||
if (eventName !== 'action') {
|
||||
return { remove: () => {} };
|
||||
}
|
||||
this._actionEventSubscribers.add(handler);
|
||||
return {
|
||||
remove: () => {
|
||||
this._actionEventSubscribers.delete(handler);
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
}
|
||||
navigation = this._navigation;
|
||||
|
||||
154
packages/react-navigation/src/getChildEventSubscriber.js
vendored
Normal file
154
packages/react-navigation/src/getChildEventSubscriber.js
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
/**
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {
|
||||
NavigationEventSubscriber,
|
||||
NavigationAction,
|
||||
NavigationState,
|
||||
NavigationEventPayload,
|
||||
} from './TypeDefinition';
|
||||
|
||||
/*
|
||||
* This is used to extract one children's worth of events from a stream of navigation action events
|
||||
*
|
||||
* Based on the 'action' events that get fired for this navigation state, this utility will fire
|
||||
* focus and blur events for this child
|
||||
*/
|
||||
export default function getChildEventSubscriber(
|
||||
addListener: NavigationEventSubscriber,
|
||||
key: string
|
||||
): NavigationEventSubscriber {
|
||||
const actionSubscribers = new Set();
|
||||
const willFocusSubscribers = new Set();
|
||||
const didFocusSubscribers = new Set();
|
||||
const willBlurSubscribers = new Set();
|
||||
const didBlurSubscribers = new Set();
|
||||
|
||||
const getChildSubscribers = evtName => {
|
||||
switch (evtName) {
|
||||
case 'action':
|
||||
return actionSubscribers;
|
||||
case 'willFocus':
|
||||
return willFocusSubscribers;
|
||||
case 'didFocus':
|
||||
return didFocusSubscribers;
|
||||
case 'willBlur':
|
||||
return willBlurSubscribers;
|
||||
case 'didBlur':
|
||||
return didBlurSubscribers;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const emit = payload => {
|
||||
const subscribers = getChildSubscribers(payload.type);
|
||||
subscribers &&
|
||||
subscribers.forEach(subs => {
|
||||
// $FlowFixMe - Payload should probably understand generic state type
|
||||
subs(payload);
|
||||
});
|
||||
};
|
||||
|
||||
let isSelfFocused = false;
|
||||
|
||||
const cleanup = () => {
|
||||
upstreamSubscribers.forEach(subs => subs && subs.remove());
|
||||
};
|
||||
|
||||
const upstreamEvents = [
|
||||
'willFocus',
|
||||
'didFocus',
|
||||
'willBlur',
|
||||
'didBlur',
|
||||
'action',
|
||||
];
|
||||
|
||||
const upstreamSubscribers = upstreamEvents.map(eventName =>
|
||||
addListener(eventName, (payload: NavigationEventPayload) => {
|
||||
const { state, lastState, action } = payload;
|
||||
const lastFocusKey = lastState && lastState.routes[lastState.index].key;
|
||||
const focusKey = state && state.routes[state.index].key;
|
||||
|
||||
const isFocused = focusKey === key;
|
||||
const wasFocused = lastFocusKey === key;
|
||||
const lastRoute =
|
||||
lastState && lastState.routes.find(route => route.key === key);
|
||||
const newRoute = state && state.routes.find(route => route.key === key);
|
||||
const childPayload = {
|
||||
state: newRoute,
|
||||
lastState: lastRoute,
|
||||
action,
|
||||
type: eventName,
|
||||
};
|
||||
|
||||
const didNavigate =
|
||||
(lastState && lastState.isTransitioning) !==
|
||||
(state && state.isTransitioning);
|
||||
|
||||
const isTransitioning = !!state && state.isTransitioning;
|
||||
const wasTransitioning = !!lastState && lastState.isTransitioning;
|
||||
const didStartTransitioning = !wasTransitioning && isTransitioning;
|
||||
const didFinishTransitioning = wasTransitioning && !isTransitioning;
|
||||
|
||||
if (eventName !== 'action') {
|
||||
switch (eventName) {
|
||||
case 'didFocus':
|
||||
isSelfFocused = true;
|
||||
break;
|
||||
case 'didBlur':
|
||||
isSelfFocused = false;
|
||||
break;
|
||||
}
|
||||
emit(childPayload);
|
||||
return;
|
||||
}
|
||||
|
||||
// now we're exclusively handling the "action" event
|
||||
|
||||
if (newRoute) {
|
||||
// fire this event to pass navigation events to children subscribers
|
||||
emit(childPayload);
|
||||
}
|
||||
if (isFocused && didStartTransitioning && !isSelfFocused) {
|
||||
emit({
|
||||
...childPayload,
|
||||
type: 'willFocus',
|
||||
});
|
||||
}
|
||||
if (isFocused && didFinishTransitioning && !isSelfFocused) {
|
||||
emit({
|
||||
...childPayload,
|
||||
type: 'didFocus',
|
||||
});
|
||||
isSelfFocused = true;
|
||||
}
|
||||
if (!isFocused && didStartTransitioning && isSelfFocused) {
|
||||
emit({
|
||||
...childPayload,
|
||||
type: 'willBlur',
|
||||
});
|
||||
}
|
||||
if (!isFocused && didFinishTransitioning && isSelfFocused) {
|
||||
emit({
|
||||
...childPayload,
|
||||
type: 'didBlur',
|
||||
});
|
||||
isSelfFocused = false;
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return (eventName, eventHandler) => {
|
||||
const subscribers = getChildSubscribers(eventName);
|
||||
if (!subscribers) {
|
||||
throw new Error(`Invalid event name "${eventName}"`);
|
||||
}
|
||||
subscribers.add(eventHandler);
|
||||
const remove = () => {
|
||||
subscribers.delete(eventHandler);
|
||||
};
|
||||
return { remove };
|
||||
};
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import createNavigator from './createNavigator';
|
||||
import CardStackTransitioner from '../views/CardStack/CardStackTransitioner';
|
||||
import StackRouter from '../routers/StackRouter';
|
||||
import NavigatorTypes from './NavigatorTypes';
|
||||
import NavigationActions from '../NavigationActions';
|
||||
|
||||
// A stack navigators props are the intersection between
|
||||
// the base navigator props (navgiation, screenProps, etc)
|
||||
@@ -46,7 +47,11 @@ export default (routeConfigMap, stackConfig = {}) => {
|
||||
cardStyle={cardStyle}
|
||||
transitionConfig={transitionConfig}
|
||||
onTransitionStart={onTransitionStart}
|
||||
onTransitionEnd={onTransitionEnd}
|
||||
onTransitionEnd={(lastTransition, transition) => {
|
||||
const { state, dispatch } = props.navigation;
|
||||
dispatch(NavigationActions.completeTransition());
|
||||
onTransitionEnd && onTransitionEnd();
|
||||
}}
|
||||
/>
|
||||
));
|
||||
|
||||
|
||||
@@ -93,12 +93,14 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] =
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"dispatch": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
"isTransitioning": false,
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "Init-id-0-1",
|
||||
@@ -324,12 +326,14 @@ exports[`StackNavigator renders successfully 1`] = `
|
||||
mode="float"
|
||||
navigation={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"dispatch": [Function],
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
"setParams": [Function],
|
||||
"state": Object {
|
||||
"index": 0,
|
||||
"isTransitioning": false,
|
||||
"routes": Array [
|
||||
Object {
|
||||
"key": "Init-id-0-0",
|
||||
|
||||
@@ -87,6 +87,7 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
childRouters[action.routeName] !== undefined
|
||||
) {
|
||||
return {
|
||||
isTransitioning: false,
|
||||
index: 0,
|
||||
routes: [
|
||||
{
|
||||
@@ -120,6 +121,7 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
};
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
state = {
|
||||
isTransitioning: false,
|
||||
index: 0,
|
||||
routes: [route],
|
||||
};
|
||||
@@ -171,7 +173,20 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
routeName: action.routeName,
|
||||
};
|
||||
}
|
||||
return StateUtils.push(state, route);
|
||||
return {
|
||||
...StateUtils.push(state, route),
|
||||
isTransitioning: action.immediate !== true,
|
||||
};
|
||||
}
|
||||
|
||||
if (
|
||||
action.type === NavigationActions.COMPLETE_TRANSITION &&
|
||||
state.isTransitioning
|
||||
) {
|
||||
return {
|
||||
...state,
|
||||
isTransitioning: false,
|
||||
};
|
||||
}
|
||||
|
||||
// Handle navigation to other child routers that are not yet pushed
|
||||
@@ -257,19 +272,17 @@ export default (routeConfigs, stackConfig = {}) => {
|
||||
|
||||
if (action.type === NavigationActions.BACK) {
|
||||
const key = action.key;
|
||||
let backRouteIndex = null;
|
||||
let backRouteIndex = state.index;
|
||||
if (key) {
|
||||
const backRoute = state.routes.find(route => route.key === key);
|
||||
backRouteIndex = state.routes.indexOf(backRoute);
|
||||
}
|
||||
if (backRouteIndex == null) {
|
||||
return StateUtils.pop(state);
|
||||
}
|
||||
if (backRouteIndex > 0) {
|
||||
return {
|
||||
...state,
|
||||
routes: state.routes.slice(0, backRouteIndex),
|
||||
index: backRouteIndex - 1,
|
||||
isTransitioning: action.immediate !== true,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ export default (routeConfigs, config = {}) => {
|
||||
state = {
|
||||
routes,
|
||||
index: initialRouteIndex,
|
||||
isTransitioning: false,
|
||||
};
|
||||
// console.log(`${order.join('-')}: Initial state`, {state});
|
||||
}
|
||||
|
||||
@@ -13,6 +13,10 @@ const ROUTERS = {
|
||||
StackRouter,
|
||||
};
|
||||
|
||||
const dummyEventSubscriber = (name: string, handler: (*) => void) => ({
|
||||
remove: () => {},
|
||||
});
|
||||
|
||||
Object.keys(ROUTERS).forEach(routerName => {
|
||||
const Router = ROUTERS[routerName];
|
||||
|
||||
@@ -49,19 +53,31 @@ Object.keys(ROUTERS).forEach(routerName => {
|
||||
];
|
||||
expect(
|
||||
router.getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[0],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).title
|
||||
).toEqual(undefined);
|
||||
expect(
|
||||
router.getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[1], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[1],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).title
|
||||
).toEqual('BarTitle');
|
||||
expect(
|
||||
router.getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[2], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[2],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).title
|
||||
).toEqual('Baz-123');
|
||||
@@ -114,6 +130,7 @@ test('Handles deep action', () => {
|
||||
const state1 = TestRouter.getStateForAction({ type: NavigationActions.INIT });
|
||||
const expectedState = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0-2',
|
||||
@@ -126,6 +143,7 @@ test('Handles deep action', () => {
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
immediate: true,
|
||||
action: { type: NavigationActions.NAVIGATE, routeName: 'Zoo' },
|
||||
},
|
||||
state1
|
||||
@@ -152,6 +170,7 @@ test('Supports lazily-evaluated getScreen', () => {
|
||||
const state1 = TestRouter.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = TestRouter.getStateForAction({
|
||||
type: NavigationActions.NAVIGATE,
|
||||
immediate: true,
|
||||
routeName: 'Qux',
|
||||
});
|
||||
expect(state1.routes[0].key).toEqual('Init-id-0-4');
|
||||
@@ -159,7 +178,11 @@ test('Supports lazily-evaluated getScreen', () => {
|
||||
state1.routes[0].key = state2.routes[0].key;
|
||||
expect(state1).toEqual(state2);
|
||||
const state3 = TestRouter.getStateForAction(
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Zap' },
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
immediate: true,
|
||||
routeName: 'Zap',
|
||||
},
|
||||
state2
|
||||
);
|
||||
expect(state2).toEqual(state3);
|
||||
|
||||
@@ -83,6 +83,7 @@ describe('StackRouter', () => {
|
||||
expect(
|
||||
router.getComponentForState({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
@@ -93,6 +94,7 @@ describe('StackRouter', () => {
|
||||
expect(
|
||||
router.getComponentForState({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
@@ -116,6 +118,7 @@ describe('StackRouter', () => {
|
||||
expect(
|
||||
router.getComponentForState({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
@@ -126,6 +129,7 @@ describe('StackRouter', () => {
|
||||
expect(
|
||||
router.getComponentForState({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'foo' },
|
||||
{ key: 'b', routeName: 'bar' },
|
||||
@@ -324,6 +328,7 @@ describe('StackRouter', () => {
|
||||
const initState = TestRouter.getStateForAction(NavigationActions.init());
|
||||
expect(initState).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [{ key: 'Init-id-0-0', routeName: 'foo' }],
|
||||
});
|
||||
const pushedState = TestRouter.getStateForAction(
|
||||
@@ -349,6 +354,7 @@ describe('StackRouter', () => {
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0-4',
|
||||
@@ -361,6 +367,7 @@ describe('StackRouter', () => {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { name: 'Zoom' },
|
||||
immediate: true,
|
||||
},
|
||||
state
|
||||
);
|
||||
@@ -369,11 +376,12 @@ describe('StackRouter', () => {
|
||||
expect(state2 && state2.routes[1].params).toEqual({ name: 'Zoom' });
|
||||
expect(state2 && state2.routes.length).toEqual(2);
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: NavigationActions.BACK },
|
||||
{ type: NavigationActions.BACK, immediate: true },
|
||||
state2
|
||||
);
|
||||
expect(state3).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0-4',
|
||||
@@ -383,6 +391,38 @@ describe('StackRouter', () => {
|
||||
});
|
||||
});
|
||||
|
||||
test('Handles push transition logic with completion action', () => {
|
||||
const FooScreen = () => <div />;
|
||||
const BarScreen = () => <div />;
|
||||
const router = StackRouter({
|
||||
Foo: {
|
||||
screen: FooScreen,
|
||||
},
|
||||
Bar: {
|
||||
screen: BarScreen,
|
||||
},
|
||||
});
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { name: 'Zoom' },
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(1);
|
||||
expect(state2 && state2.isTransitioning).toEqual(true);
|
||||
const state3 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.COMPLETE_TRANSITION,
|
||||
},
|
||||
state2
|
||||
);
|
||||
expect(state3 && state3.index).toEqual(1);
|
||||
expect(state3 && state3.isTransitioning).toEqual(false);
|
||||
});
|
||||
|
||||
test('Handle basic stack logic for components with router', () => {
|
||||
const FooScreen = () => <div />;
|
||||
const BarScreen = () => <div />;
|
||||
@@ -402,9 +442,10 @@ describe('StackRouter', () => {
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0-6',
|
||||
key: 'Init-id-0-8',
|
||||
routeName: 'Foo',
|
||||
},
|
||||
],
|
||||
@@ -414,6 +455,7 @@ describe('StackRouter', () => {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { name: 'Zoom' },
|
||||
immediate: true,
|
||||
},
|
||||
state
|
||||
);
|
||||
@@ -422,14 +464,15 @@ describe('StackRouter', () => {
|
||||
expect(state2 && state2.routes[1].params).toEqual({ name: 'Zoom' });
|
||||
expect(state2 && state2.routes.length).toEqual(2);
|
||||
const state3 = router.getStateForAction(
|
||||
{ type: NavigationActions.BACK },
|
||||
{ type: NavigationActions.BACK, immediate: true },
|
||||
state2
|
||||
);
|
||||
expect(state3).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0-6',
|
||||
key: 'Init-id-0-8',
|
||||
routeName: 'Foo',
|
||||
},
|
||||
],
|
||||
@@ -452,6 +495,7 @@ describe('StackRouter', () => {
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
immediate: true,
|
||||
params: { name: 'Zoom' },
|
||||
},
|
||||
state
|
||||
@@ -460,6 +504,7 @@ describe('StackRouter', () => {
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
immediate: true,
|
||||
params: { name: 'Foo' },
|
||||
},
|
||||
state2
|
||||
@@ -470,7 +515,11 @@ describe('StackRouter', () => {
|
||||
);
|
||||
expect(state3).toEqual(state4);
|
||||
const state5 = router.getStateForAction(
|
||||
{ type: NavigationActions.BACK, key: state3 && state3.routes[1].key },
|
||||
{
|
||||
type: NavigationActions.BACK,
|
||||
key: state3 && state3.routes[1].key,
|
||||
immediate: true,
|
||||
},
|
||||
state4
|
||||
);
|
||||
expect(state5).toEqual(state);
|
||||
@@ -493,9 +542,10 @@ describe('StackRouter', () => {
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0-12',
|
||||
key: 'Init-id-0-14',
|
||||
routeName: 'Bar',
|
||||
},
|
||||
],
|
||||
@@ -515,9 +565,10 @@ describe('StackRouter', () => {
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
key: 'Init-id-0-13',
|
||||
key: state && state.routes[0].key,
|
||||
routeName: 'Bar',
|
||||
params: { foo: 'bar' },
|
||||
},
|
||||
@@ -542,6 +593,7 @@ describe('StackRouter', () => {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
params: { bar: '42' },
|
||||
immediate: true,
|
||||
},
|
||||
state
|
||||
);
|
||||
@@ -566,14 +618,17 @@ describe('StackRouter', () => {
|
||||
}
|
||||
);
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'Qux' },
|
||||
key: 'Init-id-0-16',
|
||||
},
|
||||
state
|
||||
);
|
||||
const key = state && state.routes[0].key;
|
||||
const state2 =
|
||||
key &&
|
||||
router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'Qux' },
|
||||
key,
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(0);
|
||||
expect(state2 && state2.routes[0].params).toEqual({ name: 'Qux' });
|
||||
});
|
||||
@@ -598,14 +653,14 @@ describe('StackRouter', () => {
|
||||
{
|
||||
type: NavigationActions.SET_PARAMS,
|
||||
params: { name: 'foobar' },
|
||||
key: 'Init-id-0-17',
|
||||
key: 'Init-id-0-19',
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(0);
|
||||
expect(state2 && state2.routes[0].routes[0].routes).toEqual([
|
||||
{
|
||||
key: 'Init-id-0-17',
|
||||
key: 'Init-id-0-19',
|
||||
routeName: 'Quux',
|
||||
params: { name: 'foobar' },
|
||||
},
|
||||
@@ -630,8 +685,13 @@ describe('StackRouter', () => {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
params: { bar: '42' },
|
||||
immediate: true,
|
||||
},
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
immediate: true,
|
||||
},
|
||||
{ type: NavigationActions.NAVIGATE, routeName: 'Bar' },
|
||||
],
|
||||
index: 1,
|
||||
},
|
||||
@@ -665,7 +725,13 @@ describe('StackRouter', () => {
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.RESET,
|
||||
actions: [{ type: NavigationActions.NAVIGATE, routeName: 'Foo' }],
|
||||
actions: [
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
immediate: true,
|
||||
},
|
||||
],
|
||||
index: 0,
|
||||
},
|
||||
state
|
||||
@@ -699,7 +765,12 @@ describe('StackRouter', () => {
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
action: { type: NavigationActions.NAVIGATE, routeName: 'baz' },
|
||||
immediate: true,
|
||||
action: {
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'baz',
|
||||
immediate: true,
|
||||
},
|
||||
},
|
||||
state
|
||||
);
|
||||
@@ -707,7 +778,13 @@ describe('StackRouter', () => {
|
||||
{
|
||||
type: NavigationActions.RESET,
|
||||
key: 'Init',
|
||||
actions: [{ type: NavigationActions.NAVIGATE, routeName: 'Foo' }],
|
||||
actions: [
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Foo',
|
||||
immediate: true,
|
||||
},
|
||||
],
|
||||
index: 0,
|
||||
},
|
||||
state2
|
||||
@@ -716,7 +793,13 @@ describe('StackRouter', () => {
|
||||
{
|
||||
type: NavigationActions.RESET,
|
||||
key: null,
|
||||
actions: [{ type: NavigationActions.NAVIGATE, routeName: 'Bar' }],
|
||||
actions: [
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Bar',
|
||||
immediate: true,
|
||||
},
|
||||
],
|
||||
index: 0,
|
||||
},
|
||||
state3
|
||||
@@ -738,6 +821,7 @@ describe('StackRouter', () => {
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
immediate: true,
|
||||
routeName: 'Bar',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
@@ -767,6 +851,7 @@ describe('StackRouter', () => {
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
immediate: true,
|
||||
routeName: 'Bar',
|
||||
params: { foo: '42' },
|
||||
},
|
||||
@@ -836,6 +921,7 @@ describe('StackRouter', () => {
|
||||
|
||||
const state = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
index: 1,
|
||||
@@ -954,3 +1040,51 @@ describe('StackRouter', () => {
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('Handles deep navigate completion action', () => {
|
||||
const LeafScreen = () => <div />;
|
||||
const FooScreen = () => <div />;
|
||||
FooScreen.router = StackRouter({
|
||||
Boo: { path: 'boo', screen: LeafScreen },
|
||||
Baz: { path: 'baz/:bazId', screen: LeafScreen },
|
||||
});
|
||||
const router = StackRouter({
|
||||
Foo: {
|
||||
screen: FooScreen,
|
||||
},
|
||||
Bar: {
|
||||
screen: LeafScreen,
|
||||
},
|
||||
});
|
||||
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
expect(state && state.index).toEqual(0);
|
||||
expect(state && state.routes[0].routeName).toEqual('Foo');
|
||||
const key = state && state.routes[0].key;
|
||||
const state2 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.NAVIGATE,
|
||||
routeName: 'Baz',
|
||||
},
|
||||
state
|
||||
);
|
||||
expect(state2 && state2.index).toEqual(0);
|
||||
expect(state2 && state2.isTransitioning).toEqual(false);
|
||||
/* $FlowFixMe */
|
||||
expect(state2 && state2.routes[0].index).toEqual(1);
|
||||
/* $FlowFixMe */
|
||||
expect(state2 && state2.routes[0].isTransitioning).toEqual(true);
|
||||
expect(!!key).toEqual(true);
|
||||
const state3 = router.getStateForAction(
|
||||
{
|
||||
type: NavigationActions.COMPLETE_TRANSITION,
|
||||
},
|
||||
state2
|
||||
);
|
||||
expect(state3 && state3.index).toEqual(0);
|
||||
expect(state3 && state3.isTransitioning).toEqual(false);
|
||||
/* $FlowFixMe */
|
||||
expect(state3 && state3.routes[0].index).toEqual(1);
|
||||
/* $FlowFixMe */
|
||||
expect(state3 && state3.routes[0].isTransitioning).toEqual(false);
|
||||
});
|
||||
|
||||
@@ -27,6 +27,7 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(state).toEqual(expectedState);
|
||||
const state2 = router.getStateForAction(
|
||||
@@ -39,6 +40,7 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(state2).toEqual(expectedState2);
|
||||
expect(router.getComponentForState(expectedState)).toEqual(ScreenA);
|
||||
@@ -64,6 +66,7 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(state).toEqual(expectedState);
|
||||
const state2 = router.getStateForAction(
|
||||
@@ -76,6 +79,7 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
expect(state2).toEqual(expectedState2);
|
||||
expect(router.getComponentForState(expectedState)).toEqual(ScreenA);
|
||||
@@ -99,6 +103,7 @@ describe('TabRouter', () => {
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -170,6 +175,7 @@ describe('TabRouter', () => {
|
||||
const state = router.getStateForAction(navAction);
|
||||
expect(state).toEqual({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
key: 'Foo',
|
||||
@@ -177,6 +183,7 @@ describe('TabRouter', () => {
|
||||
},
|
||||
{
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
routes: [
|
||||
@@ -216,12 +223,14 @@ describe('TabRouter', () => {
|
||||
let state = router.getStateForAction(navAction);
|
||||
expect(state).toEqual({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{
|
||||
index: 0,
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
@@ -241,6 +250,7 @@ describe('TabRouter', () => {
|
||||
);
|
||||
expect(state && state.routes[1]).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
routes: [
|
||||
@@ -267,12 +277,14 @@ describe('TabRouter', () => {
|
||||
});
|
||||
expect(state).toEqual({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{
|
||||
index: 1,
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
@@ -287,12 +299,14 @@ describe('TabRouter', () => {
|
||||
);
|
||||
expect(state2).toEqual({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{
|
||||
index: 0,
|
||||
key: 'Baz',
|
||||
routeName: 'Baz',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
@@ -331,16 +345,19 @@ describe('TabRouter', () => {
|
||||
const state = router.getStateForAction(INIT_ACTION);
|
||||
expect(state).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
@@ -350,6 +367,7 @@ describe('TabRouter', () => {
|
||||
index: 0,
|
||||
key: 'Bar',
|
||||
routeName: 'Bar',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
@@ -366,16 +384,19 @@ describe('TabRouter', () => {
|
||||
);
|
||||
expect(state2).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
index: 1,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
@@ -385,6 +406,7 @@ describe('TabRouter', () => {
|
||||
index: 1,
|
||||
key: 'Bar',
|
||||
routeName: 'Bar',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
@@ -411,16 +433,19 @@ describe('TabRouter', () => {
|
||||
});
|
||||
expect(state4).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
index: 1,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
index: 0,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
@@ -430,6 +455,7 @@ describe('TabRouter', () => {
|
||||
index: 1,
|
||||
key: 'Bar',
|
||||
routeName: 'Bar',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Zoo', routeName: 'Zoo' },
|
||||
{ key: 'Zap', routeName: 'Zap' },
|
||||
@@ -467,6 +493,7 @@ describe('TabRouter', () => {
|
||||
const state = router.getStateForAction({ type: NavigationActions.INIT });
|
||||
const expectedState = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar' },
|
||||
@@ -476,6 +503,7 @@ describe('TabRouter', () => {
|
||||
const state2 = router.getStateForAction(expectedAction, state);
|
||||
const expectedState2 = {
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Foo', routeName: 'Foo' },
|
||||
{ key: 'Bar', routeName: 'Bar', params },
|
||||
@@ -530,11 +558,13 @@ describe('TabRouter', () => {
|
||||
|
||||
const state = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{
|
||||
index: 1,
|
||||
key: 'Foo',
|
||||
routeName: 'Foo',
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'Boo', routeName: 'Boo' },
|
||||
{ key: 'Baz', routeName: 'Baz' },
|
||||
@@ -583,6 +613,7 @@ describe('TabRouter', () => {
|
||||
|
||||
expect(state0).toEqual({
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [{ key: 'a', routeName: 'a' }, { key: 'b', routeName: 'b' }],
|
||||
});
|
||||
|
||||
@@ -595,6 +626,7 @@ describe('TabRouter', () => {
|
||||
|
||||
expect(state1).toEqual({
|
||||
index: 1,
|
||||
isTransitioning: false,
|
||||
routes: [
|
||||
{ key: 'a', routeName: 'a' },
|
||||
{ key: 'b', routeName: 'b', params },
|
||||
@@ -620,11 +652,13 @@ describe('TabRouter', () => {
|
||||
const screenApreState = {
|
||||
index: 0,
|
||||
key: 'Init',
|
||||
isTransitioning: false,
|
||||
routeName: 'Foo',
|
||||
routes: [{ key: 'Init', routeName: 'Bar' }],
|
||||
};
|
||||
const preState = {
|
||||
index: 0,
|
||||
isTransitioning: false,
|
||||
routes: [screenApreState],
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ import { Component } from 'react';
|
||||
import createConfigGetter from '../createConfigGetter';
|
||||
import addNavigationHelpers from '../../addNavigationHelpers';
|
||||
|
||||
const dummyEventSubscriber = (name: string, handler: (*) => void) => ({
|
||||
remove: () => {},
|
||||
});
|
||||
|
||||
test('should get config for screen', () => {
|
||||
/* eslint-disable react/no-multi-comp */
|
||||
|
||||
@@ -63,49 +67,81 @@ test('should get config for screen', () => {
|
||||
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[0],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).title
|
||||
).toEqual('Welcome anonymous');
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[1], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[1],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).title
|
||||
).toEqual('Welcome jane');
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[0],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).gesturesEnabled
|
||||
).toEqual(true);
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[2], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[2],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).title
|
||||
).toEqual('Settings!!!');
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[2], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[2],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).gesturesEnabled
|
||||
).toEqual(false);
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[3], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[3],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).title
|
||||
).toEqual('10 new notifications');
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[3], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[3],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).gesturesEnabled
|
||||
).toEqual(true);
|
||||
expect(
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[4], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[4],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
).gesturesEnabled
|
||||
).toEqual(false);
|
||||
@@ -128,7 +164,11 @@ test('should throw if the route does not exist', () => {
|
||||
|
||||
expect(() =>
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false }),
|
||||
addNavigationHelpers({
|
||||
state: routes[0],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
}),
|
||||
{}
|
||||
)
|
||||
).toThrowError(
|
||||
@@ -147,7 +187,11 @@ test('should throw if the screen is not defined under the route config', () => {
|
||||
|
||||
expect(() =>
|
||||
getScreenOptions(
|
||||
addNavigationHelpers({ state: routes[0], dispatch: () => false })
|
||||
addNavigationHelpers({
|
||||
state: routes[0],
|
||||
dispatch: () => false,
|
||||
addListener: dummyEventSubscriber,
|
||||
})
|
||||
)
|
||||
).toThrowError('Route Home must define a screen or a getScreen.');
|
||||
});
|
||||
|
||||
@@ -3,6 +3,7 @@ import invariant from '../utils/invariant';
|
||||
import getScreenForRouteName from './getScreenForRouteName';
|
||||
import addNavigationHelpers from '../addNavigationHelpers';
|
||||
import validateScreenOptions from './validateScreenOptions';
|
||||
import getChildEventSubscriber from '../getChildEventSubscriber';
|
||||
|
||||
function applyConfig(configurer, navigationOptions, configProps) {
|
||||
if (typeof configurer === 'function') {
|
||||
@@ -51,6 +52,10 @@ export default (routeConfigs, navigatorScreenConfig) => (
|
||||
const childNavigation = addNavigationHelpers({
|
||||
state: childRoute,
|
||||
dispatch,
|
||||
addListener: getChildEventSubscriber(
|
||||
navigation.addListener,
|
||||
childRoute.key
|
||||
),
|
||||
});
|
||||
outputConfig = router.getScreenOptions(childNavigation, screenProps);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import Card from './Card';
|
||||
import Header from '../Header/Header';
|
||||
import NavigationActions from '../../NavigationActions';
|
||||
import addNavigationHelpers from '../../addNavigationHelpers';
|
||||
import getChildEventSubscriber from '../../getChildEventSubscriber';
|
||||
import SceneView from '../SceneView';
|
||||
|
||||
import TransitionConfigs from './TransitionConfigs';
|
||||
@@ -99,6 +100,10 @@ class CardStack extends React.Component {
|
||||
const screenNavigation = addNavigationHelpers({
|
||||
dispatch: navigation.dispatch,
|
||||
state: scene.route,
|
||||
addListener: getChildEventSubscriber(
|
||||
navigation.addListener,
|
||||
scene.route.key
|
||||
),
|
||||
});
|
||||
screenDetails = {
|
||||
state: scene.route,
|
||||
|
||||
@@ -4,6 +4,7 @@ import DrawerLayout from 'react-native-drawer-layout-polyfill';
|
||||
|
||||
import addNavigationHelpers from '../../addNavigationHelpers';
|
||||
import DrawerSidebar from './DrawerSidebar';
|
||||
import getChildEventSubscriber from '../../getChildEventSubscriber';
|
||||
|
||||
/**
|
||||
* Component that renders the drawer.
|
||||
@@ -81,6 +82,10 @@ export default class DrawerView extends React.PureComponent {
|
||||
this._screenNavigationProp = addNavigationHelpers({
|
||||
dispatch: navigation.dispatch,
|
||||
state: navigationState,
|
||||
addListener: getChildEventSubscriber(
|
||||
navigation.addListener,
|
||||
navigationState.key
|
||||
),
|
||||
});
|
||||
};
|
||||
|
||||
@@ -120,13 +125,8 @@ export default class DrawerView extends React.PureComponent {
|
||||
this.props.drawerCloseRoute
|
||||
);
|
||||
|
||||
const screenNavigation = addNavigationHelpers({
|
||||
state: this._screenNavigationProp.state,
|
||||
dispatch: this._screenNavigationProp.dispatch,
|
||||
});
|
||||
|
||||
const config = this.props.router.getScreenOptions(
|
||||
screenNavigation,
|
||||
this._screenNavigationProp,
|
||||
this.props.screenProps
|
||||
);
|
||||
|
||||
|
||||
@@ -120,6 +120,14 @@ class Transitioner extends React.Component {
|
||||
]
|
||||
: [];
|
||||
|
||||
// When there are no animations happening, avoid calling onTransitionStart/End.
|
||||
// This is important because the stack navigator fires the completion prop when
|
||||
// the transition is ended.
|
||||
if (!animations.length) {
|
||||
this.setState(nextState);
|
||||
return;
|
||||
}
|
||||
|
||||
// update scenes and play the transition
|
||||
this._isTransitionRunning = true;
|
||||
this.setState(nextState, async () => {
|
||||
|
||||
@@ -7,6 +7,7 @@ function testTransition(states) {
|
||||
const routes = states.map(keys => ({
|
||||
index: 0,
|
||||
routes: keys.map(key => ({ key, routeName: '' })),
|
||||
isTransitioning: false,
|
||||
}));
|
||||
|
||||
let scenes = [];
|
||||
@@ -89,11 +90,13 @@ describe('ScenesReducer', () => {
|
||||
const state1 = {
|
||||
index: 0,
|
||||
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
const state2 = {
|
||||
index: 1,
|
||||
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
const scenes1 = ScenesReducer([], state1, null);
|
||||
@@ -106,11 +109,13 @@ describe('ScenesReducer', () => {
|
||||
const state1 = {
|
||||
index: 0,
|
||||
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
const state2 = {
|
||||
index: 0,
|
||||
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
const scenes1 = ScenesReducer([], state1, null);
|
||||
@@ -122,11 +127,13 @@ describe('ScenesReducer', () => {
|
||||
const state1 = {
|
||||
index: 0,
|
||||
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
const state2 = {
|
||||
index: 0,
|
||||
routes: [{ key: '2', routeName: '' }, { key: '1', routeName: '' }],
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
const scenes1 = ScenesReducer([], state1, null);
|
||||
@@ -141,6 +148,7 @@ describe('ScenesReducer', () => {
|
||||
{ key: '1', x: 1, routeName: '' },
|
||||
{ key: '2', x: 2, routeName: '' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
const state2 = {
|
||||
@@ -149,6 +157,7 @@ describe('ScenesReducer', () => {
|
||||
{ key: '1', x: 3, routeName: '' },
|
||||
{ key: '2', x: 4, routeName: '' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
const scenes1 = ScenesReducer([], state1, null);
|
||||
@@ -163,6 +172,7 @@ describe('ScenesReducer', () => {
|
||||
{ key: '1', x: 1, routeName: '' },
|
||||
{ key: '2', x: 2, routeName: '' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
const state2 = {
|
||||
@@ -171,6 +181,7 @@ describe('ScenesReducer', () => {
|
||||
{ key: '1', x: 1, routeName: '' },
|
||||
{ key: '2', x: 2, routeName: '' },
|
||||
],
|
||||
isTransitioning: false,
|
||||
};
|
||||
|
||||
const scenes1 = ScenesReducer([], state1, null);
|
||||
|
||||
@@ -6,6 +6,10 @@ import TabRouter from '../../routers/TabRouter';
|
||||
import TabView from '../TabView/TabView';
|
||||
import TabBarBottom from '../TabView/TabBarBottom';
|
||||
|
||||
const dummyEventSubscriber = (name, handler) => ({
|
||||
remove: () => {},
|
||||
});
|
||||
|
||||
describe('TabBarBottom', () => {
|
||||
it('renders successfully', () => {
|
||||
const navigation = {
|
||||
@@ -13,6 +17,7 @@ describe('TabBarBottom', () => {
|
||||
index: 0,
|
||||
routes: [{ key: 's1', routeName: 's1' }],
|
||||
},
|
||||
addListener: dummyEventSubscriber,
|
||||
};
|
||||
const router = TabRouter({ s1: { screen: View } });
|
||||
|
||||
|
||||
@@ -219,6 +219,7 @@ exports[`TabBarBottom renders successfully 1`] = `
|
||||
<View
|
||||
navigation={
|
||||
Object {
|
||||
"addListener": [Function],
|
||||
"dispatch": undefined,
|
||||
"goBack": [Function],
|
||||
"navigate": [Function],
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import addNavigationHelpers from './addNavigationHelpers';
|
||||
import getChildEventSubscriber from './getChildEventSubscriber';
|
||||
|
||||
/**
|
||||
* HOC which caches the child navigation items.
|
||||
@@ -30,6 +31,10 @@ export default function withCachedChildNavigation(Comp) {
|
||||
this._childNavigationProps[route.key] = addNavigationHelpers({
|
||||
dispatch: navigation.dispatch,
|
||||
state: route,
|
||||
addListener: getChildEventSubscriber(
|
||||
navigation.addListener,
|
||||
route.key
|
||||
),
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@@ -2295,6 +2295,12 @@ fb-watchman@^2.0.0:
|
||||
dependencies:
|
||||
bser "^2.0.0"
|
||||
|
||||
fbemitter@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/fbemitter/-/fbemitter-2.1.1.tgz#523e14fdaf5248805bb02f62efc33be703f51865"
|
||||
dependencies:
|
||||
fbjs "^0.8.4"
|
||||
|
||||
fbjs-scripts@^0.8.1:
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/fbjs-scripts/-/fbjs-scripts-0.8.1.tgz#c1c6efbecb7f008478468976b783880c2f669765"
|
||||
@@ -2308,7 +2314,7 @@ fbjs-scripts@^0.8.1:
|
||||
semver "^5.1.0"
|
||||
through2 "^2.0.0"
|
||||
|
||||
fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.9:
|
||||
fbjs@^0.8.14, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9:
|
||||
version "0.8.16"
|
||||
resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db"
|
||||
dependencies:
|
||||
|
||||
Reference in New Issue
Block a user