diff --git a/src/NavigationActions.js b/src/NavigationActions.js index e758e9bb83..a0618eb082 100644 --- a/src/NavigationActions.js +++ b/src/NavigationActions.js @@ -2,6 +2,7 @@ const BACK = 'Navigation/BACK'; const INIT = 'Navigation/INIT'; const NAVIGATE = 'Navigation/NAVIGATE'; const POP = 'Navigation/POP'; +const POP_TO_TOP = 'Navigation/POP_TO_TOP'; const RESET = 'Navigation/RESET'; const SET_PARAMS = 'Navigation/SET_PARAMS'; const URI = 'Navigation/URI'; @@ -50,6 +51,11 @@ const pop = createAction(POP, payload => ({ immediate: payload && payload.immediate, })); +const popToTop = createAction(POP_TO_TOP, payload => ({ + type: POP_TO_TOP, + immediate: payload && payload.immediate, +})); + const reset = createAction(RESET, payload => ({ type: RESET, index: payload.index, @@ -131,6 +137,7 @@ export default { INIT, NAVIGATE, POP, + POP_TO_TOP, RESET, SET_PARAMS, URI, @@ -141,6 +148,7 @@ export default { init, navigate, pop, + popToTop, reset, setParams, uri, diff --git a/src/addNavigationHelpers.js b/src/addNavigationHelpers.js index 6faa1ab05e..12548527db 100644 --- a/src/addNavigationHelpers.js +++ b/src/addNavigationHelpers.js @@ -43,6 +43,10 @@ export default function(navigation) { navigation.dispatch( NavigationActions.pop({ n, immediate: params && params.immediate }) ), + popToTop: params => + navigation.dispatch( + NavigationActions.popToTop({ immediate: params && params.immediate }) + ), /** * For updating current route params. For example the nav bar title and * buttons are based on the route params. diff --git a/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap b/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap index 007386e86f..c586ba8e78 100644 --- a/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap +++ b/src/navigators/__tests__/__snapshots__/StackNavigator-test.js.snap @@ -98,6 +98,7 @@ exports[`StackNavigator applies correct values when headerRight is present 1`] = "goBack": [Function], "navigate": [Function], "pop": [Function], + "popToTop": [Function], "setParams": [Function], "state": Object { "index": 0, @@ -332,6 +333,7 @@ exports[`StackNavigator renders successfully 1`] = ` "goBack": [Function], "navigate": [Function], "pop": [Function], + "popToTop": [Function], "setParams": [Function], "state": Object { "index": 0, diff --git a/src/routers/StackRouter.js b/src/routers/StackRouter.js index a6371ca98a..e7fa8a12fe 100644 --- a/src/routers/StackRouter.js +++ b/src/routers/StackRouter.js @@ -164,7 +164,19 @@ export default (routeConfigs, stackConfig = {}) => { } } - // Handle explicit push navigation action + //Handle pop-to-top behavior. Make sure this happens after children have had a chance to handle the action, so that the inner stack pops to top first. + if (action.type === NavigationActions.POP_TO_TOP) { + if (state.index !== 0) { + return { + isTransitioning: action.immediate !== true, + index: 0, + routes: [state.routes[0]], + }; + } + return state; + } + + // Handle explicit push navigation action. Make sure this happens after children have had a chance to handle the action if ( action.type === NavigationActions.NAVIGATE && childRouters[action.routeName] !== undefined diff --git a/src/routers/__tests__/StackRouter-test.js b/src/routers/__tests__/StackRouter-test.js index 6306120678..ce0676ea5b 100644 --- a/src/routers/__tests__/StackRouter-test.js +++ b/src/routers/__tests__/StackRouter-test.js @@ -365,6 +365,42 @@ describe('StackRouter', () => { expect(pushedState.routes[1].routes[1].routeName).toEqual('qux'); }); + test('popToTop works as expected', () => { + const TestRouter = StackRouter({ + foo: { screen: () =>
}, + bar: { screen: () =>
}, + }); + + const state = { + index: 2, + isTransitioning: false, + routes: [ + { key: 'A', routeName: 'foo' }, + { key: 'B', routeName: 'bar', params: { bazId: '321' } }, + { key: 'C', routeName: 'foo' }, + ], + }; + const poppedState = TestRouter.getStateForAction( + NavigationActions.popToTop(), + state + ); + expect(poppedState.routes.length).toBe(1); + expect(poppedState.index).toBe(0); + expect(poppedState.isTransitioning).toBe(true); + const poppedState2 = TestRouter.getStateForAction( + NavigationActions.popToTop(), + poppedState + ); + expect(poppedState).toEqual(poppedState2); + const poppedImmediatelyState = TestRouter.getStateForAction( + NavigationActions.popToTop({ immediate: true }), + state + ); + expect(poppedImmediatelyState.routes.length).toBe(1); + expect(poppedImmediatelyState.index).toBe(0); + expect(poppedImmediatelyState.isTransitioning).toBe(false); + }); + test('Navigate Pushes duplicate routeName', () => { const TestRouter = StackRouter({ foo: { screen: () =>
}, diff --git a/src/views/__tests__/__snapshots__/TabView-test.js.snap b/src/views/__tests__/__snapshots__/TabView-test.js.snap index c8a80c6f3b..046827474e 100644 --- a/src/views/__tests__/__snapshots__/TabView-test.js.snap +++ b/src/views/__tests__/__snapshots__/TabView-test.js.snap @@ -225,6 +225,7 @@ exports[`TabBarBottom renders successfully 1`] = ` "goBack": [Function], "navigate": [Function], "pop": [Function], + "popToTop": [Function], "setParams": [Function], "state": Object { "key": "s1",