mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-29 05:00:43 +10:00
Merge pull request #42 from sharetribe/update-react-router2
Update React Router
This commit is contained in:
commit
d41eb61be8
37 changed files with 243 additions and 245 deletions
|
|
@ -16,7 +16,7 @@
|
|||
"react-helmet": "^4.0.0",
|
||||
"react-intl": "^2.2.3",
|
||||
"react-redux": "^5.0.2",
|
||||
"react-router": "4.0.0-alpha.6",
|
||||
"react-router-dom": "4.0.0-beta.6",
|
||||
"redux": "^3.6.0",
|
||||
"redux-form": "^6.5.0",
|
||||
"redux-saga": "^0.14.3",
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ const qs = require('qs');
|
|||
const url = require('url');
|
||||
const _ = require('lodash');
|
||||
const React = require('react');
|
||||
const { createServerRenderContext } = require('react-router');
|
||||
const sagaEffects = require('redux-saga/effects');
|
||||
const auth = require('./auth');
|
||||
const sdk = require('./fakeSDK');
|
||||
|
|
@ -143,22 +142,16 @@ app.use(compression());
|
|||
app.use('/static', express.static(path.join(buildPath, 'static')));
|
||||
|
||||
app.get('*', (req, res) => {
|
||||
const context = createServerRenderContext();
|
||||
const context = {};
|
||||
const filters = qs.parse(req.query);
|
||||
|
||||
// TODO fetch this asynchronously
|
||||
fetchInitialState(req.url)
|
||||
.then(preloadedState => {
|
||||
const html = render(req.url, context, preloadedState);
|
||||
const result = context.getResult();
|
||||
|
||||
if (result.redirect) {
|
||||
res.redirect(result.redirect.pathname);
|
||||
} else if (result.missed) {
|
||||
// Do a second render pass with the context to clue <Miss>
|
||||
// components into rendering this time.
|
||||
// See: https://react-router.now.sh/ServerRouter
|
||||
res.status(404).send(render(req.url, context, preloadedState));
|
||||
if (context.url) {
|
||||
res.redirect(context.url);
|
||||
} else {
|
||||
res.send(html);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,54 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { Miss } from 'react-router';
|
||||
import { RouterProvider, RoutesProvider } from './components';
|
||||
import { NotFoundPage, MatchWithSubRoutes } from './containers';
|
||||
import { flattenRoutes } from './routesConfiguration';
|
||||
import { connect } from 'react-redux';
|
||||
import { Switch, Route } from 'react-router-dom';
|
||||
import { NotFoundPage } from './containers';
|
||||
import { NamedRedirect } from './components';
|
||||
|
||||
const Routes = props => {
|
||||
const flattenedRoutes = flattenRoutes(props.routes);
|
||||
const matches = flattenedRoutes.map(route => <MatchWithSubRoutes key={route.name} {...route} />);
|
||||
const { isAuthenticated, routes } = props;
|
||||
|
||||
const renderComponent = (route, matchProps) => {
|
||||
const { auth, component: Component } = route;
|
||||
const { match, location } = matchProps;
|
||||
const canShowComponent = !auth || isAuthenticated;
|
||||
return canShowComponent
|
||||
? <Component params={match.params} location={location} />
|
||||
: <NamedRedirect name="LogInPage" state={{ from: match.url }} />;
|
||||
};
|
||||
|
||||
const toRouteComponent = route => (
|
||||
<Route
|
||||
key={route.name}
|
||||
path={route.pattern}
|
||||
exact={route.exactly}
|
||||
render={matchProps => renderComponent(route, matchProps)}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<RouterProvider router={props.router}>
|
||||
<RoutesProvider routes={props.routes}>
|
||||
<div>
|
||||
{matches}
|
||||
<Miss component={NotFoundPage} />
|
||||
</div>
|
||||
</RoutesProvider>
|
||||
</RouterProvider>
|
||||
<Switch>
|
||||
{routes.map(toRouteComponent)}
|
||||
<Route component={NotFoundPage} />
|
||||
</Switch>
|
||||
);
|
||||
};
|
||||
|
||||
const { any, array } = PropTypes;
|
||||
const { bool, arrayOf, shape, string, func } = PropTypes;
|
||||
|
||||
Routes.propTypes = { router: any.isRequired, routes: array.isRequired };
|
||||
Routes.propTypes = {
|
||||
isAuthenticated: bool.isRequired,
|
||||
routes: arrayOf(
|
||||
shape({
|
||||
name: string.isRequired,
|
||||
pattern: string.isRequired,
|
||||
exactly: bool,
|
||||
strict: bool,
|
||||
component: func.isRequired,
|
||||
loadData: func,
|
||||
}),
|
||||
).isRequired,
|
||||
};
|
||||
|
||||
export default Routes;
|
||||
const mapStateToProps = state => ({ isAuthenticated: state.Auth.isAuthenticated });
|
||||
|
||||
export default connect(mapStateToProps)(Routes);
|
||||
|
|
|
|||
35
src/app.js
35
src/app.js
|
|
@ -1,27 +1,29 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
import Helmet from 'react-helmet';
|
||||
import { BrowserRouter, ServerRouter } from 'react-router';
|
||||
import { BrowserRouter, StaticRouter } from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||
import en from 'react-intl/locale-data/en';
|
||||
import configureStore from './store';
|
||||
import Routes from './Routes';
|
||||
import routesConfiguration from './routesConfiguration';
|
||||
import { RoutesProvider } from './components';
|
||||
import routesConfiguration, { flattenRoutes } from './routesConfiguration';
|
||||
import localeData from './translations/en.json';
|
||||
|
||||
export const ClientApp = props => {
|
||||
const { store } = props;
|
||||
addLocaleData([...en]);
|
||||
const flattenedRoutes = flattenRoutes(routesConfiguration);
|
||||
return (
|
||||
<IntlProvider locale="en" messages={localeData}>
|
||||
<BrowserRouter>
|
||||
{({ router }) => (
|
||||
<Provider store={store}>
|
||||
<Routes router={router} routes={routesConfiguration} />
|
||||
</Provider>
|
||||
)}
|
||||
</BrowserRouter>
|
||||
<Provider store={store}>
|
||||
<RoutesProvider routes={routesConfiguration}>
|
||||
<BrowserRouter>
|
||||
<Routes routes={flattenedRoutes} />
|
||||
</BrowserRouter>
|
||||
</RoutesProvider>
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
};
|
||||
|
|
@ -33,15 +35,16 @@ ClientApp.propTypes = { store: any.isRequired };
|
|||
export const ServerApp = props => {
|
||||
const { url, context, store } = props;
|
||||
addLocaleData([...en]);
|
||||
const flattenedRoutes = flattenRoutes(routesConfiguration);
|
||||
return (
|
||||
<IntlProvider locale="en" messages={localeData}>
|
||||
<ServerRouter location={url} context={context}>
|
||||
{({ router }) => (
|
||||
<Provider store={store}>
|
||||
<Routes router={router} routes={routesConfiguration} />
|
||||
</Provider>
|
||||
)}
|
||||
</ServerRouter>
|
||||
<Provider store={store}>
|
||||
<RoutesProvider routes={routesConfiguration}>
|
||||
<StaticRouter location={url} context={context}>
|
||||
<Routes routes={flattenedRoutes} />
|
||||
</StaticRouter>
|
||||
</RoutesProvider>
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,26 +2,26 @@ import React from 'react';
|
|||
import ReactDOM from 'react-dom';
|
||||
import ReactDOMServer from 'react-dom/server';
|
||||
import { forEach } from 'lodash';
|
||||
import { createServerRenderContext } from 'react-router';
|
||||
import { ClientApp, ServerApp } from './app';
|
||||
import configureStore from './store';
|
||||
|
||||
const store = configureStore({});
|
||||
|
||||
const render = (url, context) =>
|
||||
ReactDOMServer.renderToString(<ServerApp url={url} context={context} store={store} />);
|
||||
const render = (url, context) => {
|
||||
const store = configureStore({});
|
||||
return ReactDOMServer.renderToString(<ServerApp url={url} context={context} store={store} />);
|
||||
};
|
||||
|
||||
describe('Application', () => {
|
||||
it('renders in the client without crashing', () => {
|
||||
const store = configureStore({});
|
||||
const div = document.createElement('div');
|
||||
ReactDOM.render(<ClientApp store={store} />, div);
|
||||
});
|
||||
|
||||
it('renders in the server without crashing', () => {
|
||||
render('/', createServerRenderContext());
|
||||
render('/', {});
|
||||
});
|
||||
|
||||
it('renders correct routes in the server', () => {
|
||||
it('server renders pages that do not require authentication', () => {
|
||||
const urlTitles = {
|
||||
'/': 'Landing page',
|
||||
'/s': 'Search page',
|
||||
|
|
@ -35,11 +35,13 @@ describe('Application', () => {
|
|||
'/this-url-should-not-be-found': 'Page not found',
|
||||
};
|
||||
forEach(urlTitles, (title, url) => {
|
||||
const context = createServerRenderContext();
|
||||
const context = {};
|
||||
const body = render(url, context);
|
||||
expect(body).toMatch(`>${title}</h1>`);
|
||||
});
|
||||
});
|
||||
|
||||
it('server renders redirects for pages that require authentication', () => {
|
||||
const urlRedirects = {
|
||||
'/orders': '/login',
|
||||
'/sales': '/login',
|
||||
|
|
@ -56,10 +58,9 @@ describe('Application', () => {
|
|||
'/account/security': '/login',
|
||||
};
|
||||
forEach(urlRedirects, (redirectPath, url) => {
|
||||
const context = createServerRenderContext();
|
||||
const context = {};
|
||||
const body = render(url, context);
|
||||
const result = context.getResult();
|
||||
expect(result.redirect.pathname).toEqual(redirectPath);
|
||||
expect(context.url).toEqual(redirectPath);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ exports[`FilterPanel matches snapshot 1`] = `
|
|||
hash=""
|
||||
name="SearchListingsPage"
|
||||
params={Object {}}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
See studios
|
||||
</NamedLink>
|
||||
|
|
@ -15,7 +15,7 @@ exports[`FilterPanel matches snapshot 1`] = `
|
|||
hash=""
|
||||
name="SearchListingsPage"
|
||||
params={Object {}}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
X
|
||||
</NamedLink>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ exports[`ListingCard matches snapshot 1`] = `
|
|||
"slug": "Banyan-Studios",
|
||||
}
|
||||
}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
Banyan Studios
|
||||
</NamedLink>
|
||||
|
|
|
|||
|
|
@ -14,10 +14,9 @@ exports[`ListingCardSmall matches snapshot 1`] = `
|
|||
<div
|
||||
className={undefined}>
|
||||
<a
|
||||
className=""
|
||||
className={undefined}
|
||||
href="/l/Banyan-Studios/123"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
onClick={[Function]}>
|
||||
Banyan Studios
|
||||
</a>
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ exports[`MapPanel matches snapshot 1`] = `
|
|||
hash=""
|
||||
name="SearchFiltersPage"
|
||||
params={Object {}}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
Filters
|
||||
</NamedLink>
|
||||
|
|
@ -16,7 +16,7 @@ exports[`MapPanel matches snapshot 1`] = `
|
|||
hash=""
|
||||
name="SearchListingsPage"
|
||||
params={Object {}}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
X
|
||||
</NamedLink>
|
||||
|
|
|
|||
|
|
@ -4,28 +4,28 @@
|
|||
* (Helps to narrow down the scope of possible format changes to routes.)
|
||||
*/
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { pathByRouteName } from '../../routesConfiguration';
|
||||
|
||||
const NamedLink = (props, context) => {
|
||||
const { name, params, query, hash, state, ...rest } = props;
|
||||
const { name, search, hash, state, params, ...rest } = props;
|
||||
const pathname = pathByRouteName(name, context.routes, params);
|
||||
const locationDescriptor = { pathname, query, hash, state };
|
||||
const locationDescriptor = { pathname, search, hash, state };
|
||||
return <Link to={locationDescriptor} {...rest} />;
|
||||
};
|
||||
|
||||
const { array, object, string } = PropTypes;
|
||||
|
||||
NamedLink.contextTypes = { routes: array };
|
||||
NamedLink.contextTypes = { routes: array.isRequired };
|
||||
|
||||
NamedLink.defaultProps = { hash: '', params: {}, query: {}, state: {} };
|
||||
NamedLink.defaultProps = { search: '', hash: '', state: {}, params: {} };
|
||||
|
||||
NamedLink.propTypes = {
|
||||
hash: string,
|
||||
name: string.isRequired,
|
||||
params: object,
|
||||
query: object,
|
||||
search: string,
|
||||
hash: string,
|
||||
state: object,
|
||||
params: object,
|
||||
};
|
||||
|
||||
export default NamedLink;
|
||||
|
|
|
|||
|
|
@ -4,28 +4,27 @@
|
|||
* (Helps to narrow down the scope of possible format changes to routes.)
|
||||
*/
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Redirect } from 'react-router';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import { pathByRouteName } from '../../routesConfiguration';
|
||||
|
||||
const NamedRedirect = (props, context) => {
|
||||
const { name, params, query, hash, state, ...rest } = props;
|
||||
const { name, search, state, params, ...rest } = props;
|
||||
const pathname = pathByRouteName(name, context.routes, params);
|
||||
const locationDescriptor = { pathname, query, hash, state };
|
||||
const locationDescriptor = { pathname, search, state };
|
||||
return <Redirect to={locationDescriptor} {...rest} />;
|
||||
};
|
||||
|
||||
const { array, object, string } = PropTypes;
|
||||
|
||||
NamedRedirect.contextTypes = { routes: array };
|
||||
NamedRedirect.contextTypes = { routes: array.isRequired };
|
||||
|
||||
NamedRedirect.defaultProps = { hash: '', params: {}, query: {}, state: {} };
|
||||
NamedRedirect.defaultProps = { search: '', state: {}, params: {} };
|
||||
|
||||
NamedRedirect.propTypes = {
|
||||
hash: string,
|
||||
name: string.isRequired,
|
||||
params: object,
|
||||
query: object,
|
||||
search: string,
|
||||
state: object,
|
||||
params: object,
|
||||
};
|
||||
|
||||
export default NamedRedirect;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ exports[`OrderDetailsPanel matches snapshot 1`] = `
|
|||
"id": "some-test-order-id",
|
||||
}
|
||||
}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
You have a new message!
|
||||
</NamedLink>
|
||||
|
|
|
|||
|
|
@ -10,11 +10,15 @@ const scrollToTop = () => {
|
|||
|
||||
class PageLayout extends Component {
|
||||
componentDidMount() {
|
||||
this.historyUnlisten = this.context.history.listen(() => scrollToTop());
|
||||
if (this.context && this.context.history && this.context.history.listen) {
|
||||
this.historyUnlisten = this.context.history.listen(() => scrollToTop());
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.historyUnlisten();
|
||||
if (this.historyUnlisten) {
|
||||
this.historyUnlisten();
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ exports[`SearchResultsPanel matches snapshot 1`] = `
|
|||
hash=""
|
||||
name="SearchFiltersPage"
|
||||
params={Object {}}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
Filters
|
||||
</NamedLink>
|
||||
|
|
@ -14,7 +14,7 @@ exports[`SearchResultsPanel matches snapshot 1`] = `
|
|||
hash=""
|
||||
name="SearchMapPage"
|
||||
params={Object {}}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
Map
|
||||
</NamedLink>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link, Redirect } from 'react-router';
|
||||
import { Link, Redirect } from 'react-router-dom';
|
||||
import { PageLayout } from '../../components';
|
||||
import { LoginForm, SignUpForm } from '../../containers';
|
||||
import { login } from '../../ducks/Auth.ducks';
|
||||
|
|
@ -9,15 +9,14 @@ export const AuthenticationPageComponent = props => {
|
|||
const { location, tab, isAuthenticated, onLoginSubmit, onSignUpSubmit } = props;
|
||||
const isLogin = tab === 'login';
|
||||
const from = location.state && location.state.from ? location.state.from : null;
|
||||
const pathname = from ? from.pathname : null;
|
||||
|
||||
return (
|
||||
<PageLayout title={`Authentication page: ${tab} tab`}>
|
||||
{isAuthenticated ? <Redirect to={from || '/'} /> : null}
|
||||
{pathname
|
||||
{from
|
||||
? <p>
|
||||
You must log in to view the page at
|
||||
<code>{pathname}</code>
|
||||
<code>{from}</code>
|
||||
</p>
|
||||
: null}
|
||||
{isLogin ? <LoginForm onSubmit={onLoginSubmit} /> : <SignUpForm onSubmit={onSignUpSubmit} />}
|
||||
|
|
@ -28,12 +27,12 @@ export const AuthenticationPageComponent = props => {
|
|||
);
|
||||
};
|
||||
|
||||
AuthenticationPageComponent.defaultProps = { location: {}, tab: 'signup' };
|
||||
AuthenticationPageComponent.defaultProps = { tab: 'signup' };
|
||||
|
||||
const { any, oneOf, shape, bool, func } = PropTypes;
|
||||
const { object, oneOf, shape, bool, func } = PropTypes;
|
||||
|
||||
AuthenticationPageComponent.propTypes = {
|
||||
location: shape({ state: shape({ from: any }) }),
|
||||
location: shape({ state: object }).isRequired,
|
||||
tab: oneOf(['login', 'signup']),
|
||||
isAuthenticated: bool.isRequired,
|
||||
onLoginSubmit: func.isRequired,
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
exports[`AuthenticationPageComponent matches snapshot 1`] = `
|
||||
<Connect(PageLayout)
|
||||
title="Authentication page: signup tab">
|
||||
<p>
|
||||
You must log in to view the page at
|
||||
<code>
|
||||
/protected
|
||||
</code>
|
||||
</p>
|
||||
<ReduxForm
|
||||
onSubmit={[Function]} />
|
||||
<Link
|
||||
activeClassName=""
|
||||
activeOnlyWhenExact={false}
|
||||
activeStyle={Object {}}
|
||||
className=""
|
||||
isActive={[Function]}
|
||||
replace={false}
|
||||
style={Object {}}
|
||||
to={
|
||||
Object {
|
||||
"pathname": "/login",
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
"id": 12345,
|
||||
}
|
||||
}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
Confirm & Pay
|
||||
</NamedLink>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { PageLayout } from '../../components';
|
||||
|
||||
const toPath = (filter, id) => {
|
||||
|
|
|
|||
|
|
@ -4,13 +4,7 @@ exports[`InboxPage matches snapshot 1`] = `
|
|||
<ul>
|
||||
<li>
|
||||
<Link
|
||||
activeClassName=""
|
||||
activeOnlyWhenExact={false}
|
||||
activeStyle={Object {}}
|
||||
className=""
|
||||
isActive={[Function]}
|
||||
replace={false}
|
||||
style={Object {}}
|
||||
to="/conversation/1234">
|
||||
Single thread
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { NamedLink, PageLayout } from '../../components';
|
||||
import css from './ListingPage.css';
|
||||
|
||||
|
|
|
|||
|
|
@ -74,13 +74,7 @@ exports[`ListingPage matches snapshot 1`] = `
|
|||
</p>
|
||||
</div>
|
||||
<Link
|
||||
activeClassName=""
|
||||
activeOnlyWhenExact={false}
|
||||
activeStyle={Object {}}
|
||||
className=""
|
||||
isActive={[Function]}
|
||||
replace={false}
|
||||
style={Object {}}
|
||||
to="mailto:studio.dude@mystudio.com">
|
||||
<h2>
|
||||
Contact studio
|
||||
|
|
@ -137,7 +131,7 @@ exports[`ListingPage matches snapshot 1`] = `
|
|||
"id": 12345,
|
||||
}
|
||||
}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
Book Banyan Studios
|
||||
</NamedLink>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { PageLayout } from '../../components';
|
||||
|
||||
export default () => (
|
||||
|
|
|
|||
|
|
@ -4,13 +4,7 @@ exports[`ManageListingsPage matches snapshot 1`] = `
|
|||
<ul>
|
||||
<li>
|
||||
<Link
|
||||
activeClassName=""
|
||||
activeOnlyWhenExact={false}
|
||||
activeStyle={Object {}}
|
||||
className=""
|
||||
isActive={[Function]}
|
||||
replace={false}
|
||||
style={Object {}}
|
||||
to="/l/1234">
|
||||
Listing 1234
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Match, Redirect } from 'react-router';
|
||||
import { Match, Redirect } from 'react-router-dom';
|
||||
import routesConfiguration, { pathByRouteName } from '../../routesConfiguration';
|
||||
|
||||
// wrap `Match` and use this everywhere instead, then when
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { PageLayout } from '../../components';
|
||||
|
||||
export default () => (
|
||||
|
|
|
|||
|
|
@ -2,13 +2,7 @@ exports[`NotFoundPage matches snapshot 1`] = `
|
|||
<Connect(PageLayout)
|
||||
title="Page not found">
|
||||
<Link
|
||||
activeClassName=""
|
||||
activeOnlyWhenExact={false}
|
||||
activeStyle={Object {}}
|
||||
className=""
|
||||
isActive={[Function]}
|
||||
replace={false}
|
||||
style={Object {}}
|
||||
to="/">
|
||||
Index page
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -28,7 +28,6 @@ const OrderPage = props => {
|
|||
confirmationCode: 'X2587X',
|
||||
};
|
||||
|
||||
const activeStyle = { fontWeight: 'bold' };
|
||||
const detailsClassName = classNames(css.tabContent, {
|
||||
[css.tabContentVisible]: props.tab === 'details',
|
||||
});
|
||||
|
|
@ -38,15 +37,10 @@ const OrderPage = props => {
|
|||
|
||||
return (
|
||||
<PageLayout title={`Your ${title} booking is confirmed!`}>
|
||||
<NamedLink
|
||||
name="OrderDetailsPage"
|
||||
params={{ id: orderId }}
|
||||
style={{ marginRight: '2rem' }}
|
||||
activeStyle={activeStyle}
|
||||
>
|
||||
<NamedLink name="OrderDetailsPage" params={{ id: orderId }} style={{ marginRight: '2rem' }}>
|
||||
Booking details
|
||||
</NamedLink>
|
||||
<NamedLink name="OrderDiscussionPage" params={{ id: orderId }} activeStyle={activeStyle}>
|
||||
<NamedLink name="OrderDiscussionPage" params={{ id: orderId }}>
|
||||
Discussion
|
||||
</NamedLink>
|
||||
<OrderDetailsPanel className={detailsClassName} {...detailsProps} />
|
||||
|
|
|
|||
|
|
@ -2,11 +2,6 @@ exports[`OrderPage matches snapshot 1`] = `
|
|||
<Connect(PageLayout)
|
||||
title="Your Banyan Studios booking is confirmed!">
|
||||
<NamedLink
|
||||
activeStyle={
|
||||
Object {
|
||||
"fontWeight": "bold",
|
||||
}
|
||||
}
|
||||
hash=""
|
||||
name="OrderDetailsPage"
|
||||
params={
|
||||
|
|
@ -14,7 +9,7 @@ exports[`OrderPage matches snapshot 1`] = `
|
|||
"id": 1234,
|
||||
}
|
||||
}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}
|
||||
style={
|
||||
Object {
|
||||
|
|
@ -24,11 +19,6 @@ exports[`OrderPage matches snapshot 1`] = `
|
|||
Booking details
|
||||
</NamedLink>
|
||||
<NamedLink
|
||||
activeStyle={
|
||||
Object {
|
||||
"fontWeight": "bold",
|
||||
}
|
||||
}
|
||||
hash=""
|
||||
name="OrderDiscussionPage"
|
||||
params={
|
||||
|
|
@ -36,7 +26,7 @@ exports[`OrderPage matches snapshot 1`] = `
|
|||
"id": 1234,
|
||||
}
|
||||
}
|
||||
query={Object {}}
|
||||
search=""
|
||||
state={Object {}}>
|
||||
Discussion
|
||||
</NamedLink>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* eslint-disable react/no-unescaped-entities */
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { PageLayout } from '../../components';
|
||||
|
||||
const SalesConversationPage = props => {
|
||||
|
|
|
|||
|
|
@ -6,25 +6,13 @@ exports[`SalesConversationPage matches snapshot 1`] = `
|
|||
12345
|
||||
</p>
|
||||
<Link
|
||||
activeClassName=""
|
||||
activeOnlyWhenExact={false}
|
||||
activeStyle={Object {}}
|
||||
className=""
|
||||
isActive={[Function]}
|
||||
replace={false}
|
||||
style={Object {}}
|
||||
to="/sale/12345/discussion">
|
||||
Discussion tab
|
||||
</Link>
|
||||
<br />
|
||||
<Link
|
||||
activeClassName=""
|
||||
activeOnlyWhenExact={false}
|
||||
activeStyle={Object {}}
|
||||
className=""
|
||||
isActive={[Function]}
|
||||
replace={false}
|
||||
style={Object {}}
|
||||
to="/sale/12345/details">
|
||||
Details tab
|
||||
</Link>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { map, size } from 'lodash';
|
||||
import * as allExamples from '../../examples';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link } from 'react-router';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { logout } from '../../ducks/Auth.ducks';
|
||||
|
||||
import css from './Topbar.css';
|
||||
|
||||
const Topbar = (props, context) => {
|
||||
const { isAuthenticated, onLogout, user } = props;
|
||||
const { router } = context;
|
||||
const Topbar = props => {
|
||||
const { isAuthenticated, onLogout, user, push: historyPush } = props;
|
||||
const house = { dangerouslySetInnerHTML: { __html: '🏠' } };
|
||||
const hamburger = { dangerouslySetInnerHTML: { __html: '🍔' } };
|
||||
|
||||
const handleChange = e => {
|
||||
const value = e.target.value;
|
||||
router.transitionTo(value);
|
||||
historyPush(value);
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
// Router is passed to the action to enable redirect to home when
|
||||
// logout succeeds.
|
||||
onLogout(router);
|
||||
// History push function is passed to the action to enable
|
||||
// redirect to home when logout succeeds.
|
||||
onLogout(historyPush);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -60,17 +59,21 @@ const Topbar = (props, context) => {
|
|||
|
||||
Topbar.defaultProps = { user: null };
|
||||
|
||||
const { bool, object, func, any } = PropTypes;
|
||||
const { bool, object, func } = PropTypes;
|
||||
|
||||
Topbar.propTypes = { isAuthenticated: bool.isRequired, user: object, onLogout: func.isRequired };
|
||||
|
||||
Topbar.contextTypes = { router: any };
|
||||
Topbar.propTypes = {
|
||||
isAuthenticated: bool.isRequired,
|
||||
user: object,
|
||||
onLogout: func.isRequired,
|
||||
// history.push prop from withRouter
|
||||
push: func.isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
isAuthenticated: state.Auth.isAuthenticated,
|
||||
user: state.Auth.user,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = dispatch => ({ onLogout: router => dispatch(logout(router)) });
|
||||
const mapDispatchToProps = dispatch => ({ onLogout: historyPush => dispatch(logout(historyPush)) });
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(Topbar);
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(withRouter(Topbar));
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ export const login = (email, password) => ({ type: LOGIN_REQUEST, payload: { ema
|
|||
export const loginSuccess = user => ({ type: LOGIN_SUCCESS, payload: user });
|
||||
export const loginError = error => ({ type: LOGIN_ERROR, payload: error, error: true });
|
||||
|
||||
export const logout = router => ({ type: LOGOUT_REQUEST, payload: { router } });
|
||||
export const logout = historyPush => ({ type: LOGOUT_REQUEST, payload: { historyPush } });
|
||||
export const logoutSuccess = () => ({ type: LOGOUT_SUCCESS });
|
||||
export const logoutError = error => ({ type: LOGOUT_ERROR, payload: error, error: true });
|
||||
|
||||
|
|
@ -77,11 +77,11 @@ export function* callLogin(action, sdk) {
|
|||
|
||||
export function* callLogout(action, sdk) {
|
||||
const { payload } = action;
|
||||
const { router } = payload;
|
||||
const { historyPush } = payload;
|
||||
try {
|
||||
yield call(sdk.logout);
|
||||
yield put(logoutSuccess());
|
||||
yield call(router.transitionTo, '/');
|
||||
yield call(historyPush, '/');
|
||||
} catch (e) {
|
||||
yield put(logoutError(e));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,28 +93,28 @@ describe('Auth duck', () => {
|
|||
describe('logout worker', () => {
|
||||
it('should redirect to root after logout', () => {
|
||||
const sdk = { logout: jest.fn() };
|
||||
const router = { transitionTo: jest.fn() };
|
||||
const action = logout(router);
|
||||
const historyPush = jest.fn();
|
||||
const action = logout(historyPush);
|
||||
const worker = callLogout(action, sdk);
|
||||
expect(worker.next()).toEqual({ done: false, value: call(sdk.logout) });
|
||||
expect(worker.next()).toEqual({ done: false, value: put(logoutSuccess()) });
|
||||
expect(worker.next()).toEqual({ done: false, value: call(router.transitionTo, '/') });
|
||||
expect(worker.next()).toEqual({ done: false, value: call(historyPush, '/') });
|
||||
expect(worker.next().done).toEqual(true);
|
||||
expect(sdk.logout).not.toHaveBeenCalled();
|
||||
expect(router.transitionTo).not.toHaveBeenCalled();
|
||||
expect(historyPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not redirect if logout fails', () => {
|
||||
const sdk = { logout: jest.fn() };
|
||||
const router = { transitionTo: jest.fn() };
|
||||
const action = logout(router);
|
||||
const historyPush = jest.fn();
|
||||
const action = logout(historyPush);
|
||||
const worker = callLogout(action, sdk);
|
||||
expect(worker.next()).toEqual({ done: false, value: call(sdk.logout) });
|
||||
const error = new Error('Test logout error');
|
||||
expect(worker.throw(error)).toEqual({ done: false, value: put(logoutError(error)) });
|
||||
expect(worker.next().done).toEqual(true);
|
||||
expect(sdk.logout).not.toHaveBeenCalled();
|
||||
expect(router.transitionTo).not.toHaveBeenCalled();
|
||||
expect(historyPush).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -140,9 +140,9 @@ describe('Auth duck', () => {
|
|||
|
||||
it('calls logout', () => {
|
||||
const sdk = { logout: jest.fn() };
|
||||
const router = { transitionTo: jest.fn() };
|
||||
const historyPush = jest.fn();
|
||||
const watcher = watchAuth(sdk);
|
||||
const logoutAction = logout(router);
|
||||
const logoutAction = logout(historyPush);
|
||||
const takeLoginOrLogout = take([LOGIN_REQUEST, LOGOUT_REQUEST]);
|
||||
const forkLogout = fork(callLogout, logoutAction, sdk);
|
||||
|
||||
|
|
@ -156,7 +156,7 @@ describe('Auth duck', () => {
|
|||
expect(watcher.next().value).toEqual(takeLoginOrLogout);
|
||||
|
||||
expect(sdk.logout).not.toHaveBeenCalled();
|
||||
expect(router.transitionTo).not.toHaveBeenCalled();
|
||||
expect(historyPush).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should cancel login if another login comes', () => {
|
||||
|
|
@ -195,10 +195,10 @@ describe('Auth duck', () => {
|
|||
|
||||
it('should cancel login if a logout comes', () => {
|
||||
const sdk = { login: jest.fn(), logout: jest.fn() };
|
||||
const router = { transitionTo: jest.fn() };
|
||||
const historyPush = jest.fn();
|
||||
const watcher = watchAuth(sdk);
|
||||
const loginAction = login('email', 'password');
|
||||
const logoutAction = logout(router);
|
||||
const logoutAction = logout(historyPush);
|
||||
const task = createMockTask();
|
||||
const takeLoginOrLogout = take([LOGIN_REQUEST, LOGOUT_REQUEST]);
|
||||
const forkLogin = fork(callLogin, loginAction, sdk);
|
||||
|
|
@ -226,7 +226,7 @@ describe('Auth duck', () => {
|
|||
|
||||
expect(sdk.login).not.toHaveBeenCalled();
|
||||
expect(sdk.logout).not.toHaveBeenCalled();
|
||||
expect(router.transitionTo).not.toHaveBeenCalled();
|
||||
expect(historyPush).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,10 +1,6 @@
|
|||
import React from 'react';
|
||||
import { find } from 'lodash';
|
||||
import { Redirect } from 'react-router';
|
||||
|
||||
// This will change to `matchPath` soonish
|
||||
import matchPattern from 'react-router/matchPattern';
|
||||
|
||||
import { Redirect, matchPath } from 'react-router-dom';
|
||||
import pathToRegexp from 'path-to-regexp';
|
||||
import {
|
||||
AuthenticationPage,
|
||||
|
|
@ -31,33 +27,38 @@ import {
|
|||
const RedirectLandingPage = () => <Redirect to="/" />;
|
||||
|
||||
const routesConfiguration = [
|
||||
{ pattern: '/', exactly: true, name: 'LandingPage', component: LandingPage },
|
||||
{
|
||||
pattern: '/',
|
||||
exactly: true,
|
||||
name: 'LandingPage',
|
||||
component: match => <LandingPage {...match} />,
|
||||
},
|
||||
{
|
||||
pattern: '/s',
|
||||
exactly: true,
|
||||
name: 'SearchPage',
|
||||
component: SearchPage,
|
||||
component: match => <SearchPage {...match} />,
|
||||
loadData: SearchPage ? SearchPage.loadData : null,
|
||||
routes: [
|
||||
{
|
||||
pattern: '/s/filters',
|
||||
exactly: true,
|
||||
name: 'SearchFiltersPage',
|
||||
component: props => <SearchPage {...props} tab="filters" />,
|
||||
component: match => <SearchPage {...match} tab="filters" />,
|
||||
loadData: SearchPage ? SearchPage.loadData : null,
|
||||
},
|
||||
{
|
||||
pattern: '/s/listings',
|
||||
exactly: true,
|
||||
name: 'SearchListingsPage',
|
||||
component: props => <SearchPage {...props} tab="listings" />,
|
||||
component: match => <SearchPage {...match} tab="listings" />,
|
||||
loadData: SearchPage ? SearchPage.loadData : null,
|
||||
},
|
||||
{
|
||||
pattern: '/s/map',
|
||||
exactly: true,
|
||||
name: 'SearchMapPage',
|
||||
component: props => <SearchPage {...props} tab="map" />,
|
||||
component: match => <SearchPage {...match} tab="map" />,
|
||||
loadData: SearchPage ? SearchPage.loadData : null,
|
||||
},
|
||||
],
|
||||
|
|
@ -68,7 +69,12 @@ const routesConfiguration = [
|
|||
name: 'ListingBasePage',
|
||||
component: RedirectLandingPage,
|
||||
routes: [
|
||||
{ pattern: '/l/:slug/:id', exactly: true, name: 'ListingPage', component: ListingPage },
|
||||
{
|
||||
pattern: '/l/:slug/:id',
|
||||
exactly: true,
|
||||
name: 'ListingPage',
|
||||
component: match => <ListingPage {...match} />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
@ -81,14 +87,14 @@ const routesConfiguration = [
|
|||
pattern: '/u/:displayName',
|
||||
exactly: true,
|
||||
name: 'ProfilePage',
|
||||
component: ProfilePage,
|
||||
component: match => <ProfilePage {...match} />,
|
||||
routes: [
|
||||
{
|
||||
pattern: '/u/:displayName/edit',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'EditProfilePage',
|
||||
component: EditProfilePage,
|
||||
component: match => <EditProfilePage {...match} />,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -104,7 +110,7 @@ const routesConfiguration = [
|
|||
pattern: '/checkout/:listingId',
|
||||
exactly: true,
|
||||
name: 'CheckoutPage',
|
||||
component: CheckoutPage,
|
||||
component: match => <CheckoutPage {...match} />,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -112,61 +118,66 @@ const routesConfiguration = [
|
|||
pattern: '/login',
|
||||
exactly: true,
|
||||
name: 'LogInPage',
|
||||
component: props => <AuthenticationPage {...props} tab="login" />,
|
||||
component: match => <AuthenticationPage {...match} tab="login" />,
|
||||
},
|
||||
{
|
||||
pattern: '/signup',
|
||||
exactly: true,
|
||||
name: 'SignUpPage',
|
||||
component: props => <AuthenticationPage {...props} tab="signup" />,
|
||||
component: match => <AuthenticationPage {...match} tab="signup" />,
|
||||
},
|
||||
{
|
||||
pattern: '/password',
|
||||
exactly: true,
|
||||
name: 'PasswordPage',
|
||||
component: match => <PasswordForgottenPage {...match} />,
|
||||
},
|
||||
{ pattern: '/password', exactly: true, name: 'PasswordPage', component: PasswordForgottenPage },
|
||||
{
|
||||
pattern: '/password/forgotten',
|
||||
exactly: true,
|
||||
name: 'PasswordForgottenPage',
|
||||
component: PasswordForgottenPage,
|
||||
component: match => <PasswordForgottenPage {...match} />,
|
||||
},
|
||||
{
|
||||
pattern: '/password/change',
|
||||
exactly: true,
|
||||
name: 'PasswordChangePage',
|
||||
component: PasswordChangePage,
|
||||
component: match => <PasswordChangePage {...match} />,
|
||||
},
|
||||
{
|
||||
pattern: '/orders',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'OrdersPage',
|
||||
component: props => <InboxPage {...props} filter="orders" />,
|
||||
component: match => <InboxPage {...match} filter="orders" />,
|
||||
},
|
||||
{
|
||||
pattern: '/sales',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'SalesPage',
|
||||
component: props => <InboxPage {...props} filter="sales" />,
|
||||
component: match => <InboxPage {...match} filter="sales" />,
|
||||
},
|
||||
{
|
||||
pattern: '/order/:id',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'OrderPage',
|
||||
component: () => <Redirect to="/" />,
|
||||
component: RedirectLandingPage,
|
||||
routes: [
|
||||
{
|
||||
pattern: '/order/:id/details',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'OrderDetailsPage',
|
||||
component: props => <OrderPage {...props} tab="details" />,
|
||||
component: match => <OrderPage {...match} tab="details" />,
|
||||
},
|
||||
{
|
||||
pattern: '/order/:id/discussion',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'OrderDiscussionPage',
|
||||
component: props => <OrderPage {...props} tab="discussion" />,
|
||||
component: match => <OrderPage {...match} tab="discussion" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -175,21 +186,21 @@ const routesConfiguration = [
|
|||
auth: true,
|
||||
exactly: true,
|
||||
name: 'SalePage',
|
||||
component: props => <SalesConversationPage {...props} tab="discussion" />,
|
||||
component: match => <SalesConversationPage {...match} tab="discussion" />,
|
||||
routes: [
|
||||
{
|
||||
pattern: '/sale/:id/details',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'SaleDetailsPage',
|
||||
component: props => <SalesConversationPage {...props} tab="discussion" />,
|
||||
component: match => <SalesConversationPage {...match} tab="discussion" />,
|
||||
},
|
||||
{
|
||||
pattern: '/sale/:id/discussion',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'SaleDiscussionPage',
|
||||
component: props => <SalesConversationPage {...props} tab="discussion" />,
|
||||
component: match => <SalesConversationPage {...match} tab="discussion" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
@ -198,7 +209,7 @@ const routesConfiguration = [
|
|||
auth: true,
|
||||
exactly: true,
|
||||
name: 'ManageListingsPage',
|
||||
component: ManageListingsPage,
|
||||
component: match => <ManageListingsPage {...match} />,
|
||||
},
|
||||
{
|
||||
pattern: '/account',
|
||||
|
|
@ -212,42 +223,47 @@ const routesConfiguration = [
|
|||
auth: true,
|
||||
exactly: true,
|
||||
name: 'ContactDetailsPage',
|
||||
component: ContactDetailsPage,
|
||||
component: match => <ContactDetailsPage {...match} />,
|
||||
},
|
||||
{
|
||||
pattern: '/account/payout-preferences',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'PayoutPreferencesPage',
|
||||
component: PayoutPreferencesPage,
|
||||
component: match => <PayoutPreferencesPage {...match} />,
|
||||
},
|
||||
{
|
||||
pattern: '/account/security',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'SecurityPage',
|
||||
component: SecurityPage,
|
||||
component: match => <SecurityPage {...match} />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{ pattern: '/styleguide', exactly: true, name: 'Styleguide', component: StyleguidePage },
|
||||
{
|
||||
pattern: '/styleguide',
|
||||
exactly: true,
|
||||
name: 'Styleguide',
|
||||
component: match => <StyleguidePage {...match} />,
|
||||
},
|
||||
{
|
||||
pattern: '/styleguide/:component',
|
||||
exactly: true,
|
||||
name: 'StyleguideComponent',
|
||||
component: StyleguidePage,
|
||||
component: match => <StyleguidePage {...match} />,
|
||||
},
|
||||
{
|
||||
pattern: '/styleguide/:component/:example',
|
||||
exactly: true,
|
||||
name: 'StyleguideComponentExample',
|
||||
component: StyleguidePage,
|
||||
component: match => <StyleguidePage {...match} />,
|
||||
},
|
||||
{
|
||||
pattern: '/styleguide/:component/:example/:type',
|
||||
exactly: true,
|
||||
name: 'StyleguideComponentExampleRaw',
|
||||
component: StyleguidePage,
|
||||
component: match => <StyleguidePage {...match} />,
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -286,7 +302,7 @@ const matchRoutesToLocation = (routes, location, matchedRoutes = [], params = {}
|
|||
|
||||
routes.forEach(route => {
|
||||
const { exactly = false } = route;
|
||||
const match = !route.pattern ? true : matchPattern(route.pattern, location, exactly);
|
||||
const match = !route.pattern || matchPath(location.pathname, route.pattern, { exact: exactly });
|
||||
|
||||
if (match) {
|
||||
matched.push(route);
|
||||
|
|
@ -297,7 +313,12 @@ const matchRoutesToLocation = (routes, location, matchedRoutes = [], params = {}
|
|||
}
|
||||
|
||||
if (route.routes) {
|
||||
const { matchedRoutes: subRouteMatches, params: subRouteParams } = matchRoutesToLocation(route.routes, location, matched, parameters);
|
||||
const { matchedRoutes: subRouteMatches, params: subRouteParams } = matchRoutesToLocation(
|
||||
route.routes,
|
||||
location,
|
||||
matched,
|
||||
parameters,
|
||||
);
|
||||
matched = matched.concat(subRouteMatches);
|
||||
parameters = { ...parameters, ...subRouteParams };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import renderer from 'react-test-renderer';
|
|||
import { shallow } from 'enzyme';
|
||||
import toJson from 'enzyme-to-json';
|
||||
import { IntlProvider } from 'react-intl';
|
||||
import { BrowserRouter } from 'react-router';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import { Provider } from 'react-redux';
|
||||
import configureStore from '../store';
|
||||
|
||||
|
|
|
|||
31
yarn.lock
31
yarn.lock
|
|
@ -2707,7 +2707,7 @@ hide-powered-by@1.0.0:
|
|||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hide-powered-by/-/hide-powered-by-1.0.0.tgz#4a85ad65881f62857fc70af7174a1184dccce32b"
|
||||
|
||||
history@^4.3.0:
|
||||
history@^4.5.1:
|
||||
version "4.5.1"
|
||||
resolved "https://registry.yarnpkg.com/history/-/history-4.5.1.tgz#44935a51021e3b8e67ebac267a35675732aba569"
|
||||
dependencies:
|
||||
|
|
@ -3833,7 +3833,7 @@ longest@^1.0.1:
|
|||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097"
|
||||
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0:
|
||||
loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.3.1.tgz#d1a8ad33fa9ce0e713d65fdd0ac8b748d478c848"
|
||||
dependencies:
|
||||
|
|
@ -5064,7 +5064,7 @@ qs@^6.3.0, qs@~6.3.0:
|
|||
version "6.3.0"
|
||||
resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.0.tgz#f403b264f23bc01228c74131b407f18d5ea5d442"
|
||||
|
||||
query-string@4.2.3, query-string@^4.1.0:
|
||||
query-string@^4.1.0:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/query-string/-/query-string-4.2.3.tgz#9f27273d207a25a8ee4c7b8c74dcd45d556db822"
|
||||
dependencies:
|
||||
|
|
@ -5110,12 +5110,6 @@ react-addons-test-utils@^15.4.2:
|
|||
fbjs "^0.8.4"
|
||||
object-assign "^4.1.0"
|
||||
|
||||
react-broadcast@^0.1.2:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/react-broadcast/-/react-broadcast-0.1.2.tgz#950de63578a2af399a396067a617af7402182330"
|
||||
dependencies:
|
||||
invariant "^2.2.1"
|
||||
|
||||
react-dev-utils@^0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-0.4.2.tgz#ba6fae581fe945a2fc402e9b27c71fda4f62f6a1"
|
||||
|
|
@ -5163,14 +5157,21 @@ react-redux@^5.0.2:
|
|||
lodash-es "^4.2.0"
|
||||
loose-envify "^1.1.0"
|
||||
|
||||
react-router@4.0.0-alpha.6:
|
||||
version "4.0.0-alpha.6"
|
||||
resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.0.0-alpha.6.tgz#239fcf9a6ba7997021022c9b51d72d370f7b6bf4"
|
||||
react-router-dom@4.0.0-beta.6:
|
||||
version "4.0.0-beta.6"
|
||||
resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.0.0-beta.6.tgz#bdbd8f2fea3def52970735778db03b24cc082a02"
|
||||
dependencies:
|
||||
history "^4.3.0"
|
||||
history "^4.5.1"
|
||||
react-router "^4.0.0-beta.6"
|
||||
|
||||
react-router@^4.0.0-beta.6:
|
||||
version "4.0.0-beta.6"
|
||||
resolved "https://registry.yarnpkg.com/react-router/-/react-router-4.0.0-beta.6.tgz#561ac0bf1929960813bf201319ff85d821d5547b"
|
||||
dependencies:
|
||||
history "^4.5.1"
|
||||
invariant "^2.2.2"
|
||||
loose-envify "^1.3.1"
|
||||
path-to-regexp "^1.5.3"
|
||||
query-string "4.2.3"
|
||||
react-broadcast "^0.1.2"
|
||||
|
||||
react-side-effect@^1.1.0:
|
||||
version "1.1.0"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue