Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(confirmLeave): update current route's history blocking after "addRoutes" #359

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 154 additions & 1 deletion __tests__/pure-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import pathToAction from '../src/pure-utils/pathToAction'
import actionToPath from '../src/pure-utils/actionToPath'
import changePageTitle from '../src/pure-utils/changePageTitle'

import { NOT_FOUND } from '../src/index'
import { NOT_FOUND, addRoutes } from '../src/index'

beforeEach(() => {
window.SSRtest = false
Expand Down Expand Up @@ -510,6 +510,159 @@ describe('confirmLeave()', () => {
expect(history.location.pathname).toEqual('/first')
})

it('can block leaving after addRoutes if confirmLeave was missing on init', () => {
const firstRouteName = 'FIRST'

const firstRouteOptions = {
path: '/first'
}

const routesMap = {
[firstRouteName]: firstRouteOptions,
SECOND: '/second'
}

const displayConfirmLeave = jest.fn()
const options = { displayConfirmLeave }
const { store, history } = setupAll('/first', options, { routesMap })
const confirmLeave = jest.fn((state, action) => 'blocked')

store.dispatch(
addRoutes({
[firstRouteName]: {
...firstRouteOptions,
confirmLeave
}
})
)

store.dispatch({ type: 'SECOND' })

const { type } = store.getState().location
expect(type).toEqual(firstRouteName)
expect(displayConfirmLeave).toBeCalled()
expect(history.location.pathname).toEqual('/first')
expect(confirmLeave).toBeCalled()
})

it('can block leaving after addRoutes if confirmLeave was passing on init', () => {
const firstRouteName = 'FIRST'

const firstRouteOptions = {
path: '/first'
}

const prevConfirmLeave = jest.fn((state, action) => undefined)
const nextConfirmLeave = jest.fn((state, action) => 'blocked')

const routesMap = {
[firstRouteName]: {
...firstRouteOptions,
confirmLeave: prevConfirmLeave
},
SECOND: '/second'
}

const displayConfirmLeave = jest.fn()
const options = { displayConfirmLeave }
const { store, history } = setupAll('/first', options, { routesMap })

store.dispatch(
addRoutes({
[firstRouteName]: {
...firstRouteOptions,
confirmLeave: nextConfirmLeave
}
})
)

store.dispatch({ type: 'SECOND' })

const { type } = store.getState().location
expect(type).toEqual(firstRouteName)
expect(displayConfirmLeave).toBeCalled()
expect(history.location.pathname).toEqual('/first')
expect(prevConfirmLeave).not.toBeCalled()
expect(nextConfirmLeave).toBeCalled()
})

it('can unblock leaving after addRoutes removing blocking confirmLeave', () => {
const firstRouteName = 'FIRST'

const firstRouteOptions = {
path: '/first'
}

const confirmLeave = jest.fn((state, action) => 'blocked')

const routesMap = {
[firstRouteName]: {
...firstRouteOptions,
confirmLeave
},
SECOND: '/second'
}

const displayConfirmLeave = jest.fn()
const options = { displayConfirmLeave }
const { store, history } = setupAll('/first', options, { routesMap })

store.dispatch(
addRoutes({
[firstRouteName]: firstRouteOptions
})
)

store.dispatch({ type: 'SECOND' })

const { type } = store.getState().location
expect(type).toEqual('SECOND')
expect(displayConfirmLeave).not.toBeCalled()
expect(history.location.pathname).toEqual('/second')
expect(confirmLeave).not.toBeCalled()
})

it('can unblock leaving after addRoutes if confirmLeave was blocking on init', () => {
const firstRouteName = 'FIRST'

const firstRouteOptions = {
path: '/first'
}

const prevConfirmLeave = jest.fn((state, action) => 'blocked')
const nextConfirmLeave = jest.fn((state, action) => undefined)

const routesMap = {
[firstRouteName]: {
...firstRouteOptions,
confirmLeave: prevConfirmLeave
},
SECOND: '/second'
}

const displayConfirmLeave = jest.fn()
const options = { displayConfirmLeave }
const { store, history } = setupAll('/first', options, { routesMap })

store.dispatch(
addRoutes({
[firstRouteName]: {
...firstRouteOptions,
confirmLeave: nextConfirmLeave
}
})
)

store.dispatch({ type: 'SECOND' })

const { type } = store.getState().location
expect(type).toEqual('SECOND')
expect(displayConfirmLeave).not.toBeCalled()
expect(history.location.pathname).toEqual('/second')
expect(prevConfirmLeave).not.toBeCalled()
expect(nextConfirmLeave).toBeCalled()
})

it('can leave throws (React Native where window.confirm does not exist)', () => {
global.confirm = undefined

Expand Down
29 changes: 27 additions & 2 deletions src/connectRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import pathnamePlusSearch from './pure-utils/pathnamePlusSearch'
import canUseDom from './pure-utils/canUseDom'

import {
clearBlocking,
createConfirm,
confirmUI,
setDisplayConfirmLeave,
Expand Down Expand Up @@ -230,8 +231,32 @@ export default (routesMap: RoutesMap = {}, options: Options = {}) => {

// code-splitting functionliaty to add routes after store is initially configured
if (action.type === ADD_ROUTES) {
const { type } = selectLocationState(store.getState())
const route = routesMap[type]

routesMap = { ...routesMap, ...action.payload.routes }
return next(action)

const result = next(action)
const nextRoute = routesMap[type]

if (route !== nextRoute) {
if (_confirm !== null) {
clearBlocking()
}

if (typeof nextRoute === 'object' && nextRoute.confirmLeave) {
_confirm = createConfirm(
nextRoute.confirmLeave,
store,
selectLocationState,
history,
querySerializer,
() => (_confirm = null)
)
}
}

return result
}

// navigation transformation specific to React Navigation
Expand Down Expand Up @@ -571,7 +596,7 @@ export default (routesMap: RoutesMap = {}, options: Options = {}) => {
_selectLocationState = selectLocationState

let _initialDispatch
let _confirm
let _confirm = null

_updateScroll = (performedByUser: boolean = true) => {
if (scrollBehavior) {
Expand Down
2 changes: 1 addition & 1 deletion src/pure-utils/confirmLeave.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ let _unblock
let _removeConfirmBlocking
let _displayConfirmLeave

const clearBlocking = () => {
export const clearBlocking = () => {
_unblock && _unblock()
_removeConfirmBlocking && _removeConfirmBlocking()
}
Expand Down