* Fix Flow and Android build

* Enable flow on CI

* Fix and suppress flow errors
This commit is contained in:
Satyajit Sahoo
2017-02-13 21:56:30 +05:30
parent c151ccda40
commit 2e44476e75
43 changed files with 442 additions and 361 deletions

View File

@@ -1,4 +1,5 @@
{
"presets": ["react-native"],
"env": {
// For RN example development
"development": {

View File

@@ -52,4 +52,4 @@ suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
unsafe.enable_getters_and_setters=true
[version]
^0.35.0
^0.37.0

View File

@@ -17,3 +17,6 @@ deployment:
commands:
- yarn run build-docs
- ./scripts/deploy-website.sh
test:
pre:
- yarn run flow

View File

@@ -22,7 +22,7 @@ public class MainApplication extends Application implements ReactApplication {
}
@Override
protected boolean getUseDeveloperSupport() {
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}

View File

@@ -1,15 +1,13 @@
/* @flow */
/* eslint-env jest */
// See https://github.com/facebook/jest/issues/2208
jest.mock('Linking', () => {
return {
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
openURL: jest.fn(),
canOpenURL: jest.fn(),
getInitialURL: jest.fn().mockImplementation((value: string) => Promise.resolve(value)),
}
});
jest.mock('Linking', () => ({
addEventListener: jest.fn(),
removeEventListener: jest.fn(),
openURL: jest.fn(),
canOpenURL: jest.fn(),
getInitialURL: jest.fn().mockImplementation((value: string) => Promise.resolve(value)),
}));
// See https://github.com/facebook/react-native/issues/11659
jest.mock('ScrollView', () => {
@@ -17,5 +15,5 @@ jest.mock('ScrollView', () => {
class ScrollView extends RealComponent {
scrollTo = () => {}
}
return ScrollView
return ScrollView;
});

View File

@@ -42,7 +42,7 @@
"babel-eslint": "^7.0.0",
"babel-jest": "^17.0.2",
"babel-preset-es2015": "^6.18.0",
"babel-preset-react": "^6.16.0",
"babel-preset-react": "^6.22.0",
"babel-preset-react-native": "^1.9.0",
"babel-preset-react-native-syntax": "^1.0.0",
"babel-preset-stage-1": "^6.16.0",
@@ -52,10 +52,10 @@
"eslint-plugin-import": "^1.16.0",
"eslint-plugin-jsx-a11y": "^2.2.2",
"eslint-plugin-react": "^6.3.0",
"flow-bin": "^0.35.0",
"flow-bin": "^0.37.4",
"jest": "^18.1.0",
"react": "~15.4.0",
"react-native": "^0.40.0",
"react": "~15.4.2",
"react-native": "^0.41.2",
"react-native-vector-icons": "^3.0.0",
"react-test-renderer": "^15.4.2"
},

View File

@@ -29,10 +29,10 @@ export type NavigationState = {
* Index refers to the active child route in the routes array.
*/
index: number,
routes: Array<NavigationRoute>,
routes: Array<NavigationRoute | (NavigationRoute & NavigationState)>,
};
export interface NavigationRoute {
export type NavigationRoute = {
/**
* React's key used by some navigators. No need to specify these manually,
* they will be defined by the router.
@@ -52,7 +52,8 @@ export interface NavigationRoute {
* e.g. `{ car_id: 123 }` in a route that displays a car.
*/
params?: NavigationParams,
}
};
export type NavigationRouter = {
/**
@@ -92,12 +93,11 @@ export type NavigationRouter = {
};
export type NavigationScreenOption<T> =
| T
| (navigation: NavigationScreenProp<NavigationRoute, NavigationAction>,
config: NavigationScreenOptionConfig,
router?: NavigationRouter) => T
| T;
config: T) => T;
export type Style = Object | number | false | void;
export type Style = { [key: string]: any } | number | false | null | void | Array<Style>;
export type HeaderConfig = {
/**
@@ -260,7 +260,7 @@ export type NavigationContainerConfig = {
export type NavigationStackViewConfig = {
mode?: 'card' | 'modal',
headerMode?: HeaderMode,
headerComponent?: ReactClass<HeaderProps>,
headerComponent?: ReactClass<HeaderProps<*>>,
cardStyle?: Style,
onTransitionStart?: () => void,
onTransitionEnd?: () => void
@@ -290,23 +290,18 @@ export type NavigationAction =
| NavigationStackAction
| NavigationTabAction;
export type NavigationScreenRouteConfig = {
/** React component or navigator to render for this route */
screen: NavigationScreenComponent<*> | NavigationNavigator<*>,
export type NavigationRouteConfig<T> = T & {
navigationOptions?: NavigationScreenOptions,
path?: string,
};
export type NavigationLazyScreenRouteConfig = {
/** React component or navigator to lazily require and render for this route */
getScreen: () => (NavigationScreenComponent<*> | NavigationNavigator<*>),
navigationOptions?: NavigationScreenOptions,
path?: string,
};
export type NavigationRouteConfig =
| NavigationScreenRouteConfig
| NavigationLazyScreenRouteConfig;
export type NavigationScreenRouteConfig = NavigationScreenRouteConfig<{
// React component or navigator for this route */
screen: NavigationComponent,
} | {
// React component to lazily require and render for this route */
getScreen: () => NavigationComponent,
}>;
export type NavigationPathsConfig = {
[routeName: string]: string,
@@ -323,7 +318,7 @@ export type NavigationTabRouterConfig = {
};
export type NavigationRouteConfigMap = {
[routeName: string]: NavigationRouteConfig,
[routeName: string]: NavigationRouteConfig<*>,
};
export type NavigationDispatch<A> = (action: A) => boolean;
@@ -333,16 +328,14 @@ export type NavigationProp<S, A> = {
dispatch: NavigationDispatch<A>,
};
export type NavigationScreenProp<S, A> = {
state: S,
dispatch: NavigationDispatch<A>,
goBack: (routeKey?: string) => boolean,
export type NavigationScreenProp<S, A> = NavigationProp<S, A> & {
goBack: (routeKey?: ?string) => boolean,
navigate: (routeName: string, params?: NavigationParams, action?: NavigationAction) => boolean,
setParams: (newParams: NavigationParams) => boolean,
};
export type NavigationNavigatorProps = {
navigation: NavigationProp<NavigationState, NavigationAction>,
navigation: NavigationProp<NavigationRoute, NavigationAction>,
};
/**
@@ -396,7 +389,7 @@ export type NavigationTransitionProps = {
// is the index of the scene
scene: NavigationScene,
index: number,
navigation: NavigationScreenProp<NavigationRoute, NavigationAction>,
navigation: NavigationScreenProp<*, NavigationAction>,
// The gesture distance for `horizontal` and `vertical` transitions
gestureResponseDistance?: ?number,
@@ -442,8 +435,4 @@ export type NavigationSceneRenderer = (
export type NavigationStyleInterpolator = (
props: NavigationSceneRendererProps,
) => Object;
export type ContextWithNavigation = {
navigation: NavigationScreenProp<NavigationState, NavigationAction>;
};
) => Style;

View File

@@ -0,0 +1,6 @@
{
"extends": "../../.eslintrc",
"env": {
"jest": true
},
}

View File

@@ -17,7 +17,7 @@ describe('addNavigationHelpers', () => {
it('handles Back action when the key is not defined', () => {
const mockedDispatch = jest.fn(() => false).mockImplementationOnce(() => true);
expect(addNavigationHelpers({
state: {},
state: { routeName: 'Home' },
dispatch: mockedDispatch,
}).goBack()).toEqual(true);
expect(mockedDispatch).toBeCalledWith({ type: NavigationActions.BACK });
@@ -27,7 +27,7 @@ describe('addNavigationHelpers', () => {
it('handles Navigate action', () => {
const mockedDispatch = jest.fn(() => false).mockImplementationOnce(() => true);
expect(addNavigationHelpers({
state: {},
state: { routeName: 'Home' },
dispatch: mockedDispatch,
}).navigate('Profile', { name: 'Matt' })).toEqual(true);
expect(mockedDispatch).toBeCalledWith({
@@ -43,11 +43,11 @@ describe('addNavigationHelpers', () => {
expect(addNavigationHelpers({
state: { key: 'B', routeName: 'Settings' },
dispatch: mockedDispatch,
}).setParams({ notificationsEnabled: true })).toEqual(true);
}).setParams({ notificationsEnabled: 'yes' })).toEqual(true);
expect(mockedDispatch).toBeCalledWith({
type: NavigationActions.SET_PARAMS,
key: 'B',
params: { notificationsEnabled: true },
params: { notificationsEnabled: 'yes' },
});
expect(mockedDispatch.mock.calls.length).toBe(1);
});

View File

@@ -6,36 +6,36 @@
import type {
NavigationAction,
NavigationScreenProp,
NavigationProp,
NavigationRoute,
NavigationParams,
} from './TypeDefinition';
import NavigationActions from './NavigationActions'
import NavigationActions from './NavigationActions';
export default (navigation: NavigationProp<NavigationRoute, NavigationAction>): NavigationScreenProp<NavigationRoute, NavigationAction> => ({
...navigation,
goBack: (key): boolean => {
return navigation.dispatch(NavigationActions.back({
export default function<S: *> (navigation: NavigationProp<S, NavigationAction>) {
return {
...navigation,
goBack: (key?: ?string): boolean => navigation.dispatch(NavigationActions.back({
key: key === undefined ? navigation.state.key : key,
}));
},
navigate: (routeName, params, action): boolean => {
return navigation.dispatch(NavigationActions.navigate({
routeName,
params,
action,
}));
},
/**
* For updating current route params. For example the nav bar title and
* buttons are based on the route params.
* This means `setParams` can be used to update nav bar for example.
*/
setParams: (params): boolean => {
return navigation.dispatch(NavigationActions.setParams({
params,
key: navigation.state.key
}));
}
});
})),
navigate: (
routeName: string,
params?: NavigationParams,
action?: NavigationAction): boolean =>
navigation.dispatch(NavigationActions.navigate({
routeName,
params,
action,
})),
/**
* For updating current route params. For example the nav bar title and
* buttons are based on the route params.
* This means `setParams` can be used to update nav bar for example.
*/
setParams: (params: NavigationParams): boolean =>
navigation.dispatch(NavigationActions.setParams({
params,
key: navigation.state.key,
})),
};
}

View File

@@ -10,10 +10,12 @@ import NavigationActions from './NavigationActions';
import addNavigationHelpers from './addNavigationHelpers';
import type {
NavigationRoute,
NavigationAction,
NavigationContainerOptions,
NavigationProp,
NavigationState,
NavigationScreenProp,
} from './TypeDefinition';
/**
@@ -22,16 +24,16 @@ import type {
* This allows to use e.g. the StackNavigator and TabNavigator as root-level
* components.
*/
const createNavigationContainer = (
export default function createNavigationContainer<T: *>(
Component: ReactClass<*>,
containerConfig?: NavigationContainerOptions
) => {
) {
type Props = {
navigation: NavigationProp<NavigationState, NavigationAction>,
navigation: NavigationProp<T, NavigationAction>,
};
type State = {
nav: NavigationState,
nav: ?NavigationState,
};
function urlToPathAndParams(url: string) {
@@ -49,7 +51,7 @@ const createNavigationContainer = (
}
class NavigationContainer extends React.Component {
state: ?State;
state: State;
props: Props;
subs: ?{
@@ -75,12 +77,11 @@ const createNavigationContainer = (
constructor(props: Props) {
super(props);
this.state = null;
if (this._isStateful()) {
this.state = {
nav: Component.router.getStateForAction(NavigationActions.init()),
};
}
this.state = {
nav: this._isStateful()
? Component.router.getStateForAction(NavigationActions.init())
: null,
};
}
componentDidMount() {
@@ -110,7 +111,7 @@ const createNavigationContainer = (
this.subs && this.subs.remove();
}
_handleOpenURL = ({ url }) => {
_handleOpenURL = ({ url }: { url: string }) => {
console.log('Handling URL:', url);
const parsedUrl = urlToPathAndParams(url);
if (parsedUrl) {
@@ -145,6 +146,8 @@ const createNavigationContainer = (
return false;
};
_navigation: ?NavigationScreenProp<NavigationRoute, NavigationAction>;
render() {
let navigation = this.props.navigation;
if (this._isStateful()) {
@@ -166,6 +169,5 @@ const createNavigationContainer = (
}
return NavigationContainer;
};
}
export default createNavigationContainer;

View File

@@ -4,6 +4,7 @@ import React from 'react';
import type {
NavigationRouter,
NavigationRoute,
NavigationNavigator,
NavigationNavigatorProps,
} from '../TypeDefinition';

View File

@@ -9,6 +9,7 @@ import StateUtils from '../StateUtils';
import validateRouteConfigMap from './validateRouteConfigMap';
import type {
NavigationRoute,
NavigationAction,
NavigationComponent,
NavigationNavigateAction,
@@ -69,10 +70,8 @@ export default (
const wildcardRe = pathToRegexp(`${pathPattern}/*`, keys);
re = new RegExp(`(?:${re.source})|(?:${wildcardRe.source})`);
}
paths[routeName] = {
re,
keys,
};
/* $FlowFixMe */
paths[routeName] = { re, keys };
});
return {
@@ -91,7 +90,7 @@ export default (
},
getStateForAction(action: NavigationStackAction, state: ?NavigationState) {
action = NavigationActions.mapDeprecatedActionAndWarn(action)
action = NavigationActions.mapDeprecatedActionAndWarn(action);
// Set up the initial state if needed
if (!state) {
@@ -157,7 +156,8 @@ export default (
}
if (action.type === NavigationActions.SET_PARAMS) {
const lastRoute = state.routes.find(route => route.key === action.key);
/* $FlowFixMe */
const lastRoute = state.routes.find((route: *) => route.key === action.key);
if (lastRoute) {
const params = {
...lastRoute.params,
@@ -180,7 +180,7 @@ export default (
return {
...state,
routes: resetAction.actions.map((action: NavigationNavigateAction, index) => {
routes: resetAction.actions.map((action: NavigationNavigateAction, index: number) => {
const router = childRouters[action.routeName];
if (router) {
return {
@@ -204,7 +204,9 @@ export default (
if (action.type === NavigationActions.BACK) {
let backRouteIndex = null;
if (action.key) {
const backRoute = state.routes.find(route => route.key === action.key);
/* $FlowFixMe */
const backRoute = state.routes.find((route: *) => route.key === action.key);
/* $FlowFixMe */
backRouteIndex = state.routes.indexOf(backRoute);
}
if (backRouteIndex == null) {
@@ -242,7 +244,9 @@ export default (
let matchedRouteName;
let pathMatch;
let pathMatchKeys;
for (const routeName in paths) {
/* $FlowFixMe */
const { re, keys } = paths[routeName];
pathMatch = re.exec(pathToResolve);
if (pathMatch && pathMatch.length) {
@@ -264,21 +268,23 @@ export default (
let nestedAction;
if (childRouters[matchedRouteName]) {
nestedAction = childRouters[matchedRouteName].getActionForPathAndParams(
/* $FlowFixMe */
pathMatch.slice(pathMatchKeys.length).join('/')
);
}
// reduce the matched pieces of the path into the params
// of the route. `params` is null if there are no params.
const params = pathMatch.slice(1).reduce((result, matchResult, i) => {
/* $FlowFixMe */
const params = pathMatch.slice(1).reduce((result: *, matchResult: *, i: number) => {
const key = pathMatchKeys[i];
if (key.asterisk || !key) {
return result;
}
result = result || {};
const nextResult = result || {};
const paramName = key.name;
result[paramName] = matchResult;
return result;
nextResult[paramName] = matchResult;
return nextResult;
}, null);
return NavigationActions.navigate({

View File

@@ -1,20 +1,16 @@
/* @flow */
import React from 'react';
import invariant from 'fbjs/lib/invariant';
import getScreenForRouteName from './getScreenForRouteName';
import createConfigGetter from './createConfigGetter';
import invariant from 'fbjs/lib/invariant';
import warning from 'fbjs/lib/warning';
import NavigationActions from '../NavigationActions';
import StateUtils from '../StateUtils';
import validateRouteConfigMap from './validateRouteConfigMap';
import type {
NavigationAction,
NavigationComponent,
NavigationScreenComponent,
NavigationState,
NavigationRouteConfigMap,
NavigationParams,
@@ -51,8 +47,12 @@ export default (
`Should be one of ${order.map((n: *) => `"${n}"`).join(', ')}`
);
return {
getStateForAction(action: NavigationAction, inputState: ?NavigationState): ?NavigationState {
action = NavigationActions.mapDeprecatedActionAndWarn(action)
getStateForAction(
action: NavigationAction | { action: NavigationAction },
inputState?: ?NavigationState
): ?NavigationState {
// eslint-disable-next-line no-param-reassign
action = NavigationActions.mapDeprecatedActionAndWarn(action);
// Establish a default state
let state = inputState;
@@ -82,7 +82,10 @@ export default (
const activeTabLastState = state.routes[state.index];
const activeTabRouter = tabRouters[order[state.index]];
if (activeTabRouter) {
const activeTabState = activeTabRouter.getStateForAction(action.action || action, activeTabLastState);
const activeTabState = activeTabRouter.getStateForAction(
action.action || action,
activeTabLastState
);
if (!activeTabState && inputState) {
return null;
}
@@ -100,12 +103,15 @@ export default (
// handle the action, to allow inner tabs to change first
let activeTabIndex = state.index;
const isBackEligible = action.key == null || action.key === activeTabLastState.key;
if (action.type === NavigationActions.BACK && isBackEligible && shouldBackNavigateToInitialRoute) {
if (
action.type === NavigationActions.BACK &&
isBackEligible && shouldBackNavigateToInitialRoute
) {
activeTabIndex = initialRouteIndex;
}
let didNavigate = false;
if (action.type === NavigationActions.NAVIGATE) {
const navigateAction = ((action: any): NavigationNavigateAction);
const navigateAction = ((action: *): NavigationNavigateAction);
didNavigate = !!order.find((tabId: string, i: number) => {
if (tabId === navigateAction.routeName) {
activeTabIndex = i;
@@ -115,7 +121,9 @@ export default (
});
if (didNavigate && action.action) {
const tabRouter = tabRouters[action.routeName];
const newChildState = tabRouter && tabRouter.getStateForAction(action.action, state.routes[activeTabIndex]);
const newChildState = tabRouter
? tabRouter.getStateForAction(action.action, state.routes[activeTabIndex])
: null;
if (newChildState && newChildState !== state.routes[activeTabIndex]) {
const routes = [...state.routes];
routes[activeTabIndex] = newChildState;
@@ -126,10 +134,12 @@ export default (
};
}
}
// console.log({navId: order.join('-'), action, order, lastIndex: state.index, index: activeTabIndex});
}
if (action.type === NavigationActions.SET_PARAMS) {
const lastRoute = state.routes.find(route => route.key === action.key);
const lastRoute = state.routes.find(
/* $FlowFixMe */
(route: *) => route.key === action.key
);
if (lastRoute) {
const params = {
...lastRoute.params,
@@ -147,7 +157,6 @@ export default (
}
}
if (activeTabIndex !== state.index) {
// console.log(`${order.join('-')}: Normal navigation`, {lastIndex: state.index, newIndex: activeTabIndex});
return {
...state,
index: activeTabIndex,
@@ -160,7 +169,8 @@ export default (
// Let other tabs handle it and switch to the first tab that returns a new state
let index = state.index;
let routes = state.routes;
/* $FlowFixMe */
let routes: Array<NavigationState> = state.routes;
order.find((tabId: string, i: number) => {
const tabRouter = tabRouters[tabId];
if (i === index) {
@@ -195,7 +205,7 @@ export default (
return state;
},
getComponentForState(state: NavigationState) {
getComponentForState(state: NavigationState): NavigationScreenComponent<*> {
const routeName = order[state.index];
invariant(
routeName,
@@ -239,7 +249,7 @@ export default (
* This will return null if there is no action matched
*/
getActionForPathAndParams(path: string, params: ?NavigationParams) {
return order.map((tabId: string, index: number) => {
return order.map((tabId: string) => {
const parts = path.split('/');
const pathToTest = paths[tabId];
if (parts[0] === pathToTest) {
@@ -254,10 +264,11 @@ export default (
}
return action;
}
}).find(action => !!action) || order.map((tabId: string, index: number) => {
return null;
}).find((action: *) => !!action) || order.map((tabId: string) => {
const tabRouter = tabRouters[tabId];
return tabRouter && tabRouter.getActionForPathAndParams(path, params);
}).find(action => !!action) || null;
}).find((action: *) => !!action) || null;
},
getScreenConfig: createConfigGetter(routeConfigs, config.navigationOptions),

View File

@@ -0,0 +1,6 @@
{
"extends": "../../../.eslintrc",
"env": {
"jest": true
},
}

View File

@@ -1,6 +1,4 @@
/**
* @flow
*/
/* @flow */
import React from 'react';
@@ -15,11 +13,10 @@ const ROUTERS = {
StackRouter,
};
Object.keys(ROUTERS).forEach((routerName) => {
Object.keys(ROUTERS).forEach((routerName: string) => {
const Router = ROUTERS[routerName];
describe(`General router features - ${routerName}`, () => {
test('title is configurable using navigationOptions and getScreenConfig', () => {
class FooView extends React.Component {
render() { return <div />; }
@@ -38,13 +35,13 @@ Object.keys(ROUTERS).forEach((routerName) => {
Baz: { screen: BazView },
});
const routes = [
{key: 'A', routeName: 'Foo'},
{key: 'B', routeName: 'Bar'},
{key: 'A', routeName: 'Baz', params: { id: '123' }},
{ key: 'A', routeName: 'Foo' },
{ key: 'B', routeName: 'Bar' },
{ key: 'A', routeName: 'Baz', params: { id: '123' } },
];
expect(router.getScreenConfig(addNavigationHelpers({ state: routes[0], dispatch: () => false, }), 'title')).toEqual(null);
expect(router.getScreenConfig(addNavigationHelpers({ state: routes[1], dispatch: () => false, }), 'title')).toEqual('BarTitle');
expect(router.getScreenConfig(addNavigationHelpers({ state: routes[2], dispatch: () => false, }), 'title')).toEqual('Baz-123');
expect(router.getScreenConfig(addNavigationHelpers({ state: routes[0], dispatch: () => false }), 'title')).toEqual(null);
expect(router.getScreenConfig(addNavigationHelpers({ state: routes[1], dispatch: () => false }), 'title')).toEqual('BarTitle');
expect(router.getScreenConfig(addNavigationHelpers({ state: routes[2], dispatch: () => false }), 'title')).toEqual('Baz-123');
});
});
});
@@ -93,9 +90,10 @@ test('Handles deep action', () => {
],
};
expect(state1).toEqual(expectedState);
const state2 = TestRouter.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Foo', action: {type: NavigationActions.NAVIGATE, routeName: 'Zoo'} }, state1);
expect(state2.index).toEqual(1);
expect(state2.routes[1].index).toEqual(1);
const state2 = TestRouter.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Foo', action: { type: NavigationActions.NAVIGATE, routeName: 'Zoo' } }, state1);
expect(state2 && state2.index).toEqual(1);
/* $FlowFixMe */
expect(state2 && state2.routes[1].index).toEqual(1);
});
test('Supports lazily-evaluated getScreen', () => {

View File

@@ -1,6 +1,4 @@
/**
* @flow
*/
/* @flow */
import React from 'react';
@@ -359,7 +357,7 @@ describe('StackRouter', () => {
const state3 = router.getStateForAction({ type: NavigationActions.NAVIGATE, routeName: 'Bar', params: { name: 'Foo' } }, state2);
const state4 = router.getStateForAction({ type: NavigationActions.BACK, key: 'wrongKey' }, state3);
expect(state3).toEqual(state4);
const state5 = router.getStateForAction({ type: NavigationActions.BACK, key: state3.routes[1].key }, state4);
const state5 = router.getStateForAction({ type: NavigationActions.BACK, key: state3 && state3.routes[1].key }, state4);
expect(state5).toEqual(state);
});
@@ -466,6 +464,7 @@ describe('StackRouter', () => {
expect(state2 && state2.index).toEqual(0);
expect(state2 && state2.routes[0].routeName).toEqual('Foo');
/* $FlowFixMe */
expect(state2 && state2.routes[0].routes[0].routeName).toEqual('baz');
});
@@ -497,7 +496,9 @@ describe('StackRouter', () => {
screen: () => <div />,
},
});
/* $FlowFixMe: these are for deprecated action names */
const state = router.getStateForAction({ type: 'Init' });
/* $FlowFixMe: these are for deprecated action names */
const state2 = router.getStateForAction({ type: 'Reset', actions: [{ type: 'Navigate', routeName: 'Foo', params: { bar: '42' } }, { type: 'Navigate', routeName: 'Bar' }], index: 1 }, state);
expect(state2 && state2.index).toEqual(1);
expect(state2 && state2.routes[0].params).toEqual({ bar: '42' });

View File

@@ -1,6 +1,4 @@
/*
* @flow
*/
/* @flow */
import React from 'react';
import TabRouter from '../TabRouter';
@@ -377,7 +375,9 @@ describe('TabRouter', () => {
test('Maps old actions (uses "getStateForAction returns null when navigating to same tab" test)', () => {
const router = TabRouter({ Foo: BareLeafRouteConfig, Bar: BareLeafRouteConfig }, { initialRouteName: 'Bar' });
/* $FlowFixMe: these are for deprecated action names */
const state = router.getStateForAction({ type: 'Init' });
/* $FlowFixMe: these are for deprecated action names */
const state2 = router.getStateForAction({ type: 'Navigate', routeName: 'Bar' }, state);
expect(state2).toEqual(null);
});

View File

@@ -62,20 +62,23 @@ test('should get config for screen', () => {
const routes = [
{ key: 'A', routeName: 'Home' },
{ key: 'B', routeName: 'Home', params: { user: 'jane', } },
{ key: 'B', routeName: 'Home', params: { user: 'jane' } },
{ key: 'C', routeName: 'Settings' },
{ key: 'D', routeName: 'Notifications' },
{ key: 'E', routeName: 'Notifications', params: { fullscreen: true, } },
{ key: 'E', routeName: 'Notifications', params: { fullscreen: true } },
];
expect(getScreenConfig(addNavigationHelpers({ state: routes[0], dispatch: () => false }), 'title')).toEqual('Welcome anonymous');
expect(getScreenConfig(addNavigationHelpers({ state: routes[1], dispatch: () => false }), 'title')).toEqual('Welcome jane');
/* $FlowFixMe: we want tests to fail on undefined */
expect(getScreenConfig(addNavigationHelpers({ state: routes[0], dispatch: () => false }), 'header').visible).toEqual(true);
expect(getScreenConfig(addNavigationHelpers({ state: routes[2], dispatch: () => false }), 'title')).toEqual('Settings!!!');
expect(getScreenConfig(addNavigationHelpers({ state: routes[2], dispatch: () => false }), 'permalink')).toEqual('');
/* $FlowFixMe: we want tests to fail on undefined */
expect(getScreenConfig(addNavigationHelpers({ state: routes[2], dispatch: () => false }), 'header').visible).toEqual(false);
expect(getScreenConfig(addNavigationHelpers({ state: routes[3], dispatch: () => false }), 'title')).toEqual('10 new notifications');
expect(getScreenConfig(addNavigationHelpers({ state: routes[3], dispatch: () => false }), 'count')).toEqual(0);
/* $FlowFixMe: we want tests to fail on undefined */
expect(getScreenConfig(addNavigationHelpers({ state: routes[4], dispatch: () => false }), 'header').visible).toEqual(false);
});
@@ -120,13 +123,12 @@ test('should throw if the screen is not defined under the route config', () => {
});
test('should get recursive config for screen', () => {
class NotificationScreen extends Component {
static router = {
getScreenConfig: (navigation, optionName) => 'Baz',
getScreenConfig: () => 'Baz',
};
static navigationOptions = {
title: (navigation, childTitle) => `Bar ${childTitle}`,
title: (navigation: *, childTitle: *) => `Bar ${childTitle}`,
};
}
@@ -134,7 +136,7 @@ test('should get recursive config for screen', () => {
Notifications: {
screen: NotificationScreen,
navigationOptions: {
title: (navigation, childTitle) => `Foo ${childTitle}`,
title: (navigation: *, childTitle: *) => `Foo ${childTitle}`,
},
},
});
@@ -144,7 +146,7 @@ test('should get recursive config for screen', () => {
key: 'A',
routeName: 'Notifications',
index: 0,
routes: [ { key: 'A', routeName: 'Anything' } ],
routes: [{ key: 'A', routeName: 'Anything' }],
},
dispatch: () => false,
});
@@ -153,10 +155,9 @@ test('should get recursive config for screen', () => {
});
test('Allow passthrough configuration', () => {
class NotificationScreen extends Component {
static navigationOptions = {
tabBar: (navigation, tabBar) => ({ deepConfig: `also ${tabBar.color}` }),
tabBar: (navigation: *, tabBar: *) => ({ deepConfig: `also ${tabBar.color}` }),
};
}
@@ -174,5 +175,5 @@ test('Allow passthrough configuration', () => {
dispatch: () => false,
});
expect(getScreenConfig(childNavigation, 'tabBar', {color: 'red'})).toEqual({deepConfig: 'also red'});
expect(getScreenConfig(childNavigation, 'tabBar', { color: 'red' })).toEqual({ deepConfig: 'also red' });
});

View File

@@ -1,6 +1,4 @@
/**
* @flow
*/
/* @flow */
import React from 'react';

View File

@@ -8,10 +8,10 @@ import getScreenForRouteName from './getScreenForRouteName';
import addNavigationHelpers from '../addNavigationHelpers';
import type {
NavigationScreenProp,
NavigationRoute,
NavigationProp,
NavigationAction,
NavigationRouteConfigMap,
NavigationScreenOption,
NavigationScreenOptions,
} from '../TypeDefinition';
@@ -20,9 +20,9 @@ export default (
defaultOptions?: NavigationScreenOptions
) =>
(
navigation: NavigationScreenProp<NavigationRoute, NavigationAction>,
navigation: NavigationProp<*, NavigationAction>,
optionName: string,
config?: Object
config?: NavigationScreenOption<*>
) => {
const route = navigation.state;
invariant(
@@ -55,7 +55,7 @@ export default (
Component.navigationOptions,
routeConfig.navigationOptions,
].reduce(
(acc: Object, options: NavigationScreenOptions) => {
(acc: *, options: NavigationScreenOptions) => {
if (options && options[optionName] !== undefined) {
return typeof options[optionName] === 'function'
? options[optionName](navigation, acc)

View File

@@ -2,34 +2,37 @@
import invariant from 'fbjs/lib/invariant';
import type { NavigationRouteConfigMap } from '../TypeDefinition';
import type {
NavigationComponent,
NavigationRouteConfigMap,
} from '../TypeDefinition';
/**
* Simple helper that gets a single screen (React component or navigator)
* out of the navigator config.
*/
export default function getScreenForRouteName(
export default function getScreenForRouteName( // eslint-disable-line consistent-return
routeConfigs: NavigationRouteConfigMap,
routeName: string,
) {
): NavigationComponent {
const routeConfig = routeConfigs[routeName];
invariant(
routeConfig,
`There is no route defined for key ${routeName}.\n` +
`Must be one of: ${Object.keys(routeConfigs).map(a => `'${a}'`).join(',')}`
`Must be one of: ${Object.keys(routeConfigs).map((a: string) => `'${a}'`).join(',')}`
);
if (routeConfig.screen) {
return routeConfig.screen;
}
if (routeConfig.getScreen) {
if (typeof routeConfig.getScreen === 'function') {
const screen = routeConfig.getScreen();
invariant(
typeof screen === 'function',
`The getScreen defined for route '${routeName} didn't return a valid ` +
`screen or navigator.\n\n` +
'screen or navigator.\n\n' +
'Please pass it like this:\n' +
`${routeName}: {\n getScreen: () => require('./MyScreen').default\n}`
);

View File

@@ -34,10 +34,7 @@ function validateRouteConfigMap(routeConfigs: NavigationRouteConfigMap) {
if (routeConfig.screen) {
invariant(
// `screen: MyScreen`
typeof (routeConfig.screen) === 'function',
//(!routeConfig.screen.navigation) &&
//(!routeConfig.component.router),
`The component for route '${routeName}' must be a ` +
'a React component. For example:\n\n' +
'import MyScreen from \'./MyScreen\';\n' +

View File

@@ -1,6 +1,6 @@
/* @flow */
import React, { PropTypes } from 'react';
import React, { PropTypes, Component } from 'react';
import {
StyleSheet,
NativeModules,
@@ -21,18 +21,16 @@ import SceneView from './SceneView';
import type {
NavigationAction,
NavigationScreenProp,
NavigationState,
NavigationScene,
NavigationRoute,
NavigationSceneRenderer,
NavigationSceneRendererProps,
NavigationTransitionProps,
NavigationRouter,
Style,
} from '../TypeDefinition';
import type {
HeaderMode,
HeaderProps,
} from './Header';
import type { TransitionConfig } from './TransitionConfigs';
@@ -44,14 +42,14 @@ const NativeAnimatedModule = NativeModules && NativeModules.NativeAnimatedModule
type Props = {
screenProps?: {};
headerMode: HeaderMode,
headerComponent?: ReactClass<HeaderProps>,
headerComponent?: ReactClass<*>,
mode: 'card' | 'modal',
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
navigation: NavigationScreenProp<*, NavigationAction>,
router: NavigationRouter,
cardStyle?: any,
cardStyle?: Style,
onTransitionStart?: () => void,
onTransitionEnd?: () => void,
style: any,
style: Style,
gestureResponseDistance?: ?number,
/**
* If true, enable navigating back by swiping (see CardStackPanResponder).
@@ -67,13 +65,15 @@ type Props = {
type DefaultProps = {
mode: 'card' | 'modal',
gesturesEnabled: boolean,
headerComponent: ReactClass<HeaderProps>,
headerComponent: ReactClass<*>,
};
class CardStack extends React.Component<DefaultProps, Props, void> {
class CardStack extends Component<DefaultProps, Props, void> {
_render: NavigationSceneRenderer;
_renderScene: NavigationSceneRenderer;
_childNavigationProps: { [key: string]: NavigationScreenProp<NavigationRoute, NavigationAction> } = {};
_childNavigationProps: {
[key: string]: NavigationScreenProp<*, NavigationAction>
} = {};
static Card = Card;
static Header = Header;
@@ -248,7 +248,7 @@ class CardStack extends React.Component<DefaultProps, Props, void> {
style={styles.scenes}
>
{props.scenes.map(
scene => this._renderScene({
(scene: *) => this._renderScene({
...props,
scene,
navigation: this._getChildNavigation(scene),
@@ -286,13 +286,13 @@ class CardStack extends React.Component<DefaultProps, Props, void> {
...this.props.transitionConfig,
...defaultConfig,
};
} else {
return defaultConfig;
}
return defaultConfig;
}
_renderInnerCard(
Component: ReactClass<*>,
SceneComponent: ReactClass<*>,
props: NavigationSceneRendererProps,
): React.Element<*> {
const header = this.props.router.getScreenConfig(props.navigation, 'header');
@@ -307,7 +307,7 @@ class CardStack extends React.Component<DefaultProps, Props, void> {
<SceneView
screenProps={this.props.screenProps}
navigation={props.navigation}
component={Component}
component={SceneComponent}
/>
</View>
);
@@ -316,12 +316,14 @@ class CardStack extends React.Component<DefaultProps, Props, void> {
<SceneView
screenProps={this.props.screenProps}
navigation={props.navigation}
component={Component}
component={SceneComponent}
/>
);
}
_getChildNavigation = (scene: NavigationScene): NavigationScreenProp<NavigationRoute, NavigationAction> => {
_getChildNavigation = (
scene: NavigationScene
): NavigationScreenProp<*, NavigationAction> => {
let navigation = this._childNavigationProps[scene.key];
if (!navigation || navigation.state !== scene.route) {
navigation = this._childNavigationProps[scene.key] = addNavigationHelpers({
@@ -335,6 +337,7 @@ class CardStack extends React.Component<DefaultProps, Props, void> {
_renderScene(props: NavigationSceneRendererProps): React.Element<*> {
const isModal = this.props.mode === 'modal';
/* $FlowFixMe */
const { screenInterpolator } = this._getTransitionConfig();
const style = screenInterpolator && screenInterpolator(props);
@@ -343,7 +346,9 @@ class CardStack extends React.Component<DefaultProps, Props, void> {
if (this.props.gesturesEnabled) {
let onNavigateBack = null;
if (this.props.navigation.state.index !== 0) {
onNavigateBack = () => this.props.navigation.dispatch(NavigationActions.back({ key: props.scene.route.key }));
onNavigateBack = () => this.props.navigation.dispatch(
NavigationActions.back({ key: props.scene.route.key })
);
}
const panHandlersProps = {
...props,
@@ -355,14 +360,14 @@ class CardStack extends React.Component<DefaultProps, Props, void> {
CardStackPanResponder.forHorizontal(panHandlersProps);
}
const Component = this.props.router.getComponentForRouteName(props.scene.route.routeName);
const SceneComponent = this.props.router.getComponentForRouteName(props.scene.route.routeName);
return (
<Card
{...props}
key={`card_${props.scene.key}`}
panHandlers={panHandlers}
renderScene={props => this._renderInnerCard(Component, props)}
renderScene={(sceneProps: *) => this._renderInnerCard(SceneComponent, sceneProps)}
style={[style, this.props.cardStyle]}
/>
);

View File

@@ -12,7 +12,9 @@ import TouchableItem from '../TouchableItem';
import type {
NavigationScreenProp,
NavigationState,
NavigationRoute,
NavigationAction,
Style,
} from '../../TypeDefinition';
import type {
DrawerScene,
@@ -26,7 +28,7 @@ type Props = {
inactiveBackgroundColor?: string;
getLabelText: (scene: DrawerScene) => string;
renderIcon: (scene: DrawerScene) => ?React.Element<*>;
style?: any;
style?: Style;
};
/**

View File

@@ -17,7 +17,9 @@ type Props = {
screenProps?: {};
router: NavigationRouter,
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
childNavigationProps: { [key: string]: NavigationScreenProp<NavigationRoute, NavigationAction> },
childNavigationProps: {
[key: string]: NavigationScreenProp<NavigationRoute, NavigationAction>;
},
};
/**

View File

@@ -12,15 +12,17 @@ import withCachedChildNavigation from '../../withCachedChildNavigation';
import type {
NavigationScreenProp,
NavigationState,
NavigationRoute,
NavigationAction,
NavigationRouter,
Style,
} from '../../TypeDefinition';
import type {
DrawerScene,
} from './DrawerView';
type Navigation = NavigationScreenProp<NavigationState, NavigationAction>;
type Navigation = NavigationScreenProp<NavigationRoute, NavigationAction>;
type Props = {
router: NavigationRouter,
@@ -28,7 +30,7 @@ type Props = {
childNavigationProps: { [key: string]: Navigation },
contentComponent: ReactClass<*>,
contentOptions?: {},
style?: any;
style?: Style;
};
/**

View File

@@ -13,6 +13,7 @@ import type {
NavigationRouter,
NavigationState,
NavigationAction,
Style,
} from '../../TypeDefinition';
export type DrawerScene = {
@@ -27,21 +28,19 @@ export type DrawerViewConfig = {
drawerPosition: 'left' | 'right',
contentComponent: ReactClass<*>,
contentOptions?: {},
style?: any;
style?: Style;
};
type Navigation = NavigationScreenProp<NavigationState, NavigationAction>;
type Props = DrawerViewConfig & {
screenProps?: {};
router: NavigationRouter,
navigation: Navigation,
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
};
/**
* Component that renders the drawer.
*/
export default class DrawerView extends PureComponent<void, Props, void> {
export default class DrawerView<T: *> extends PureComponent<void, Props, void> {
static Items = DrawerNavigatorItems;
@@ -63,7 +62,7 @@ export default class DrawerView extends PureComponent<void, Props, void> {
this._updateScreenNavigation(nextProps.navigation);
}
_screenNavigationProp: Navigation;
_screenNavigationProp: NavigationScreenProp<T, NavigationAction>;
_handleDrawerOpen = () => {
const { navigation } = this.props;
@@ -82,9 +81,9 @@ export default class DrawerView extends PureComponent<void, Props, void> {
};
_updateScreenNavigation = (
navigation: Navigation
navigation: NavigationScreenProp<NavigationState, NavigationAction>
) => {
const navigationState: any = navigation.state.routes.find((route: *) => route.routeName === 'DrawerClose');
const navigationState = navigation.state.routes.find((route: *) => route.routeName === 'DrawerClose');
if (this._screenNavigationProp && this._screenNavigationProp.state === navigationState) {
return;
}
@@ -94,8 +93,10 @@ export default class DrawerView extends PureComponent<void, Props, void> {
});
}
_getNavigationState = (navigation: Navigation) => {
const navigationState: any = navigation.state.routes.find((route: *) => route.routeName === 'DrawerClose');
_getNavigationState = (
navigation: NavigationScreenProp<NavigationState, NavigationAction>
) => {
const navigationState = navigation.state.routes.find((route: *) => route.routeName === 'DrawerClose');
return navigationState;
};

View File

@@ -20,30 +20,31 @@ import addNavigationHelpers from '../addNavigationHelpers';
import type {
NavigationScene,
NavigationRouter,
NavigationRoute,
NavigationState,
NavigationAction,
NavigationScreenProp,
NavigationSceneRendererProps,
NavigationStyleInterpolator,
Style,
} from '../TypeDefinition';
export type HeaderMode = 'float' | 'screen' | 'none';
type SubViewProps = NavigationSceneRendererProps & {
onNavigateBack: ?() => void,
onNavigateBack?: () => void,
};
type Navigation = NavigationScreenProp<NavigationRoute, NavigationAction>;
type Navigation = NavigationScreenProp<NavigationState, NavigationAction>;
type SubViewRenderer = (subViewProps: SubViewProps) => ?React.Element<*>;
export type HeaderProps = NavigationSceneRendererProps & {
mode: HeaderMode,
onNavigateBack: ?Function,
onNavigateBack?: () => void,
renderLeftComponent: SubViewRenderer,
renderRightComponent: SubViewRenderer,
renderTitleComponent: SubViewRenderer,
tintColor: ?string,
tintColor?: string,
router: NavigationRouter,
};
@@ -98,7 +99,7 @@ class Header extends React.Component<void, HeaderProps, void> {
return undefined;
}
_getHeaderTitleStyle(navigation: Navigation): ?object {
_getHeaderTitleStyle(navigation: Navigation): Style {
const header = this.props.router.getScreenConfig(navigation, 'header');
if (header && header.titleStyle) {
return header.titleStyle;
@@ -106,14 +107,14 @@ class Header extends React.Component<void, HeaderProps, void> {
return undefined;
}
_renderTitleComponent = (props: SubViewProps) => {
_renderTitleComponent = (props: SubViewProps): React.Element<HeaderTitle> => {
const titleStyle = this._getHeaderTitleStyle(props.navigation);
const color = this._getHeaderTintColor(props.navigation);
const title = this._getHeaderTitle(props.navigation);
return <HeaderTitle style={[color && { color }, titleStyle]}>{title}</HeaderTitle>;
return <HeaderTitle style={[color ? { color } : null, titleStyle]}>{title}</HeaderTitle>;
};
_renderLeftComponent = (props: SubViewProps) => {
_renderLeftComponent = (props: SubViewProps): ?React.Element<HeaderBackButton> => {
if (props.scene.index === 0 || !props.onNavigateBack) {
return null;
}
@@ -137,21 +138,17 @@ class Header extends React.Component<void, HeaderProps, void> {
);
};
_renderRightComponent = () => {
return null;
};
_renderRightComponent = () => null;
_renderLeft(props: NavigationSceneRendererProps): ?React.Element<*> {
return this._renderSubView(
_renderLeft = (props: NavigationSceneRendererProps): ?React.Element<*> => this._renderSubView(
props,
'left',
this.props.renderLeftComponent,
this._renderLeftComponent,
HeaderStyleInterpolator.forLeft,
);
}
_renderTitle(props: NavigationSceneRendererProps, options: *): ?React.Element<*> {
_renderTitle = (props: NavigationSceneRendererProps, options: *): ?React.Element<*> => {
const style = {};
if (Platform.OS === 'android') {
@@ -172,15 +169,13 @@ class Header extends React.Component<void, HeaderProps, void> {
);
}
_renderRight(props: NavigationSceneRendererProps): ?React.Element<*> {
return this._renderSubView(
_renderRight = (props: NavigationSceneRendererProps): ?React.Element<*> => this._renderSubView(
props,
'right',
this.props.renderRightComponent,
this._renderRightComponent,
HeaderStyleInterpolator.forRight,
);
}
_renderSubView(
props: NavigationSceneRendererProps,
@@ -241,11 +236,10 @@ class Header extends React.Component<void, HeaderProps, void> {
// eslint-disable-next-line no-unused-vars
const { scenes, scene, style, position, progress, ...rest } = this.props;
let leftComponents = null;
let titleComponents = null;
let rightComponents = null;
let appBar = null;
if (this.props.mode === 'float') {
// eslint-disable-next-line no-shadow
const scenesProps = (scenes.map((scene: NavigationScene, index: number) => {
const props = NavigationPropTypes.extractSceneRendererProps(this.props);
props.scene = scene;
@@ -256,35 +250,47 @@ class Header extends React.Component<void, HeaderProps, void> {
});
return props;
}): Array<NavigationSceneRendererProps>);
leftComponents = scenesProps.map(this._renderLeft, this);
rightComponents = scenesProps.map(this._renderRight, this);
titleComponents = scenesProps.map((props: *, i: number) =>
const leftComponents = scenesProps.map(this._renderLeft, this);
const rightComponents = scenesProps.map(this._renderRight, this);
const titleComponents = scenesProps.map((props: *, i: number) =>
this._renderTitle(props, {
hasLeftComponent: leftComponents && !!leftComponents[i],
hasRightComponent: rightComponents && !!rightComponents[i],
})
);
appBar = (
<View style={styles.appBar}>
{titleComponents}
{leftComponents}
{rightComponents}
</View>
);
} else {
const staticRendererProps = {
...this.props,
position: new Animated.Value(scene.index),
progress: new Animated.Value(0),
};
leftComponents = this._renderLeft(staticRendererProps);
rightComponents = this._renderRight(staticRendererProps);
titleComponents = this._renderTitle(staticRendererProps, {
hasLeftComponent: !!leftComponents,
hasRightComponent: !!rightComponents,
const leftComponent = this._renderLeft(staticRendererProps);
const rightComponent = this._renderRight(staticRendererProps);
const titleComponent = this._renderTitle(staticRendererProps, {
hasLeftComponent: !!leftComponent,
hasRightComponent: !!rightComponent,
});
appBar = (
<View style={styles.appBar}>
{titleComponent}
{leftComponent}
{rightComponent}
</View>
);
}
return (
<Animated.View {...rest} style={[styles.container, style]}>
<View style={styles.appBar}>
{titleComponents}
{leftComponents}
{rightComponents}
</View>
{appBar}
</Animated.View>
);
}

View File

@@ -13,9 +13,9 @@ import {
import TouchableItem from './TouchableItem';
type Props = {
onPress: Function,
onPress?: () => void,
title?: string,
tintColor?: string;
tintColor?: ?string;
};
const HeaderBackButton = ({ onPress, title, tintColor }: Props) => (

View File

@@ -8,8 +8,13 @@ import {
Text,
} from 'react-native';
import type {
Style,
} from '../TypeDefinition';
type Props = {
style?: any,
tintColor?: ?string;
style?: Style,
};
const HeaderTitle = ({ style, ...rest }: Props) => (

View File

@@ -5,13 +5,13 @@ import React, { PureComponent } from 'react';
import type {
NavigationScreenProp,
NavigationState,
NavigationRoute,
NavigationAction,
ContextWithNavigation,
} from '../TypeDefinition';
type Props = {
screenProps?: {};
navigation: NavigationScreenProp<NavigationState, NavigationAction>;
navigation: NavigationScreenProp<NavigationRoute, NavigationAction>;
component: ReactClass<*>;
};
@@ -24,7 +24,7 @@ export default class SceneView extends PureComponent<void, Props, void> {
props: Props;
getChildContext(): ContextWithNavigation {
getChildContext() {
return {
navigation: this.props.navigation,
};

View File

@@ -12,14 +12,12 @@ import TabBarIcon from './TabBarIcon';
import type {
NavigationRoute,
NavigationState,
Style,
} from '../../TypeDefinition';
type TabScene = {
route: NavigationRoute;
focused: boolean;
index: number;
tintColor?: string;
};
import type {
TabScene,
} from './TabView';
type DefaultProps = {
activeTintColor: string;
@@ -40,8 +38,8 @@ type Props = {
getLabelText: (scene: TabScene) => string;
renderIcon: (scene: TabScene) => React.Element<*>;
showLabel: boolean;
style: any;
labelStyle?: any;
style?: Style;
labelStyle?: Style;
showIcon: boolean;
};

View File

@@ -8,25 +8,23 @@ import {
} from 'react-native';
import type {
NavigationRoute,
NavigationState,
NavigationRoute,
Style,
} from '../../TypeDefinition';
type TabScene = {
route: NavigationRoute;
focused: boolean;
index: number;
tintColor?: string;
};
import type {
TabScene,
} from './TabView';
type Props = {
activeTintColor: string;
inactiveTintColor: string;
scene: TabScene,
scene: TabScene;
position: Animated.Value;
navigationState: NavigationState;
renderIcon: (scene: TabScene) => React.Element<*>;
style?: any;
style?: Style;
};
export default class TabBarIcon extends PureComponent<void, Props, void> {

View File

@@ -9,16 +9,14 @@ import { TabBar } from 'react-native-tab-view';
import TabBarIcon from './TabBarIcon';
import type {
NavigationRoute,
NavigationState,
NavigationRoute,
Style,
} from '../../TypeDefinition';
type TabScene = {
route: NavigationRoute;
focused: boolean;
index: number;
tintColor?: string;
};
import type {
TabScene,
} from './TabView';
type DefaultProps = {
activeTintColor: string;
@@ -38,7 +36,7 @@ type Props = {
navigationState: NavigationState;
getLabelText: (scene: TabScene) => string;
renderIcon: (scene: TabScene) => React.Element<*>;
labelStyle?: any;
labelStyle?: Style;
};
export default class TabBarTop extends PureComponent<DefaultProps, Props, void> {
@@ -81,7 +79,7 @@ export default class TabBarTop extends PureComponent<DefaultProps, Props, void>
if (typeof label === 'string') {
return (
<Animated.Text style={[styles.label, { color }, labelStyle]}>
{label}
{upperCaseLabel ? label.toUpperCase() : label}
</Animated.Text>
);
}
@@ -115,10 +113,12 @@ export default class TabBarTop extends PureComponent<DefaultProps, Props, void>
};
render() {
// TODO: Define full proptypes
const props: any = this.props;
return (
<TabBar
{/* $FlowFixMe */
...this.props}
{...props}
renderIcon={this._renderIcon}
renderLabel={this._renderLabel}
/>

View File

@@ -33,6 +33,13 @@ export type TabViewConfig = {
lazyLoad?: boolean;
};
export type TabScene = {
route: NavigationRoute;
focused: boolean;
index: number;
tintColor?: ?string;
};
type Props = TabViewConfig & {
screenProps?: {},
navigation: NavigationScreenProp<NavigationState, NavigationAction>;
@@ -40,13 +47,6 @@ type Props = TabViewConfig & {
childNavigationProps: { [key: string]: NavigationScreenProp<NavigationRoute, NavigationAction> },
};
type TabScene = {
route: NavigationRoute;
focused: boolean;
index: number;
tintColor?: string;
};
let TabViewPager;
switch (Platform.OS) {
@@ -176,6 +176,7 @@ class TabView extends PureComponent<void, Props, void> {
}
return (
/* $FlowFixMe */
<TabViewAnimated
style={styles.container}
navigationState={navigation.state}
@@ -193,7 +194,6 @@ class TabView extends PureComponent<void, Props, void> {
const TabViewEnhanced = withCachedChildNavigation(TabView);
/* $FlowFixMe */
TabViewEnhanced.TabBarTop = TabBarTop;
TabViewEnhanced.TabBarBottom = TabBarBottom;

View File

@@ -16,6 +16,9 @@ import {
TouchableOpacity,
View,
} from 'react-native';
import type {
Style,
} from '../TypeDefinition';
const ANDROID_VERSION_LOLLIPOP = 21;
@@ -26,7 +29,7 @@ type Props = {
pressColor?: string;
activeOpacity?: number;
children?: React.Element<*>;
style?: any;
style?: Style;
};
type DefaultProps = {
@@ -58,9 +61,11 @@ export default class TouchableItem extends Component<DefaultProps, Props, void>
* We need to pass the background prop to specify a borderless ripple effect.
*/
if (Platform.OS === 'android' && Platform.Version >= ANDROID_VERSION_LOLLIPOP) {
const { style, ...rest } = this.props; // eslint-disable-line no-unused-vars
return (
<TouchableNativeFeedback
{...this.props}
{...rest}
style={null}
background={
TouchableNativeFeedback.Ripple(
@@ -74,12 +79,12 @@ export default class TouchableItem extends Component<DefaultProps, Props, void>
</View>
</TouchableNativeFeedback>
);
} else {
return (
<TouchableOpacity {...this.props}>
{this.props.children}
</TouchableOpacity>
);
}
return (
<TouchableOpacity {...this.props}>
{this.props.children}
</TouchableOpacity>
);
}
}

View File

@@ -19,6 +19,7 @@ import type {
NavigationAnimatedValue,
NavigationLayout,
NavigationScene,
NavigationState,
NavigationRoute,
NavigationAction,
NavigationScreenProp,
@@ -31,7 +32,7 @@ type Props = {
transitionProps: NavigationTransitionProps,
prevTransitionProps: ?NavigationTransitionProps,
) => NavigationTransitionSpec,
navigation: NavigationScreenProp<NavigationRoute, NavigationAction>,
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
onTransitionEnd: () => void,
onTransitionStart: () => void,
render: (
@@ -58,6 +59,12 @@ class Transitioner extends React.Component<*, Props, State> {
_prevTransitionProps: ?NavigationTransitionProps;
_transitionProps: NavigationTransitionProps;
_isMounted: boolean;
_isTransitionRunning: boolean;
_queuedTransition: ?{
nextProps: Props,
nextScenes: Array<NavigationScene>,
indexHasChanged: boolean,
};
props: Props;
state: State;

View File

@@ -0,0 +1,6 @@
{
"extends": "../../../.eslintrc",
"env": {
"jest": true
},
}

View File

@@ -1,3 +1,4 @@
/* @flow */
import ScenesReducer from '../ScenesReducer';
@@ -7,7 +8,7 @@ import ScenesReducer from '../ScenesReducer';
function testTransition(states) {
const routes = states.map(keys => ({
index: 0,
routes: keys.map(key => ({ key })),
routes: keys.map(key => ({ key, routeName: '' })),
}));
let scenes = [];
@@ -34,6 +35,7 @@ describe('ScenesReducer', () => {
key: 'scene_1',
route: {
key: '1',
routeName: '',
},
},
{
@@ -43,6 +45,7 @@ describe('ScenesReducer', () => {
key: 'scene_2',
route: {
key: '2',
routeName: '',
},
},
]);
@@ -63,6 +66,7 @@ describe('ScenesReducer', () => {
key: 'scene_1',
route: {
key: '1',
routeName: '',
},
},
{
@@ -72,6 +76,7 @@ describe('ScenesReducer', () => {
key: 'scene_2',
route: {
key: '2',
routeName: '',
},
},
{
@@ -81,6 +86,7 @@ describe('ScenesReducer', () => {
key: 'scene_3',
route: {
key: '3',
routeName: '',
},
},
]);
@@ -89,29 +95,30 @@ describe('ScenesReducer', () => {
it('gets active scene when index changes', () => {
const state1 = {
index: 0,
routes: [{ key: '1' }, { key: '2' }],
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
};
const state2 = {
index: 1,
routes: [{ key: '1' }, { key: '2' }],
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
};
const scenes1 = ScenesReducer([], state1, null);
const scenes2 = ScenesReducer(scenes1, state2, state1);
const route = scenes2.find(scene => scene.isActive).route;
expect(route).toEqual({ key: '2' });
/* $FlowFixMe: We want tests to fail on undefined */
const route = scenes2.find((scene: *) => scene.isActive).route;
expect(route).toEqual({ key: '2', routeName: '' });
});
it('gets same scenes', () => {
const state1 = {
index: 0,
routes: [{ key: '1' }, { key: '2' }],
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
};
const state2 = {
index: 0,
routes: [{ key: '1' }, { key: '2' }],
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
};
const scenes1 = ScenesReducer([], state1, null);
@@ -122,12 +129,12 @@ describe('ScenesReducer', () => {
it('gets different scenes when keys are different', () => {
const state1 = {
index: 0,
routes: [{ key: '1' }, { key: '2' }],
routes: [{ key: '1', routeName: '' }, { key: '2', routeName: '' }],
};
const state2 = {
index: 0,
routes: [{ key: '2' }, { key: '1' }],
routes: [{ key: '2', routeName: '' }, { key: '1', routeName: '' }],
};
const scenes1 = ScenesReducer([], state1, null);
@@ -138,12 +145,12 @@ describe('ScenesReducer', () => {
it('gets different scenes when routes are different', () => {
const state1 = {
index: 0,
routes: [{ key: '1', x: 1 }, { key: '2', x: 2 }],
routes: [{ key: '1', x: 1, routeName: '' }, { key: '2', x: 2, routeName: '' }],
};
const state2 = {
index: 0,
routes: [{ key: '1', x: 3 }, { key: '2', x: 4 }],
routes: [{ key: '1', x: 3, routeName: '' }, { key: '2', x: 4, routeName: '' }],
};
const scenes1 = ScenesReducer([], state1, null);
@@ -155,12 +162,12 @@ describe('ScenesReducer', () => {
it('gets different scenes when state index changes', () => {
const state1 = {
index: 0,
routes: [{ key: '1', x: 1 }, { key: '2', x: 2 }],
routes: [{ key: '1', x: 1, routeName: '' }, { key: '2', x: 2, routeName: '' }],
};
const state2 = {
index: 1,
routes: [{ key: '1', x: 1 }, { key: '2', x: 2 }],
routes: [{ key: '1', x: 1, routeName: '' }, { key: '2', x: 2, routeName: '' }],
};
const scenes1 = ScenesReducer([], state1, null);
@@ -183,6 +190,7 @@ describe('ScenesReducer', () => {
key: 'scene_1',
route: {
key: '1',
routeName: '',
},
},
{
@@ -192,6 +200,7 @@ describe('ScenesReducer', () => {
key: 'scene_2',
route: {
key: '2',
routeName: '',
},
},
{
@@ -201,6 +210,7 @@ describe('ScenesReducer', () => {
key: 'scene_3',
route: {
key: '3',
routeName: '',
},
},
]);
@@ -220,6 +230,7 @@ describe('ScenesReducer', () => {
key: 'scene_1',
route: {
key: '1',
routeName: '',
},
},
{
@@ -229,6 +240,7 @@ describe('ScenesReducer', () => {
key: 'scene_3',
route: {
key: '3',
routeName: '',
},
},
{
@@ -238,6 +250,7 @@ describe('ScenesReducer', () => {
key: 'scene_2',
route: {
key: '2',
routeName: '',
},
},
]);
@@ -258,6 +271,7 @@ describe('ScenesReducer', () => {
key: 'scene_1',
route: {
key: '1',
routeName: '',
},
},
{
@@ -267,6 +281,7 @@ describe('ScenesReducer', () => {
key: 'scene_2',
route: {
key: '2',
routeName: '',
},
},
{
@@ -276,6 +291,7 @@ describe('ScenesReducer', () => {
key: 'scene_3',
route: {
key: '3',
routeName: '',
},
},
]);

View File

@@ -3,10 +3,23 @@
import React from 'react';
import hoistStatics from 'hoist-non-react-statics';
import type { ContextWithNavigation } from '../TypeDefinition';
import type {
NavigationState,
NavigationRoute,
NavigationAction,
} from '../TypeDefinition';
export default function withNavigation(Component: ReactClass<T>) {
const componentWithNavigation = (props: T, { navigation }: ContextWithNavigation) => (
type Context = {
navigation: InjectedProps<NavigationState, NavigationAction>,
};
type InjectedProps = {
navigation: InjectedProps<NavigationState, NavigationAction>,
};
export default function withNavigation<T: *>(Component: ReactClass<T & InjectedProps>) {
const componentWithNavigation = (props: T, { navigation }: Context) => (
<Component {...props} navigation={navigation} />
);

View File

@@ -4,23 +4,24 @@ import React, { PureComponent } from 'react';
import addNavigationHelpers from './addNavigationHelpers';
import type {
NavigationScreenProp,
NavigationState,
NavigationRoute,
NavigationAction,
} from './TypeDefinition';
type Props = {
navigation: NavigationScreenProp<NavigationState, NavigationAction>,
type InjectedProps<N> = {
childNavigationProps: {
[key: string]: N,
},
};
/**
* HOC which caches the child navigation items.
*/
export default function withCachedChildNavigation<T: Props>(Comp: ReactClass<T>): ReactClass<T> {
return class extends PureComponent<void, T, void> {
export default function withCachedChildNavigation<T: *, N: *>(
Comp: ReactClass<T & InjectedProps<N>>
): ReactClass<T> {
return class extends PureComponent {
static displayName = `withCachedChildNavigation(${Comp.displayName || Comp.name})`;
@@ -35,11 +36,11 @@ export default function withCachedChildNavigation<T: Props>(Comp: ReactClass<T>)
}
_childNavigationProps: {
[key: string]: NavigationScreenProp<NavigationRoute, NavigationAction>,
[key: string]: NavigationScreenProp<N, NavigationAction>,
};
_updateNavigationProps = (
navigation: NavigationScreenProp<NavigationState, NavigationAction>
navigation: NavigationScreenProp<N, NavigationAction>
) => {
// Update props for each child route
if (!this._childNavigationProps) {

View File

@@ -259,7 +259,7 @@ babel-code-frame@^6.16.0, babel-code-frame@^6.22.0:
esutils "^2.0.2"
js-tokens "^3.0.0"
babel-core@^6.0.0, babel-core@^6.18.2, babel-core@^6.22.0, babel-core@^6.22.1, babel-core@^6.7.2:
babel-core@^6.0.0, babel-core@^6.18.2, babel-core@^6.21.0, babel-core@^6.22.0, babel-core@^6.22.1, babel-core@^6.7.2:
version "6.22.1"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-6.22.1.tgz#9c5fd658ba1772d28d721f6d25d968fc7ae21648"
dependencies:
@@ -293,7 +293,7 @@ babel-eslint@^7.0.0:
babylon "^6.13.0"
lodash.pickby "^4.6.0"
babel-generator@^6.18.0, babel-generator@^6.19.0, babel-generator@^6.22.0:
babel-generator@^6.18.0, babel-generator@^6.21.0, babel-generator@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-generator/-/babel-generator-6.22.0.tgz#d642bf4961911a8adc7c692b0c9297f325cda805"
dependencies:
@@ -542,7 +542,7 @@ babel-plugin-syntax-object-rest-spread@^6.13.0, babel-plugin-syntax-object-rest-
version "6.13.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-object-rest-spread/-/babel-plugin-syntax-object-rest-spread-6.13.0.tgz#fd6536f2bce13836ffa3a5458c4903a597bb3bf5"
babel-plugin-syntax-trailing-function-commas@^6.13.0, babel-plugin-syntax-trailing-function-commas@^6.22.0, babel-plugin-syntax-trailing-function-commas@^6.5.0, babel-plugin-syntax-trailing-function-commas@^6.8.0:
babel-plugin-syntax-trailing-function-commas@^6.13.0, babel-plugin-syntax-trailing-function-commas@^6.20.0, babel-plugin-syntax-trailing-function-commas@^6.22.0, babel-plugin-syntax-trailing-function-commas@^6.5.0, babel-plugin-syntax-trailing-function-commas@^6.8.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz#ba0360937f8d06e40180a43fe0d5616fff532cf3"
@@ -784,7 +784,7 @@ babel-plugin-transform-export-extensions@^6.22.0:
babel-plugin-syntax-export-extensions "^6.8.0"
babel-runtime "^6.22.0"
babel-plugin-transform-flow-strip-types@^6.18.0, babel-plugin-transform-flow-strip-types@^6.22.0, babel-plugin-transform-flow-strip-types@^6.5.0, babel-plugin-transform-flow-strip-types@^6.7.0, babel-plugin-transform-flow-strip-types@^6.8.0:
babel-plugin-transform-flow-strip-types@^6.21.0, babel-plugin-transform-flow-strip-types@^6.22.0, babel-plugin-transform-flow-strip-types@^6.5.0, babel-plugin-transform-flow-strip-types@^6.7.0, babel-plugin-transform-flow-strip-types@^6.8.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-flow-strip-types/-/babel-plugin-transform-flow-strip-types-6.22.0.tgz#84cb672935d43714fdc32bce84568d87441cf7cf"
dependencies:
@@ -797,7 +797,7 @@ babel-plugin-transform-object-assign@^6.5.0:
dependencies:
babel-runtime "^6.22.0"
babel-plugin-transform-object-rest-spread@^6.19.0, babel-plugin-transform-object-rest-spread@^6.22.0, babel-plugin-transform-object-rest-spread@^6.5.0, babel-plugin-transform-object-rest-spread@^6.6.5, babel-plugin-transform-object-rest-spread@^6.8.0:
babel-plugin-transform-object-rest-spread@^6.20.2, babel-plugin-transform-object-rest-spread@^6.22.0, babel-plugin-transform-object-rest-spread@^6.5.0, babel-plugin-transform-object-rest-spread@^6.6.5, babel-plugin-transform-object-rest-spread@^6.8.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-plugin-transform-object-rest-spread/-/babel-plugin-transform-object-rest-spread-6.22.0.tgz#1d419b55e68d2e4f64a5ff3373bd67d73c8e83bc"
dependencies:
@@ -845,7 +845,7 @@ babel-plugin-transform-strict-mode@^6.22.0:
babel-runtime "^6.22.0"
babel-types "^6.22.0"
babel-polyfill@^6.16.0, babel-polyfill@^6.22.0:
babel-polyfill@^6.20.0, babel-polyfill@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-polyfill/-/babel-polyfill-6.22.0.tgz#1ac99ebdcc6ba4db1e2618c387b2084a82154a3b"
dependencies:
@@ -981,7 +981,7 @@ babel-preset-react-native-syntax@^1.0.0:
babel-plugin-syntax-object-rest-spread "^6.13.0"
babel-plugin-syntax-trailing-function-commas "^6.13.0"
babel-preset-react-native@^1.9.0:
babel-preset-react-native@^1.9.0, babel-preset-react-native@^1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/babel-preset-react-native/-/babel-preset-react-native-1.9.1.tgz#ec8e378274410d78f550fa9f8edd70353f3bb2fe"
dependencies:
@@ -1015,7 +1015,7 @@ babel-preset-react-native@^1.9.0:
babel-plugin-transform-regenerator "^6.5.0"
react-transform-hmr "^1.0.4"
babel-preset-react@^6.16.0:
babel-preset-react@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-preset-react/-/babel-preset-react-6.22.0.tgz#7bc97e2d73eec4b980fb6b4e4e0884e81ccdc165"
dependencies:
@@ -1066,7 +1066,7 @@ babel-register@^6.18.0, babel-register@^6.22.0:
mkdirp "^0.5.1"
source-map-support "^0.4.2"
babel-runtime@^6.18.0, babel-runtime@^6.22.0:
babel-runtime@^6.18.0, babel-runtime@^6.20.0, babel-runtime@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.22.0.tgz#1cf8b4ac67c77a4ddb0db2ae1f74de52ac4ca611"
dependencies:
@@ -1083,7 +1083,7 @@ babel-template@^6.16.0, babel-template@^6.22.0:
babylon "^6.11.0"
lodash "^4.2.0"
babel-traverse@^6.15.0, babel-traverse@^6.18.0, babel-traverse@^6.19.0, babel-traverse@^6.22.0, babel-traverse@^6.22.1:
babel-traverse@^6.15.0, babel-traverse@^6.18.0, babel-traverse@^6.21.0, babel-traverse@^6.22.0, babel-traverse@^6.22.1:
version "6.22.1"
resolved "https://registry.yarnpkg.com/babel-traverse/-/babel-traverse-6.22.1.tgz#3b95cd6b7427d6f1f757704908f2fc9748a5f59f"
dependencies:
@@ -1097,7 +1097,7 @@ babel-traverse@^6.15.0, babel-traverse@^6.18.0, babel-traverse@^6.19.0, babel-tr
invariant "^2.2.0"
lodash "^4.2.0"
babel-types@^6.15.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.22.0:
babel-types@^6.15.0, babel-types@^6.18.0, babel-types@^6.19.0, babel-types@^6.21.0, babel-types@^6.22.0:
version "6.22.0"
resolved "https://registry.yarnpkg.com/babel-types/-/babel-types-6.22.0.tgz#2a447e8d0ea25d2512409e4175479fd78cc8b1db"
dependencies:
@@ -1214,18 +1214,12 @@ browser-resolve@^1.11.2:
dependencies:
resolve "1.1.7"
bser@1.0.2:
bser@1.0.2, bser@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.2.tgz#381116970b2a6deea5646dd15dd7278444b56169"
dependencies:
node-int64 "^0.4.0"
bser@^1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/bser/-/bser-1.0.3.tgz#d63da19ee17330a0e260d2a34422b21a89520317"
dependencies:
node-int64 "^0.4.0"
buffer-shims@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51"
@@ -2130,9 +2124,9 @@ flat-cache@^1.2.1:
graceful-fs "^4.1.2"
write "^0.2.1"
flow-bin@^0.35.0:
version "0.35.0"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.35.0.tgz#63d4eb9582ce352541be98e6a424503217141b07"
flow-bin@^0.37.4:
version "0.37.4"
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.37.4.tgz#3d8da2ef746e80e730d166e09040f4198969b76b"
for-in@^0.1.5:
version "0.1.6"
@@ -2844,9 +2838,9 @@ jest-file-exists@^17.0.0:
version "17.0.0"
resolved "https://registry.yarnpkg.com/jest-file-exists/-/jest-file-exists-17.0.0.tgz#7f63eb73a1c43a13f461be261768b45af2cdd169"
jest-haste-map@17.0.3:
version "17.0.3"
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-17.0.3.tgz#5232783e70577217b6b17d2a1c1766637a1d2fbd"
jest-haste-map@18.0.0:
version "18.0.0"
resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-18.0.0.tgz#707d3b5ae3bcbda971c39e8b911d20ad8502c748"
dependencies:
fb-watchman "^1.9.0"
graceful-fs "^4.1.6"
@@ -3881,27 +3875,27 @@ react-native-vector-icons@^3.0.0:
lodash "^4.0.0"
yargs "^6.3.0"
react-native@^0.40.0:
version "0.40.0"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.40.0.tgz#ca7b86a8e8fbc7653634ad47ca2ffd69fdf18ad5"
react-native@^0.41.2:
version "0.41.2"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.41.2.tgz#c6f486b8450a9909e6fed2dfd2c2af946563bf4f"
dependencies:
absolute-path "^0.0.0"
art "^0.10.0"
async "^2.0.1"
babel-core "^6.18.2"
babel-generator "^6.19.0"
babel-core "^6.21.0"
babel-generator "^6.21.0"
babel-plugin-external-helpers "^6.18.0"
babel-plugin-syntax-trailing-function-commas "^6.13.0"
babel-plugin-transform-flow-strip-types "^6.18.0"
babel-plugin-transform-object-rest-spread "^6.19.0"
babel-polyfill "^6.16.0"
babel-plugin-syntax-trailing-function-commas "^6.20.0"
babel-plugin-transform-flow-strip-types "^6.21.0"
babel-plugin-transform-object-rest-spread "^6.20.2"
babel-polyfill "^6.20.0"
babel-preset-es2015-node "^6.1.1"
babel-preset-fbjs "^2.1.0"
babel-preset-react-native "^1.9.0"
babel-preset-react-native "^1.9.1"
babel-register "^6.18.0"
babel-runtime "^6.18.0"
babel-traverse "^6.19.0"
babel-types "^6.19.0"
babel-runtime "^6.20.0"
babel-traverse "^6.21.0"
babel-types "^6.21.0"
babylon "^6.14.1"
base64-js "^1.1.2"
bser "^1.0.2"
@@ -3921,7 +3915,7 @@ react-native@^0.40.0:
immutable "~3.7.6"
imurmurhash "^0.1.4"
inquirer "^0.12.0"
jest-haste-map "17.0.3"
jest-haste-map "18.0.0"
joi "^6.6.1"
json-stable-stringify "^1.0.1"
json5 "^0.4.0"
@@ -3936,7 +3930,6 @@ react-native@^0.40.0:
opn "^3.0.2"
optimist "^0.6.1"
plist "^1.2.0"
progress "^1.1.8"
promise "^7.1.1"
react-clone-referenced-element "^1.0.1"
react-timer-mixin "^0.13.2"