diff --git a/__tests__/router.test.js b/__tests__/router.test.js index e208671..547a6ce 100644 --- a/__tests__/router.test.js +++ b/__tests__/router.test.js @@ -88,6 +88,33 @@ describe('Router without interceptors', () => { expect(wrapper.prop('history').location.search).toEqual('?the=query') expect(Router.getCurrentQuery()).toEqual(query) }) + + test('Redirect from some path to Details page', () => { + const historyLength = wrapper.prop('history').index + + Router.push(null, {path: '/path_to_redirect_from'}) + + expect(Router.getCurrentRoute()).toEqual(Router.routes.details.path) + expect(wrapper.prop('history').index).toEqual(historyLength + 1) + }) + + test('Default redirect to About page', () => { + const historyLength = wrapper.prop('history').index + + Router.push(null, {path: 'some_unrealistic_path'}) + + expect(Router.getCurrentRoute()).toEqual(Router.routes.about.path) + expect(wrapper.prop('history').index).toEqual(historyLength + 1) + }) + + test('Redirect to any external url', () => { + const externalUrl = 'http://example.com' + + window.location.replace = jest.fn() + Router.redirect(externalUrl) + expect(window.location.replace).toHaveBeenCalledWith(externalUrl) + window.location.replace.mockRestore() + }) }) diff --git a/__tests__/setup/routes.js b/__tests__/setup/routes.js index 01c06a5..71e3a06 100644 --- a/__tests__/setup/routes.js +++ b/__tests__/setup/routes.js @@ -56,4 +56,11 @@ export default { component: DummyComponent('WhiteListParams'), whiteListParams: ['a', 'c'], }, + redirectFromRule: { + redirectFrom: '/path_to_redirect_from', + redirectTo: '/details' + }, + redirectToAbout: { + redirectTo: '/about', + }, } diff --git a/index.js b/index.js index c781f52..57034ca 100644 --- a/index.js +++ b/index.js @@ -10,6 +10,7 @@ const navigationMethodFields = [ 'pop', 'popToTop', 'replace', + 'redirect', 'showModal', 'dismissModal', ] diff --git a/package-lock.json b/package-lock.json index feb7fe3..8a36d3f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "tipsi-router", - "version": "1.7.0", + "version": "1.8.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -7935,9 +7935,8 @@ "dev": true }, "tipsi-travis-scripts": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/tipsi-travis-scripts/-/tipsi-travis-scripts-1.0.0.tgz", - "integrity": "sha512-n6A5ZJNnc4H2yKTt+4wYWmhou1WNwkf770I6/DtAqjVvAaqMPeoq4JgEX/SpLVvr5mdMxHV+GxdN0NLD1LLiHw==", + "version": "github:tipsi/tipsi-travis-scripts#7c15c289619228ad0e03926c30fe391e934ed672", + "from": "github:tipsi/tipsi-travis-scripts#7c15c289619228ad0e03926c30fe391e934ed672", "dev": true, "requires": { "chalk": "2.4.1", diff --git a/package.json b/package.json index 2737fcd..261e142 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "tipsi-router", - "version": "1.7.0", + "version": "1.8.0", "description": "React and RN router solution", "main": "index.js", "scripts": { diff --git a/src/Router.js b/src/Router.js index c70e9e8..fad2503 100644 --- a/src/Router.js +++ b/src/Router.js @@ -1,5 +1,5 @@ import React from 'react' -import { Router, Switch, Route } from 'react-router-dom' +import { Router, Switch, Route, Redirect } from 'react-router-dom' import { ModalRoute } from 'react-router-modal' import createBrowserHistory from 'history/createBrowserHistory' import createMemoryHistory from 'history/createMemoryHistory' @@ -34,7 +34,10 @@ export default class TipsiRouter extends RouterBase { const initialEntries = Object.values(routes).map(route => route.path) const initialIndex = initialEntries.indexOf(initialRoute) - return createMemoryHistory({ initialEntries, initialIndex }) + // filter out all Redirects (items with 'path' prop equal to undefined) + const entriesWithoutRedirects = initialEntries.filter(entry => !!entry) + + return createMemoryHistory({ initialEntries: entriesWithoutRedirects, initialIndex }) } filterSyncedState(state, filterFields) { @@ -53,6 +56,21 @@ export default class TipsiRouter extends RouterBase { createRouter(initialRoute, routes) { const shouldScrollToTop = this.defaultRouteConfig.shouldScrollToTop || true const elements = Object.entries(routes).reduce((memo, [key, route]) => { + // "to" is only required property for Redirect + if (route.redirectTo) { + const { redirectFrom, redirectTo, exact } = route + + // only allow "to", "from", or "exact" props to be passed to + const redirectParams = { + to: redirectTo, + } + + if (redirectFrom) redirectParams.from = redirectFrom + if (exact) redirectParams.exact = exact + + return memo.concat() + } + const RouteContainer = route.modal ? ModalRoute : Route const RouteComponent = route.component @@ -217,6 +235,10 @@ export default class TipsiRouter extends RouterBase { this.callHistoryMethodWithArguments('push', e, route, paramsOrOptions) } + redirect(url) { + window.location.replace(url) + } + async dismissModal(e) { if (e) { e.preventDefault() diff --git a/src/Router.native.js b/src/Router.native.js index 88e1bb2..30d2340 100644 --- a/src/Router.native.js +++ b/src/Router.native.js @@ -154,5 +154,8 @@ export default class TipsiRouter extends RouterBase { ) } + /* eslint-disable-next-line */ + redirect() {} + routeName = route => findKey(this.routes, { path: route.path }) } diff --git a/src/Router.wix.js b/src/Router.wix.js index 2ba97cd..753e47b 100644 --- a/src/Router.wix.js +++ b/src/Router.wix.js @@ -101,5 +101,10 @@ export default class TipsiRouter extends RouterBase { Navigation.dismissModal() } + /* eslint-disable-next-line */ + redirect(url) { + window.location.replace(url) + } + routeName = route => findKey(this.routes, { path: route.path }) }