mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-27 19:42:11 +10:00
Merge pull request #484 from sharetribe/refactor-routesconfiguration
Refactor routesconfiguration
This commit is contained in:
commit
d5bb389dc6
56 changed files with 706 additions and 1052 deletions
|
|
@ -4,7 +4,7 @@ const log = require('./log');
|
|||
|
||||
exports.loadData = function(requestUrl, sdk) {
|
||||
const { pathname, query } = url.parse(requestUrl);
|
||||
const matchedRoutes = matchPathname(pathname, routeConfiguration);
|
||||
const matchedRoutes = matchPathname(pathname, routeConfiguration());
|
||||
|
||||
const store = configureStore(sdk);
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import React, { Component, PropTypes } from 'react';
|
|||
import { compose } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { Switch, Route, withRouter } from 'react-router-dom';
|
||||
import routeConfiguration from './routeConfiguration';
|
||||
import { NotFoundPage } from './containers';
|
||||
import { NamedRedirect } from './components';
|
||||
import { withFlattenedRoutes } from './util/contextHelpers';
|
||||
import * as propTypes from './util/propTypes';
|
||||
import * as log from './util/log';
|
||||
|
||||
const { bool, arrayOf, object, func, shape, string, any } = PropTypes;
|
||||
const { bool, object, func, shape, string } = PropTypes;
|
||||
|
||||
const canShowComponent = props => {
|
||||
const { isAuthenticated, route } = props;
|
||||
|
|
@ -49,18 +49,14 @@ class RouteComponentRenderer extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { route, match, location, staticContext, flattenedRoutes } = this.props;
|
||||
const { route, match, location, staticContext } = this.props;
|
||||
const { component: RouteComponent, authPage = 'SignupPage' } = route;
|
||||
const canShow = canShowComponent(this.props);
|
||||
if (!canShow) {
|
||||
staticContext.unauthorized = true;
|
||||
}
|
||||
return canShow
|
||||
? <RouteComponent
|
||||
params={match.params}
|
||||
location={location}
|
||||
flattenedRoutes={flattenedRoutes}
|
||||
/>
|
||||
? <RouteComponent params={match.params} location={location} />
|
||||
: <NamedRedirect
|
||||
name={authPage}
|
||||
state={{ from: `${location.pathname}${location.search}${location.hash}` }}
|
||||
|
|
@ -71,7 +67,6 @@ class RouteComponentRenderer extends Component {
|
|||
RouteComponentRenderer.propTypes = {
|
||||
isAuthenticated: bool.isRequired, // eslint-disable-line react/no-unused-prop-types
|
||||
logoutInProgress: bool.isRequired, // eslint-disable-line react/no-unused-prop-types
|
||||
flattenedRoutes: any.isRequired,
|
||||
route: propTypes.route.isRequired,
|
||||
match: shape({
|
||||
params: object.isRequired,
|
||||
|
|
@ -86,7 +81,7 @@ RouteComponentRenderer.propTypes = {
|
|||
};
|
||||
|
||||
const Routes = props => {
|
||||
const { isAuthenticated, logoutInProgress, flattenedRoutes, staticContext, dispatch } = props;
|
||||
const { isAuthenticated, logoutInProgress, staticContext, dispatch } = props;
|
||||
|
||||
const toRouteComponent = route => {
|
||||
const renderProps = {
|
||||
|
|
@ -95,7 +90,6 @@ const Routes = props => {
|
|||
route,
|
||||
staticContext,
|
||||
dispatch,
|
||||
flattenedRoutes,
|
||||
};
|
||||
return (
|
||||
<Route
|
||||
|
|
@ -115,7 +109,7 @@ const Routes = props => {
|
|||
|
||||
return (
|
||||
<Switch>
|
||||
{flattenedRoutes.map(toRouteComponent)}
|
||||
{routeConfiguration().map(toRouteComponent)}
|
||||
<Route component={NotFoundPage} />
|
||||
</Switch>
|
||||
);
|
||||
|
|
@ -127,9 +121,6 @@ Routes.propTypes = {
|
|||
isAuthenticated: bool.isRequired,
|
||||
logoutInProgress: bool.isRequired,
|
||||
|
||||
// from withFlattenedRoutes
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
|
||||
// from withRouter
|
||||
staticContext: object,
|
||||
|
||||
|
|
@ -148,4 +139,4 @@ const mapStateToProps = state => {
|
|||
// lifecycle hook.
|
||||
//
|
||||
// See: https://github.com/ReactTraining/react-router/issues/4671
|
||||
export default compose(withRouter, connect(mapStateToProps), withFlattenedRoutes)(Routes);
|
||||
export default compose(withRouter, connect(mapStateToProps))(Routes);
|
||||
|
|
|
|||
21
src/app.js
21
src/app.js
|
|
@ -7,23 +7,17 @@ import { IntlProvider, addLocaleData } from 'react-intl';
|
|||
import en from 'react-intl/locale-data/en';
|
||||
import configureStore from './store';
|
||||
import Routes from './Routes';
|
||||
import { RoutesProvider } from './components';
|
||||
import routesConfiguration from './routesConfiguration';
|
||||
import { flattenRoutes } from './util/routes';
|
||||
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}>
|
||||
<Provider store={store}>
|
||||
<RoutesProvider flattenedRoutes={flattenedRoutes}>
|
||||
<BrowserRouter>
|
||||
<Routes />
|
||||
</BrowserRouter>
|
||||
</RoutesProvider>
|
||||
<BrowserRouter>
|
||||
<Routes />
|
||||
</BrowserRouter>
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
|
|
@ -36,15 +30,12 @@ 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}>
|
||||
<Provider store={store}>
|
||||
<RoutesProvider flattenedRoutes={flattenedRoutes}>
|
||||
<StaticRouter location={url} context={context}>
|
||||
<Routes />
|
||||
</StaticRouter>
|
||||
</RoutesProvider>
|
||||
<StaticRouter location={url} context={context}>
|
||||
<Routes />
|
||||
</StaticRouter>
|
||||
</Provider>
|
||||
</IntlProvider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,4 @@
|
|||
import { createListing } from '../../util/test-data';
|
||||
import { flattenRoutes } from '../../util/routes';
|
||||
import routesConfiguration from '../../routesConfiguration';
|
||||
import EditListingWizard from './EditListingWizard';
|
||||
|
||||
const noop = () => null;
|
||||
|
|
@ -16,7 +14,6 @@ export const NoPhotos = {
|
|||
},
|
||||
fetchInProgress: false,
|
||||
newListingCreated: false,
|
||||
flattenedRoutes: flattenRoutes(routesConfiguration),
|
||||
history: { push: noop },
|
||||
images: [],
|
||||
listing: createListing('listing1'),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { injectIntl, intlShape } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { ensureListing } from '../../util/data';
|
||||
import { createResourceLocatorString } from '../../util/routes';
|
||||
|
|
@ -76,7 +77,6 @@ const EditListingWizard = props => {
|
|||
errors,
|
||||
fetchInProgress,
|
||||
newListingCreated,
|
||||
flattenedRoutes,
|
||||
history,
|
||||
images,
|
||||
listing,
|
||||
|
|
@ -142,7 +142,7 @@ const EditListingWizard = props => {
|
|||
const pathParams = tabParams(LOCATION);
|
||||
// Redirect to location tab
|
||||
history.push(
|
||||
createResourceLocatorString('EditListingPage', flattenedRoutes, pathParams, {})
|
||||
createResourceLocatorString('EditListingPage', routeConfiguration(), pathParams, {})
|
||||
);
|
||||
} else {
|
||||
update(DESCRIPTION, values);
|
||||
|
|
@ -175,7 +175,7 @@ const EditListingWizard = props => {
|
|||
const pathParams = tabParams(PRICING);
|
||||
// Redirect to pricing tab
|
||||
history.push(
|
||||
createResourceLocatorString('EditListingPage', flattenedRoutes, pathParams, {})
|
||||
createResourceLocatorString('EditListingPage', routeConfiguration(), pathParams, {})
|
||||
);
|
||||
} else {
|
||||
update(LOCATION, updateValues);
|
||||
|
|
@ -200,7 +200,7 @@ const EditListingWizard = props => {
|
|||
const pathParams = tabParams(PHOTOS);
|
||||
// Redirect to photos tab
|
||||
history.push(
|
||||
createResourceLocatorString('EditListingPage', flattenedRoutes, pathParams, {})
|
||||
createResourceLocatorString('EditListingPage', routeConfiguration(), pathParams, {})
|
||||
);
|
||||
} else {
|
||||
update(PRICING, values);
|
||||
|
|
@ -253,7 +253,7 @@ EditListingWizard.defaultProps = {
|
|||
updatedTab: null,
|
||||
};
|
||||
|
||||
const { array, arrayOf, bool, func, object, oneOf, shape, string } = PropTypes;
|
||||
const { array, bool, func, object, oneOf, shape, string } = PropTypes;
|
||||
|
||||
EditListingWizard.propTypes = {
|
||||
className: string,
|
||||
|
|
@ -271,7 +271,6 @@ EditListingWizard.propTypes = {
|
|||
}).isRequired,
|
||||
fetchInProgress: bool.isRequired,
|
||||
newListingCreated: bool.isRequired,
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
history: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
|
|
|
|||
|
|
@ -3,13 +3,13 @@ exports[`FilterPanel matches snapshot 1`] = `
|
|||
<h1>
|
||||
Filters
|
||||
</h1>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
name="SearchListingsPage">
|
||||
See studios
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
</NamedLink>
|
||||
<NamedLink
|
||||
name="SearchListingsPage">
|
||||
X
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { IconSearch, Button } from '../../components';
|
||||
import { LocationSearchForm } from '../../containers';
|
||||
import { stringify } from '../../util/urlHelpers';
|
||||
import { createResourceLocatorString } from '../../util/routes';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { IconSearch, Button } from '../../components';
|
||||
import { LocationSearchForm } from '../../containers';
|
||||
|
||||
import css from './HeroSection.css';
|
||||
|
||||
const HeroSection = props => {
|
||||
const { rootClassName, className, flattenedRoutes, history, location } = props;
|
||||
const { rootClassName, className, history, location } = props;
|
||||
|
||||
const handleMobileSearchClick = () => {
|
||||
const params = { mobilesearch: 'open' };
|
||||
|
|
@ -22,7 +22,7 @@ const HeroSection = props => {
|
|||
const { search, selectedPlace } = values.location;
|
||||
const { origin, bounds, country } = selectedPlace;
|
||||
const searchParams = { address: search, origin, bounds, country };
|
||||
history.push(createResourceLocatorString('SearchPage', flattenedRoutes, {}, searchParams));
|
||||
history.push(createResourceLocatorString('SearchPage', routeConfiguration(), {}, searchParams));
|
||||
};
|
||||
|
||||
const classes = classNames(rootClassName || css.root, className);
|
||||
|
|
@ -46,13 +46,12 @@ const HeroSection = props => {
|
|||
|
||||
HeroSection.defaultProps = { rootClassName: null, className: null };
|
||||
|
||||
const { string, shape, func, arrayOf } = PropTypes;
|
||||
const { string, shape, func } = PropTypes;
|
||||
|
||||
HeroSection.propTypes = {
|
||||
rootClassName: string,
|
||||
className: string,
|
||||
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
history: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ describe('HeroSection', () => {
|
|||
it('matches snapshot', () => {
|
||||
window.google = { maps: {} };
|
||||
const heroProps = {
|
||||
flattenedRoutes: [],
|
||||
history: { push: noop },
|
||||
location: { search: '' },
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
exports[`ListingCard matches snapshot 1`] = `
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
className=""
|
||||
name="ListingPage"
|
||||
params={
|
||||
|
|
@ -58,5 +58,5 @@ exports[`ListingCard matches snapshot 1`] = `
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -22,7 +22,6 @@ export const ManageListingCardWrapped = {
|
|||
onCloseListing: noop,
|
||||
onOpenListing: noop,
|
||||
onToggleMenu: noop,
|
||||
flattenedRoutes: [],
|
||||
history: { push: noop },
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import { compose } from 'redux';
|
|||
import { withRouter } from 'react-router-dom';
|
||||
import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { formatMoney } from '../../util/currency';
|
||||
import { ensureListing } from '../../util/data';
|
||||
import { createSlug } from '../../util/urlHelpers';
|
||||
import { withFlattenedRoutes } from '../../util/contextHelpers';
|
||||
import { createResourceLocatorString } from '../../util/routes';
|
||||
import {
|
||||
InlineTextButton,
|
||||
|
|
@ -46,19 +46,18 @@ const priceData = (price, intl) => {
|
|||
return {};
|
||||
};
|
||||
|
||||
const createURL = (flattenedRoutes, listing) => {
|
||||
const createURL = (routes, listing) => {
|
||||
const id = listing.id.uuid;
|
||||
const slug = createSlug(listing.attributes.title);
|
||||
const pathParams = { id, slug, type: 'edit', tab: 'description' };
|
||||
|
||||
return createResourceLocatorString('EditListingPage', flattenedRoutes, pathParams, {});
|
||||
return createResourceLocatorString('EditListingPage', routes, pathParams, {});
|
||||
};
|
||||
|
||||
export const ManageListingCardComponent = props => {
|
||||
const {
|
||||
className,
|
||||
rootClassName,
|
||||
flattenedRoutes,
|
||||
hasClosingError,
|
||||
hasOpeningError,
|
||||
history,
|
||||
|
|
@ -232,7 +231,7 @@ export const ManageListingCardComponent = props => {
|
|||
onClick={event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
history.push(createURL(flattenedRoutes, listing));
|
||||
history.push(createURL(routeConfiguration(), listing));
|
||||
}}
|
||||
>
|
||||
<FormattedMessage id="ManageListingCard.edit" />
|
||||
|
|
@ -248,7 +247,7 @@ ManageListingCardComponent.defaultProps = {
|
|||
actionsInProgressListingId: null,
|
||||
};
|
||||
|
||||
const { arrayOf, bool, func, shape, string } = PropTypes;
|
||||
const { bool, func, shape, string } = PropTypes;
|
||||
|
||||
ManageListingCardComponent.propTypes = {
|
||||
className: string,
|
||||
|
|
@ -263,13 +262,10 @@ ManageListingCardComponent.propTypes = {
|
|||
onOpenListing: func.isRequired,
|
||||
onToggleMenu: func.isRequired,
|
||||
|
||||
// from withFlattenedRoutes
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
|
||||
// from withRouter
|
||||
history: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
};
|
||||
|
||||
export default compose(withRouter, withFlattenedRoutes, injectIntl)(ManageListingCardComponent);
|
||||
export default compose(withRouter, injectIntl)(ManageListingCardComponent);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ describe('ManageListingCard', () => {
|
|||
it('matches snapshot', () => {
|
||||
const tree = renderShallow(
|
||||
<ManageListingCardComponent
|
||||
flattenedRoutes={[]}
|
||||
history={{ push: noop }}
|
||||
listing={createListing('listing1')}
|
||||
intl={fakeIntl}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
exports[`ManageListingCard matches snapshot 1`] = `
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
className=""
|
||||
name="ListingPage"
|
||||
params={
|
||||
|
|
@ -111,5 +111,5 @@ exports[`ManageListingCard matches snapshot 1`] = `
|
|||
values={Object {}} />
|
||||
</button>
|
||||
</div>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ exports[`MapPanel matches snapshot 1`] = `
|
|||
Map
|
||||
</div>
|
||||
<div />
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
name="SearchFiltersPage">
|
||||
Filters
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
</NamedLink>
|
||||
<NamedLink
|
||||
name="SearchListingsPage">
|
||||
X
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
* This component wraps React-Router's Link by providing name-based routing.
|
||||
*
|
||||
* The `name` prop should match a route in the flattened
|
||||
* routesConfiguration passed in context by the RoutesProvider
|
||||
* routeConfiguration passed in context by the RoutesProvider
|
||||
* component. The `params` props is the route params for the route
|
||||
* path of the given route name.
|
||||
*
|
||||
|
|
@ -20,17 +20,15 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { pathByRouteName } from '../../util/routes';
|
||||
import { withFlattenedRoutes } from '../../util/contextHelpers';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
|
||||
export const NamedLinkComponent = props => {
|
||||
const { name, params, flattenedRoutes, title } = props;
|
||||
const { name, params, title } = props;
|
||||
|
||||
// Link props
|
||||
const { to, children } = props;
|
||||
|
||||
const pathname = pathByRouteName(name, flattenedRoutes, params);
|
||||
const pathname = pathByRouteName(name, routeConfiguration(), params);
|
||||
const { match } = props;
|
||||
const active = match.url && match.url === pathname;
|
||||
|
||||
|
|
@ -45,7 +43,7 @@ export const NamedLinkComponent = props => {
|
|||
return <Link to={{ pathname, ...to }} {...aElemProps}>{children}</Link>;
|
||||
};
|
||||
|
||||
const { arrayOf, object, string, shape, any } = PropTypes;
|
||||
const { object, string, shape, any } = PropTypes;
|
||||
|
||||
NamedLinkComponent.defaultProps = {
|
||||
params: {},
|
||||
|
|
@ -62,7 +60,7 @@ NamedLinkComponent.defaultProps = {
|
|||
NamedLinkComponent.displayName = 'NamedLink';
|
||||
|
||||
NamedLinkComponent.propTypes = {
|
||||
// name of the route in routesConfiguration
|
||||
// name of the route in routeConfiguration
|
||||
name: string.isRequired,
|
||||
// params object for the named route
|
||||
params: object,
|
||||
|
|
@ -76,11 +74,11 @@ NamedLinkComponent.propTypes = {
|
|||
activeClassName: string,
|
||||
title: string,
|
||||
|
||||
// from withFlattenedRoutes
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
|
||||
// from withRouter
|
||||
match: object,
|
||||
};
|
||||
|
||||
export default withFlattenedRoutes(withRouter(NamedLinkComponent));
|
||||
const NamedLink = withRouter(NamedLinkComponent);
|
||||
NamedLink.displayName = 'NamedLink';
|
||||
|
||||
export default NamedLink;
|
||||
|
|
|
|||
|
|
@ -1,33 +1,27 @@
|
|||
import React from 'react';
|
||||
import { RoutesProvider } from '../index';
|
||||
import { renderShallow, renderDeep } from '../../util/test-helpers';
|
||||
import NamedLink, { NamedLinkComponent } from './NamedLink';
|
||||
|
||||
describe('NamedLinkComponent', () => {
|
||||
it('should mark the link as active if the current URL matches', () => {
|
||||
const routesConf = [
|
||||
{ path: '/a', name: 'APage', component: () => null },
|
||||
{ path: '/b', name: 'BPage', component: () => null },
|
||||
];
|
||||
const activeClassName = 'my-active-class';
|
||||
const aProps = {
|
||||
name: 'APage',
|
||||
const landingPageProps = {
|
||||
name: 'LandingPage',
|
||||
activeClassName,
|
||||
flattenedRoutes: routesConf,
|
||||
match: { url: '/a' },
|
||||
match: { url: '/' },
|
||||
};
|
||||
const bProps = {
|
||||
name: 'BPage',
|
||||
const searchPageProps = {
|
||||
name: 'SearchPage',
|
||||
activeClassName,
|
||||
flattenedRoutes: routesConf,
|
||||
match: { url: '/a' },
|
||||
match: { url: '/' },
|
||||
};
|
||||
const tree = renderDeep(
|
||||
<div>
|
||||
<NamedLinkComponent {...aProps}>link to a</NamedLinkComponent>
|
||||
<NamedLinkComponent {...bProps}>link to b</NamedLinkComponent>
|
||||
<NamedLinkComponent {...landingPageProps}>link to a</NamedLinkComponent>
|
||||
<NamedLinkComponent {...searchPageProps}>link to b</NamedLinkComponent>
|
||||
</div>
|
||||
);
|
||||
|
||||
const aLink = tree.children[0];
|
||||
const bLink = tree.children[1];
|
||||
expect(aLink.type).toEqual('a');
|
||||
|
|
@ -40,16 +34,11 @@ describe('NamedLinkComponent', () => {
|
|||
describe('NamedLink', () => {
|
||||
it('should contain correct link', () => {
|
||||
const id = 12;
|
||||
const routesConf = [
|
||||
{ path: '/somepage/:id', name: 'SomePage', component: () => <div>blaa</div> },
|
||||
];
|
||||
const tree = renderDeep(
|
||||
<RoutesProvider flattenedRoutes={routesConf}>
|
||||
<NamedLink name="SomePage" params={{ id }}>to SomePage</NamedLink>
|
||||
</RoutesProvider>
|
||||
<NamedLink name="ListingPageCanonical" params={{ id }}>to ListingPage</NamedLink>
|
||||
);
|
||||
expect(tree.type).toEqual('a');
|
||||
expect(tree.props.href).toEqual(`/somepage/${id}`);
|
||||
expect(tree.children).toEqual(['to SomePage']);
|
||||
expect(tree.props.href).toEqual(`/l/${id}`);
|
||||
expect(tree.children).toEqual(['to ListingPage']);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -4,17 +4,16 @@
|
|||
*/
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Redirect } from 'react-router-dom';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { pathByRouteName } from '../../util/routes';
|
||||
import { withFlattenedRoutes } from '../../util/contextHelpers';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
|
||||
const NamedRedirect = props => {
|
||||
const { name, search, state, params, flattenedRoutes, push } = props;
|
||||
const pathname = pathByRouteName(name, flattenedRoutes, params);
|
||||
const { name, search, state, params, push } = props;
|
||||
const pathname = pathByRouteName(name, routeConfiguration(), params);
|
||||
return <Redirect to={{ pathname, search, state }} push={push} />;
|
||||
};
|
||||
|
||||
const { arrayOf, object, string, bool } = PropTypes;
|
||||
const { bool, object, string } = PropTypes;
|
||||
|
||||
NamedRedirect.defaultProps = { search: '', state: {}, push: false, params: {} };
|
||||
|
||||
|
|
@ -24,7 +23,6 @@ NamedRedirect.propTypes = {
|
|||
state: object,
|
||||
push: bool,
|
||||
params: object,
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
};
|
||||
|
||||
export default withFlattenedRoutes(NamedRedirect);
|
||||
export default NamedRedirect;
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ exports[`OrderDetailsPanel accepted matches snapshot 1`] = `
|
|||
id="OrderDetailsPanel.orderAcceptedSubtitle"
|
||||
values={
|
||||
Object {
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -69,7 +69,7 @@ exports[`OrderDetailsPanel accepted matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</span>
|
||||
|
|
@ -452,7 +452,7 @@ exports[`OrderDetailsPanel autorejected matches snapshot 1`] = `
|
|||
id="OrderDetailsPanel.orderAutoRejectedTitle"
|
||||
values={
|
||||
Object {
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -461,7 +461,7 @@ exports[`OrderDetailsPanel autorejected matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</h1>
|
||||
|
|
@ -853,7 +853,7 @@ exports[`OrderDetailsPanel canceled matches snapshot 1`] = `
|
|||
id="OrderDetailsPanel.orderPreauthorizedSubtitle"
|
||||
values={
|
||||
Object {
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -862,7 +862,7 @@ exports[`OrderDetailsPanel canceled matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</span>
|
||||
|
|
@ -1238,7 +1238,7 @@ exports[`OrderDetailsPanel delivered matches snapshot 1`] = `
|
|||
id="OrderDetailsPanel.orderDeliveredTitle"
|
||||
values={
|
||||
Object {
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -1247,7 +1247,7 @@ exports[`OrderDetailsPanel delivered matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</h1>
|
||||
|
|
@ -1639,7 +1639,7 @@ exports[`OrderDetailsPanel preauthorized matches snapshot 1`] = `
|
|||
id="OrderDetailsPanel.orderPreauthorizedSubtitle"
|
||||
values={
|
||||
Object {
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -1648,7 +1648,7 @@ exports[`OrderDetailsPanel preauthorized matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</span>
|
||||
|
|
@ -2024,7 +2024,7 @@ exports[`OrderDetailsPanel rejected matches snapshot 1`] = `
|
|||
id="OrderDetailsPanel.orderRejectedTitle"
|
||||
values={
|
||||
Object {
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -2033,7 +2033,7 @@ exports[`OrderDetailsPanel rejected matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</h1>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ exports[`PaginationLinks should match snapshot with both links disabled 1`] = `
|
|||
</div>
|
||||
<div
|
||||
className="">
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
className="undefined"
|
||||
name="SomePage"
|
||||
params={
|
||||
|
|
@ -23,7 +23,7 @@ exports[`PaginationLinks should match snapshot with both links disabled 1`] = `
|
|||
}
|
||||
}>
|
||||
1
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
</div>
|
||||
<div
|
||||
className="">
|
||||
|
|
@ -36,7 +36,7 @@ exports[`PaginationLinks should match snapshot with both links disabled 1`] = `
|
|||
exports[`PaginationLinks should match snapshot with both links enabled 1`] = `
|
||||
<nav
|
||||
className="">
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
name="SomePage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -51,10 +51,10 @@ exports[`PaginationLinks should match snapshot with both links enabled 1`] = `
|
|||
}>
|
||||
<PrevPageIcon
|
||||
className={null} />
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
<div
|
||||
className="">
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
className=""
|
||||
name="SomePage"
|
||||
params={
|
||||
|
|
@ -69,8 +69,8 @@ exports[`PaginationLinks should match snapshot with both links enabled 1`] = `
|
|||
}
|
||||
}>
|
||||
1
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
</NamedLink>
|
||||
<NamedLink
|
||||
className="undefined"
|
||||
name="SomePage"
|
||||
params={
|
||||
|
|
@ -85,8 +85,8 @@ exports[`PaginationLinks should match snapshot with both links enabled 1`] = `
|
|||
}
|
||||
}>
|
||||
2
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
</NamedLink>
|
||||
<NamedLink
|
||||
className=""
|
||||
name="SomePage"
|
||||
params={
|
||||
|
|
@ -101,9 +101,9 @@ exports[`PaginationLinks should match snapshot with both links enabled 1`] = `
|
|||
}
|
||||
}>
|
||||
3
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
</div>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
name="SomePage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -118,7 +118,7 @@ exports[`PaginationLinks should match snapshot with both links enabled 1`] = `
|
|||
}>
|
||||
<NextPageIcon
|
||||
className={null} />
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
</nav>
|
||||
`;
|
||||
|
||||
|
|
@ -132,7 +132,7 @@ exports[`PaginationLinks should match snapshot with prev disabled and next enabl
|
|||
</div>
|
||||
<div
|
||||
className="">
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
className="undefined"
|
||||
name="SomePage"
|
||||
params={
|
||||
|
|
@ -147,8 +147,8 @@ exports[`PaginationLinks should match snapshot with prev disabled and next enabl
|
|||
}
|
||||
}>
|
||||
1
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
</NamedLink>
|
||||
<NamedLink
|
||||
className=""
|
||||
name="SomePage"
|
||||
params={
|
||||
|
|
@ -163,8 +163,8 @@ exports[`PaginationLinks should match snapshot with prev disabled and next enabl
|
|||
}
|
||||
}>
|
||||
2
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
</NamedLink>
|
||||
<NamedLink
|
||||
className=""
|
||||
name="SomePage"
|
||||
params={
|
||||
|
|
@ -179,9 +179,9 @@ exports[`PaginationLinks should match snapshot with prev disabled and next enabl
|
|||
}
|
||||
}>
|
||||
3
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
</div>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
name="SomePage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -196,14 +196,14 @@ exports[`PaginationLinks should match snapshot with prev disabled and next enabl
|
|||
}>
|
||||
<NextPageIcon
|
||||
className={null} />
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
</nav>
|
||||
`;
|
||||
|
||||
exports[`PaginationLinks should match snapshot with prev enabled and next disabled 1`] = `
|
||||
<nav
|
||||
className="">
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
name="SomePage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -218,10 +218,10 @@ exports[`PaginationLinks should match snapshot with prev enabled and next disabl
|
|||
}>
|
||||
<PrevPageIcon
|
||||
className={null} />
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
<div
|
||||
className="">
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
className=""
|
||||
name="SomePage"
|
||||
params={
|
||||
|
|
@ -236,8 +236,8 @@ exports[`PaginationLinks should match snapshot with prev enabled and next disabl
|
|||
}
|
||||
}>
|
||||
1
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
</NamedLink>
|
||||
<NamedLink
|
||||
className=""
|
||||
name="SomePage"
|
||||
params={
|
||||
|
|
@ -252,8 +252,8 @@ exports[`PaginationLinks should match snapshot with prev enabled and next disabl
|
|||
}
|
||||
}>
|
||||
2
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
</NamedLink>
|
||||
<NamedLink
|
||||
className="undefined"
|
||||
name="SomePage"
|
||||
params={
|
||||
|
|
@ -268,7 +268,7 @@ exports[`PaginationLinks should match snapshot with prev enabled and next disabl
|
|||
}
|
||||
}>
|
||||
3
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
</div>
|
||||
<div
|
||||
className="">
|
||||
|
|
|
|||
|
|
@ -1,22 +0,0 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
|
||||
class RoutesProvider extends Component {
|
||||
getChildContext() {
|
||||
return { flattenedRoutes: this.props.flattenedRoutes };
|
||||
}
|
||||
|
||||
render() {
|
||||
return React.Children.only(this.props.children);
|
||||
}
|
||||
}
|
||||
|
||||
const { arrayOf, node } = PropTypes;
|
||||
|
||||
RoutesProvider.childContextTypes = { flattenedRoutes: arrayOf(propTypes.route).isRequired };
|
||||
|
||||
RoutesProvider.defaultProps = { children: {} };
|
||||
|
||||
RoutesProvider.propTypes = { flattenedRoutes: arrayOf(propTypes.route).isRequired, children: node };
|
||||
|
||||
export default RoutesProvider;
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import React from 'react';
|
||||
import { renderDeep } from '../../util/test-helpers';
|
||||
import RoutesProvider from './RoutesProvider';
|
||||
|
||||
describe('RoutesProvider', () => {
|
||||
it('should contain routes from context', () => {
|
||||
const routesConf = [{ name: 'SomePage', path: 'some-page', component: () => null }];
|
||||
const Child = (props, context) => {
|
||||
return <div>{context.flattenedRoutes[0].name}</div>;
|
||||
};
|
||||
Child.contextTypes = { flattenedRoutes: React.PropTypes.array };
|
||||
|
||||
const tree = renderDeep(
|
||||
<RoutesProvider flattenedRoutes={routesConf}><Child /></RoutesProvider>
|
||||
);
|
||||
expect(tree.children).toContain('SomePage');
|
||||
});
|
||||
});
|
||||
|
|
@ -68,7 +68,7 @@ exports[`SaleDetailsPanel accepted matches snapshot 1`] = `
|
|||
values={
|
||||
Object {
|
||||
"customerName": "customer1 display name",
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -77,7 +77,7 @@ exports[`SaleDetailsPanel accepted matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</h1>
|
||||
|
|
@ -422,7 +422,7 @@ exports[`SaleDetailsPanel autorejected matches snapshot 1`] = `
|
|||
values={
|
||||
Object {
|
||||
"customerName": "customer1 display name",
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -431,7 +431,7 @@ exports[`SaleDetailsPanel autorejected matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</h1>
|
||||
|
|
@ -776,7 +776,7 @@ exports[`SaleDetailsPanel canceled matches snapshot 1`] = `
|
|||
values={
|
||||
Object {
|
||||
"customerName": "customer1 display name",
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -785,7 +785,7 @@ exports[`SaleDetailsPanel canceled matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</h1>
|
||||
|
|
@ -1142,7 +1142,7 @@ exports[`SaleDetailsPanel delivered matches snapshot 1`] = `
|
|||
values={
|
||||
Object {
|
||||
"customerName": "customer1 display name",
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -1151,7 +1151,7 @@ exports[`SaleDetailsPanel delivered matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</h1>
|
||||
|
|
@ -1496,7 +1496,7 @@ exports[`SaleDetailsPanel preauthorized matches snapshot 1`] = `
|
|||
values={
|
||||
Object {
|
||||
"customerName": "customer1 display name",
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -1505,7 +1505,7 @@ exports[`SaleDetailsPanel preauthorized matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</h1>
|
||||
|
|
@ -1862,7 +1862,7 @@ exports[`SaleDetailsPanel rejected matches snapshot 1`] = `
|
|||
values={
|
||||
Object {
|
||||
"customerName": "customer1 display name",
|
||||
"listingLink": <withFlattenedRoutes(withRouter(NamedLink))
|
||||
"listingLink": <NamedLink
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -1871,7 +1871,7 @@ exports[`SaleDetailsPanel rejected matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
listing1 title
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>,
|
||||
</NamedLink>,
|
||||
}
|
||||
} />
|
||||
</h1>
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import { withRouter } from 'react-router-dom';
|
|||
import { injectIntl, intlShape } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import config from '../../config';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { formatMoney } from '../../util/currency';
|
||||
import { ensureListing } from '../../util/data';
|
||||
import { withFlattenedRoutes } from '../../util/contextHelpers';
|
||||
import { createResourceLocatorString } from '../../util/routes';
|
||||
import { createSlug } from '../../util/urlHelpers';
|
||||
import { ResponsiveImage } from '../../components';
|
||||
|
|
@ -21,23 +21,23 @@ const getPixelPositionOffset = (width, height) => {
|
|||
return { x: -1 * (width / 2), y: -1 * (height + 3) };
|
||||
};
|
||||
|
||||
const createURL = (flattenedRoutes, history, listing) => {
|
||||
const createURL = (routes, history, listing) => {
|
||||
const id = listing.id.uuid;
|
||||
const slug = createSlug(listing.attributes.title);
|
||||
const pathParams = { id, slug };
|
||||
return createResourceLocatorString('ListingPage', flattenedRoutes, pathParams, {});
|
||||
return createResourceLocatorString('ListingPage', routes, pathParams, {});
|
||||
};
|
||||
|
||||
// ListingCard is the listing info without overlayview or carousel controls
|
||||
const ListingCard = props => {
|
||||
const { className, clickHandler, flattenedRoutes, history, intl, isInCarousel, listing } = props;
|
||||
const { className, clickHandler, history, intl, isInCarousel, listing } = props;
|
||||
|
||||
const { title, price } = listing.attributes;
|
||||
const formattedPrice = price && price.currency === config.currency
|
||||
? formatMoney(intl, price)
|
||||
: price.currency;
|
||||
const firstImage = listing.images && listing.images.length > 0 ? listing.images[0] : null;
|
||||
const urlToListing = createURL(flattenedRoutes, history, listing);
|
||||
const urlToListing = createURL(routeConfiguration(), history, listing);
|
||||
|
||||
// listing card anchor needs sometimes inherited border radius.
|
||||
const classes = classNames(
|
||||
|
|
@ -100,7 +100,6 @@ ListingCard.propTypes = {
|
|||
className: string,
|
||||
listing: propTypes.listing.isRequired,
|
||||
clickHandler: func.isRequired,
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
history: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
|
|
@ -127,7 +126,7 @@ class SearchMapInfoCard extends Component {
|
|||
}
|
||||
|
||||
render() {
|
||||
const { className, rootClassName, intl, flattenedRoutes, history, listings } = this.props;
|
||||
const { className, rootClassName, intl, history, listings } = this.props;
|
||||
const currentListing = ensureListing(listings[this.state.currentListingIndex]);
|
||||
const geolocation = currentListing.attributes.geolocation;
|
||||
|
||||
|
|
@ -179,7 +178,6 @@ class SearchMapInfoCard extends Component {
|
|||
<ListingCard
|
||||
clickHandler={this.clickHandler}
|
||||
listing={currentListing}
|
||||
flattenedRoutes={flattenedRoutes}
|
||||
history={history}
|
||||
intl={intl}
|
||||
isInCarousel={hasCarousel}
|
||||
|
|
@ -204,9 +202,6 @@ SearchMapInfoCard.propTypes = {
|
|||
listings: arrayOf(propTypes.listing).isRequired,
|
||||
onClickCallback: func,
|
||||
|
||||
// from withFlattenedRoutes
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
|
||||
// from withRouter
|
||||
history: shape({
|
||||
push: func.isRequired,
|
||||
|
|
@ -216,4 +211,4 @@ SearchMapInfoCard.propTypes = {
|
|||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
export default compose(withRouter, withFlattenedRoutes, injectIntl)(SearchMapInfoCard);
|
||||
export default compose(withRouter, injectIntl)(SearchMapInfoCard);
|
||||
|
|
|
|||
|
|
@ -4,8 +4,9 @@ import { FormattedMessage, intlShape, injectIntl } from 'react-intl';
|
|||
import { pickBy } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import config from '../../config';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { ensureCurrentUser } from '../../util/data';
|
||||
import { withFlattenedRoutes, withViewport } from '../../util/contextHelpers';
|
||||
import { withViewport } from '../../util/contextHelpers';
|
||||
import { parse, stringify } from '../../util/urlHelpers';
|
||||
import { createResourceLocatorString, pathByRouteName } from '../../util/routes';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
|
|
@ -98,8 +99,9 @@ class TopbarComponent extends Component {
|
|||
const hasOrders = currentUserHasOrders === true;
|
||||
const hasListingsOrOrders = currentUserHasListings || hasOrders;
|
||||
|
||||
const routes = routeConfiguration();
|
||||
const whitelistedPaths = VERIFY_EMAIL_MODAL_WHITELIST.map(page =>
|
||||
pathByRouteName(page, this.props.flattenedRoutes));
|
||||
pathByRouteName(page, routes));
|
||||
const isNotWhitelisted = !whitelistedPaths.includes(newLocation.pathname);
|
||||
|
||||
// Show reminder
|
||||
|
|
@ -126,16 +128,16 @@ class TopbarComponent extends Component {
|
|||
|
||||
handleSubmit(values) {
|
||||
const { search, selectedPlace } = values.location;
|
||||
const { flattenedRoutes, history } = this.props;
|
||||
const { history } = this.props;
|
||||
const { origin, bounds, country } = selectedPlace;
|
||||
const searchParams = { address: search, origin, bounds, country };
|
||||
history.push(createResourceLocatorString('SearchPage', flattenedRoutes, {}, searchParams));
|
||||
history.push(createResourceLocatorString('SearchPage', routeConfiguration(), {}, searchParams));
|
||||
}
|
||||
|
||||
handleLogout() {
|
||||
const { onLogout, history, flattenedRoutes } = this.props;
|
||||
const { onLogout, history } = this.props;
|
||||
onLogout().then(() => {
|
||||
const path = pathByRouteName('LandingPage', flattenedRoutes);
|
||||
const path = pathByRouteName('LandingPage', routeConfiguration());
|
||||
|
||||
// In production we ensure that data is really lost,
|
||||
// but in development mode we use stored values for debugging
|
||||
|
|
@ -349,7 +351,7 @@ TopbarComponent.defaultProps = {
|
|||
sendVerificationEmailError: null,
|
||||
};
|
||||
|
||||
const { arrayOf, bool, func, instanceOf, number, shape, string } = PropTypes;
|
||||
const { bool, func, instanceOf, number, shape, string } = PropTypes;
|
||||
|
||||
TopbarComponent.propTypes = {
|
||||
className: string,
|
||||
|
|
@ -384,14 +386,11 @@ TopbarComponent.propTypes = {
|
|||
height: number.isRequired,
|
||||
}).isRequired,
|
||||
|
||||
// from withFlattenedRoutes
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
};
|
||||
|
||||
const Topbar = compose(withViewport, withFlattenedRoutes, injectIntl)(TopbarComponent);
|
||||
const Topbar = compose(withViewport, injectIntl)(TopbarComponent);
|
||||
|
||||
Topbar.displayName = 'Topbar';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { FormattedMessage, intlShape } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { ACCOUNT_SETTINGS_PAGES } from '../../routesConfiguration';
|
||||
import { ACCOUNT_SETTINGS_PAGES } from '../../routeConfiguration';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import {
|
||||
Avatar,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
import React from 'react';
|
||||
import { fakeIntl } from '../../util/test-data';
|
||||
import { renderDeep } from '../../util/test-helpers';
|
||||
import { RoutesProvider } from '../../components';
|
||||
import routesConfiguration from '../../routesConfiguration';
|
||||
import { flattenRoutes } from '../../util/routes';
|
||||
import TopbarDesktop from './TopbarDesktop';
|
||||
|
||||
const noop = () => null;
|
||||
|
|
@ -11,7 +8,6 @@ const noop = () => null;
|
|||
describe('TopbarDesktop', () => {
|
||||
it('data matches snapshot', () => {
|
||||
window.google = { maps: {} };
|
||||
const flattenedRoutes = flattenRoutes(routesConfiguration);
|
||||
const topbarProps = {
|
||||
isAuthenticated: true,
|
||||
currentUserHasListings: true,
|
||||
|
|
@ -20,11 +16,7 @@ describe('TopbarDesktop', () => {
|
|||
intl: fakeIntl,
|
||||
onLogout: noop,
|
||||
};
|
||||
const tree = renderDeep(
|
||||
<RoutesProvider flattenedRoutes={flattenedRoutes}>
|
||||
<TopbarDesktop {...topbarProps} />
|
||||
</RoutesProvider>
|
||||
);
|
||||
const tree = renderDeep(<TopbarDesktop {...topbarProps} />);
|
||||
delete window.google;
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { ACCOUNT_SETTINGS_PAGES } from '../../routesConfiguration';
|
||||
import { ACCOUNT_SETTINGS_PAGES } from '../../routeConfiguration';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { ensureCurrentUser } from '../../util/data';
|
||||
import { AvatarLarge, InlineTextButton, NamedLink, NotificationBadge } from '../../components';
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import classNames from 'classnames';
|
||||
import { ACCOUNT_SETTINGS_PAGES } from '../../routesConfiguration';
|
||||
import { ACCOUNT_SETTINGS_PAGES } from '../../routeConfiguration';
|
||||
import { TabNavHorizontal } from '../../components';
|
||||
|
||||
import css from './UserNav.css';
|
||||
|
|
|
|||
|
|
@ -1,159 +1,90 @@
|
|||
import AddImages from './AddImages/AddImages';
|
||||
import Avatar, { AvatarMedium, AvatarLarge } from './Avatar/Avatar';
|
||||
import BirthdayInputField from './BirthdayInputField/BirthdayInputField';
|
||||
import BookingBreakdown from './BookingBreakdown/BookingBreakdown';
|
||||
import Button, { PrimaryButton, SecondaryButton, InlineTextButton } from './Button/Button';
|
||||
import CurrencyInputField from './CurrencyInputField/CurrencyInputField';
|
||||
import DateInputField from './DateInputField/DateInputField';
|
||||
import DateRangeInputField from './DateRangeInputField/DateRangeInputField';
|
||||
import Discussion from './Discussion/Discussion';
|
||||
import EditListingDescriptionPanel from './EditListingDescriptionPanel/EditListingDescriptionPanel';
|
||||
import EditListingLocationPanel from './EditListingLocationPanel/EditListingLocationPanel';
|
||||
import EditListingPhotosPanel from './EditListingPhotosPanel/EditListingPhotosPanel';
|
||||
import EditListingPricingPanel from './EditListingPricingPanel/EditListingPricingPanel';
|
||||
import EditListingWizard from './EditListingWizard/EditListingWizard';
|
||||
import ExpandingTextarea from './ExpandingTextarea/ExpandingTextarea';
|
||||
import ExternalLink from './ExternalLink/ExternalLink';
|
||||
import FilterPanel from './FilterPanel/FilterPanel';
|
||||
import Form from './Form/Form';
|
||||
import HeroSection from './HeroSection/HeroSection';
|
||||
import IconBannedUser from './IconBannedUser/IconBannedUser';
|
||||
import IconCheckmark from './IconCheckmark/IconCheckmark';
|
||||
import IconClose from './IconClose/IconClose';
|
||||
import IconEmailAttention from './IconEmailAttention/IconEmailAttention';
|
||||
import IconEmailSent from './IconEmailSent/IconEmailSent';
|
||||
import IconEmailSuccess from './IconEmailSuccess/IconEmailSuccess';
|
||||
import IconKeys from './IconKeys/IconKeys';
|
||||
import IconKeysSuccess from './IconKeysSuccess/IconKeysSuccess';
|
||||
import IconSearch from './IconSearch/IconSearch';
|
||||
import IconSpinner from './IconSpinner/IconSpinner';
|
||||
import ImageCarousel from './ImageCarousel/ImageCarousel';
|
||||
import ImageFromFile from './ImageFromFile/ImageFromFile';
|
||||
import LayoutSideNavigation, {
|
||||
export { default as AddImages } from './AddImages/AddImages';
|
||||
export { default as Avatar, AvatarMedium, AvatarLarge } from './Avatar/Avatar';
|
||||
export { default as BirthdayInputField } from './BirthdayInputField/BirthdayInputField';
|
||||
export { default as BookingBreakdown } from './BookingBreakdown/BookingBreakdown';
|
||||
export {
|
||||
default as Button,
|
||||
PrimaryButton,
|
||||
SecondaryButton,
|
||||
InlineTextButton,
|
||||
} from './Button/Button';
|
||||
export { default as CurrencyInputField } from './CurrencyInputField/CurrencyInputField';
|
||||
export { default as DateInputField } from './DateInputField/DateInputField';
|
||||
export { default as DateRangeInputField } from './DateRangeInputField/DateRangeInputField';
|
||||
export { default as Discussion } from './Discussion/Discussion';
|
||||
export {
|
||||
default as EditListingDescriptionPanel,
|
||||
} from './EditListingDescriptionPanel/EditListingDescriptionPanel';
|
||||
export {
|
||||
default as EditListingLocationPanel,
|
||||
} from './EditListingLocationPanel/EditListingLocationPanel';
|
||||
export { default as EditListingPhotosPanel } from './EditListingPhotosPanel/EditListingPhotosPanel';
|
||||
export {
|
||||
default as EditListingPricingPanel,
|
||||
} from './EditListingPricingPanel/EditListingPricingPanel';
|
||||
export { default as EditListingWizard } from './EditListingWizard/EditListingWizard';
|
||||
export { default as ExpandingTextarea } from './ExpandingTextarea/ExpandingTextarea';
|
||||
export { default as ExternalLink } from './ExternalLink/ExternalLink';
|
||||
export { default as FilterPanel } from './FilterPanel/FilterPanel';
|
||||
export { default as Form } from './Form/Form';
|
||||
export { default as HeroSection } from './HeroSection/HeroSection';
|
||||
export { default as IconBannedUser } from './IconBannedUser/IconBannedUser';
|
||||
export { default as IconCheckmark } from './IconCheckmark/IconCheckmark';
|
||||
export { default as IconClose } from './IconClose/IconClose';
|
||||
export { default as IconEmailAttention } from './IconEmailAttention/IconEmailAttention';
|
||||
export { default as IconEmailSent } from './IconEmailSent/IconEmailSent';
|
||||
export { default as IconEmailSuccess } from './IconEmailSuccess/IconEmailSuccess';
|
||||
export { default as IconKeys } from './IconKeys/IconKeys';
|
||||
export { default as IconKeysSuccess } from './IconKeysSuccess/IconKeysSuccess';
|
||||
export { default as IconSearch } from './IconSearch/IconSearch';
|
||||
export { default as IconSpinner } from './IconSpinner/IconSpinner';
|
||||
export { default as ImageCarousel } from './ImageCarousel/ImageCarousel';
|
||||
export { default as ImageFromFile } from './ImageFromFile/ImageFromFile';
|
||||
export {
|
||||
default as LayoutSideNavigation,
|
||||
ContentWrapper,
|
||||
SideNavWrapper,
|
||||
TopbarWrapper,
|
||||
} from './LayoutSideNavigation/LayoutSideNavigation';
|
||||
import ListingCard from './ListingCard/ListingCard';
|
||||
import LocationAutocompleteInput, {
|
||||
export { default as ListingCard } from './ListingCard/ListingCard';
|
||||
export {
|
||||
default as LocationAutocompleteInput,
|
||||
LocationAutocompleteInputField,
|
||||
} from './LocationAutocompleteInput/LocationAutocompleteInput';
|
||||
import ManageListingCard from './ManageListingCard/ManageListingCard';
|
||||
import Map from './Map/Map';
|
||||
import MapPanel from './MapPanel/MapPanel';
|
||||
import Menu from './Menu/Menu';
|
||||
import MenuContent from './MenuContent/MenuContent';
|
||||
import MenuItem from './MenuItem/MenuItem';
|
||||
import MenuLabel from './MenuLabel/MenuLabel';
|
||||
import Modal from './Modal/Modal';
|
||||
import ModalInMobile from './ModalInMobile/ModalInMobile';
|
||||
import NamedLink from './NamedLink/NamedLink';
|
||||
import NamedRedirect from './NamedRedirect/NamedRedirect';
|
||||
import NotificationBadge from './NotificationBadge/NotificationBadge';
|
||||
import OrderDetailsPanel from './OrderDetailsPanel/OrderDetailsPanel';
|
||||
import OrderDiscussionPanel from './OrderDiscussionPanel/OrderDiscussionPanel';
|
||||
import Page from './Page/Page';
|
||||
import PaginationLinks from './PaginationLinks/PaginationLinks';
|
||||
import Promised from './Promised/Promised';
|
||||
import ResponsiveImage from './ResponsiveImage/ResponsiveImage';
|
||||
import RoutesProvider from './RoutesProvider/RoutesProvider';
|
||||
import SaleDetailsPanel from './SaleDetailsPanel/SaleDetailsPanel';
|
||||
import SearchMap from './SearchMap/SearchMap';
|
||||
import SearchMapGroupLabel from './SearchMapGroupLabel/SearchMapGroupLabel';
|
||||
import SearchMapInfoCard from './SearchMapInfoCard/SearchMapInfoCard';
|
||||
import SearchMapPriceLabel from './SearchMapPriceLabel/SearchMapPriceLabel';
|
||||
import SearchResultsPanel from './SearchResultsPanel/SearchResultsPanel';
|
||||
import SelectField from './SelectField/SelectField';
|
||||
import StripeBankAccountTokenInputField
|
||||
from './StripeBankAccountTokenInputField/StripeBankAccountTokenInputField';
|
||||
import TabNav from './TabNav/TabNav';
|
||||
import TabNavHorizontal from './TabNavHorizontal/TabNavHorizontal';
|
||||
import Tabs from './Tabs/Tabs';
|
||||
import TextInputField from './TextInputField/TextInputField';
|
||||
import Topbar from './Topbar/Topbar';
|
||||
import TopbarDesktop from './TopbarDesktop/TopbarDesktop';
|
||||
import TopbarMobileMenu from './TopbarMobileMenu/TopbarMobileMenu';
|
||||
import UserNav from './UserNav/UserNav';
|
||||
import ValidationError from './ValidationError/ValidationError';
|
||||
|
||||
export { default as ManageListingCard } from './ManageListingCard/ManageListingCard';
|
||||
export { default as Map } from './Map/Map';
|
||||
export { default as MapPanel } from './MapPanel/MapPanel';
|
||||
export { default as Menu } from './Menu/Menu';
|
||||
export { default as MenuContent } from './MenuContent/MenuContent';
|
||||
export { default as MenuItem } from './MenuItem/MenuItem';
|
||||
export { default as MenuLabel } from './MenuLabel/MenuLabel';
|
||||
export { default as Modal } from './Modal/Modal';
|
||||
export { default as ModalInMobile } from './ModalInMobile/ModalInMobile';
|
||||
export { default as NamedLink } from './NamedLink/NamedLink';
|
||||
export { default as NamedRedirect } from './NamedRedirect/NamedRedirect';
|
||||
export { default as NotificationBadge } from './NotificationBadge/NotificationBadge';
|
||||
export { default as OrderDetailsPanel } from './OrderDetailsPanel/OrderDetailsPanel';
|
||||
export { default as OrderDiscussionPanel } from './OrderDiscussionPanel/OrderDiscussionPanel';
|
||||
export { default as Page } from './Page/Page';
|
||||
export { default as PaginationLinks } from './PaginationLinks/PaginationLinks';
|
||||
export { default as Promised } from './Promised/Promised';
|
||||
export { default as ResponsiveImage } from './ResponsiveImage/ResponsiveImage';
|
||||
export { default as SaleDetailsPanel } from './SaleDetailsPanel/SaleDetailsPanel';
|
||||
export { default as SearchMap } from './SearchMap/SearchMap';
|
||||
export { default as SearchMapGroupLabel } from './SearchMapGroupLabel/SearchMapGroupLabel';
|
||||
export { default as SearchMapInfoCard } from './SearchMapInfoCard/SearchMapInfoCard';
|
||||
export { default as SearchMapPriceLabel } from './SearchMapPriceLabel/SearchMapPriceLabel';
|
||||
export { default as SearchResultsPanel } from './SearchResultsPanel/SearchResultsPanel';
|
||||
export { default as SelectField } from './SelectField/SelectField';
|
||||
export {
|
||||
AddImages,
|
||||
Avatar,
|
||||
AvatarLarge,
|
||||
AvatarMedium,
|
||||
BirthdayInputField,
|
||||
BookingBreakdown,
|
||||
Button,
|
||||
ContentWrapper,
|
||||
CurrencyInputField,
|
||||
DateInputField,
|
||||
DateRangeInputField,
|
||||
Discussion,
|
||||
EditListingDescriptionPanel,
|
||||
EditListingLocationPanel,
|
||||
EditListingPhotosPanel,
|
||||
EditListingPricingPanel,
|
||||
EditListingWizard,
|
||||
ExpandingTextarea,
|
||||
ExternalLink,
|
||||
FilterPanel,
|
||||
Form,
|
||||
HeroSection,
|
||||
IconBannedUser,
|
||||
IconCheckmark,
|
||||
IconClose,
|
||||
IconEmailAttention,
|
||||
IconEmailSent,
|
||||
IconEmailSuccess,
|
||||
IconKeys,
|
||||
IconKeysSuccess,
|
||||
IconSearch,
|
||||
IconSpinner,
|
||||
ImageCarousel,
|
||||
ImageFromFile,
|
||||
InlineTextButton,
|
||||
LayoutSideNavigation,
|
||||
ListingCard,
|
||||
LocationAutocompleteInput,
|
||||
LocationAutocompleteInputField,
|
||||
ManageListingCard,
|
||||
Map,
|
||||
MapPanel,
|
||||
Menu,
|
||||
MenuContent,
|
||||
MenuItem,
|
||||
MenuLabel,
|
||||
Modal,
|
||||
ModalInMobile,
|
||||
NamedLink,
|
||||
NamedRedirect,
|
||||
NotificationBadge,
|
||||
OrderDetailsPanel,
|
||||
OrderDiscussionPanel,
|
||||
Page,
|
||||
PaginationLinks,
|
||||
PrimaryButton,
|
||||
Promised,
|
||||
ResponsiveImage,
|
||||
RoutesProvider,
|
||||
SaleDetailsPanel,
|
||||
SearchMap,
|
||||
SearchMapGroupLabel,
|
||||
SearchMapInfoCard,
|
||||
SearchMapPriceLabel,
|
||||
SearchResultsPanel,
|
||||
SecondaryButton,
|
||||
SelectField,
|
||||
SideNavWrapper,
|
||||
StripeBankAccountTokenInputField,
|
||||
TabNav,
|
||||
TabNavHorizontal,
|
||||
Tabs,
|
||||
TextInputField,
|
||||
Topbar,
|
||||
TopbarDesktop,
|
||||
TopbarMobileMenu,
|
||||
TopbarWrapper,
|
||||
UserNav,
|
||||
ValidationError,
|
||||
};
|
||||
default as StripeBankAccountTokenInputField,
|
||||
} from './StripeBankAccountTokenInputField/StripeBankAccountTokenInputField';
|
||||
export { default as TabNav } from './TabNav/TabNav';
|
||||
export { default as TabNavHorizontal } from './TabNavHorizontal/TabNavHorizontal';
|
||||
export { default as Tabs } from './Tabs/Tabs';
|
||||
export { default as TextInputField } from './TextInputField/TextInputField';
|
||||
export { default as Topbar } from './Topbar/Topbar';
|
||||
export { default as TopbarDesktop } from './TopbarDesktop/TopbarDesktop';
|
||||
export { default as TopbarMobileMenu } from './TopbarMobileMenu/TopbarMobileMenu';
|
||||
export { default as UserNav } from './UserNav/UserNav';
|
||||
export { default as ValidationError } from './ValidationError/ValidationError';
|
||||
|
|
|
|||
|
|
@ -4,10 +4,10 @@ import { connect } from 'react-redux';
|
|||
import { FormattedMessage, injectIntl, intlShape } from 'react-intl';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { pathByRouteName } from '../../util/routes';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { ensureListing, ensureUser, ensureTransaction, ensureBooking } from '../../util/data';
|
||||
import { withFlattenedRoutes } from '../../util/contextHelpers';
|
||||
import { createSlug } from '../../util/urlHelpers';
|
||||
import {
|
||||
isTransactionInitiateAmountTooLowError,
|
||||
|
|
@ -99,7 +99,7 @@ export class CheckoutPageComponent extends Component {
|
|||
}
|
||||
this.setState({ submitting: true });
|
||||
|
||||
const { flattenedRoutes, history, sendOrderRequest, speculatedTransaction } = this.props;
|
||||
const { history, sendOrderRequest, speculatedTransaction } = this.props;
|
||||
const requestParams = {
|
||||
listingId: this.state.pageData.listing.id,
|
||||
cardToken,
|
||||
|
|
@ -110,7 +110,7 @@ export class CheckoutPageComponent extends Component {
|
|||
sendOrderRequest(requestParams)
|
||||
.then(orderId => {
|
||||
this.setState({ submitting: false });
|
||||
const orderDetailsPath = pathByRouteName('OrderDetailsPage', flattenedRoutes, {
|
||||
const orderDetailsPath = pathByRouteName('OrderDetailsPage', routeConfiguration(), {
|
||||
id: orderId.uuid,
|
||||
});
|
||||
clearData(STORAGE_KEY);
|
||||
|
|
@ -375,7 +375,7 @@ CheckoutPageComponent.defaultProps = {
|
|||
currentUser: null,
|
||||
};
|
||||
|
||||
const { arrayOf, func, instanceOf, shape, string, bool } = PropTypes;
|
||||
const { func, instanceOf, shape, string, bool } = PropTypes;
|
||||
|
||||
CheckoutPageComponent.propTypes = {
|
||||
authInfoError: instanceOf(Error),
|
||||
|
|
@ -404,9 +404,6 @@ CheckoutPageComponent.propTypes = {
|
|||
history: shape({
|
||||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
|
||||
// from withFlattenedRoutes
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
|
|
@ -439,12 +436,9 @@ const mapDispatchToProps = dispatch => ({
|
|||
dispatch(speculateTransaction(listingId, bookingStart, bookingEnd)),
|
||||
});
|
||||
|
||||
const CheckoutPage = compose(
|
||||
withRouter,
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
withFlattenedRoutes,
|
||||
injectIntl
|
||||
)(CheckoutPageComponent);
|
||||
const CheckoutPage = compose(withRouter, connect(mapStateToProps, mapDispatchToProps), injectIntl)(
|
||||
CheckoutPageComponent
|
||||
);
|
||||
|
||||
CheckoutPage.setInitialValues = initialValues => setInitialValues(initialValues);
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ describe('CheckoutPage', () => {
|
|||
bookingStart: new Date(Date.UTC(2017, 3, 14)),
|
||||
bookingEnd: new Date(Date.UTC(2017, 3, 16)),
|
||||
},
|
||||
flattenedRoutes: [],
|
||||
history: { push: noop },
|
||||
intl: fakeIntl,
|
||||
listing,
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
logoutError={null}
|
||||
title="CheckoutPage.title">
|
||||
<div>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
<NamedLink
|
||||
name="LandingPage">
|
||||
<LogoIcon
|
||||
alt="Logo"
|
||||
|
|
@ -15,7 +15,7 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
alt="CheckoutPage.goToLandingPage"
|
||||
className={null}
|
||||
isMobile={false} />
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</NamedLink>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ export const EditListingPageComponent = props => {
|
|||
currentUserHasOrders,
|
||||
createStripeAccountError,
|
||||
fetchInProgress,
|
||||
flattenedRoutes,
|
||||
getListing,
|
||||
history,
|
||||
intl,
|
||||
|
|
@ -168,7 +167,6 @@ export const EditListingPageComponent = props => {
|
|||
errors={errors}
|
||||
fetchInProgress={fetchInProgress}
|
||||
newListingCreated={newListingCreated}
|
||||
flattenedRoutes={flattenedRoutes}
|
||||
history={history}
|
||||
images={images}
|
||||
listing={isNew ? page.listingDraft : currentListing}
|
||||
|
|
@ -211,7 +209,7 @@ EditListingPageComponent.defaultProps = {
|
|||
sendVerificationEmailError: null,
|
||||
};
|
||||
|
||||
const { arrayOf, bool, func, instanceOf, number, object, shape, string, oneOf } = PropTypes;
|
||||
const { bool, func, instanceOf, number, object, shape, string, oneOf } = PropTypes;
|
||||
|
||||
EditListingPageComponent.propTypes = {
|
||||
authInfoError: instanceOf(Error),
|
||||
|
|
@ -221,7 +219,6 @@ EditListingPageComponent.propTypes = {
|
|||
currentUserHasListings: bool.isRequired,
|
||||
currentUserHasOrders: bool,
|
||||
fetchInProgress: bool.isRequired,
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
getListing: func.isRequired,
|
||||
isAuthenticated: bool.isRequired,
|
||||
logoutError: instanceOf(Error),
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ describe('EditListingPageComponent', () => {
|
|||
isAuthenticated={false}
|
||||
authInProgress={false}
|
||||
fetchInProgress={false}
|
||||
flattenedRoutes={[]}
|
||||
location={{ search: '' }}
|
||||
history={{ push: noop }}
|
||||
getListing={getListing}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,6 @@ exports[`EditListingPageComponent matches snapshot 1`] = `
|
|||
}
|
||||
}
|
||||
fetchInProgress={false}
|
||||
flattenedRoutes={Array []}
|
||||
history={
|
||||
Object {
|
||||
"push": [Function],
|
||||
|
|
|
|||
|
|
@ -9,15 +9,13 @@ import {
|
|||
createBooking,
|
||||
} from '../../util/test-data';
|
||||
import { InboxPageComponent, InboxItem } from './InboxPage';
|
||||
import routesConfiguration from '../../routesConfiguration';
|
||||
import { flattenRoutes } from '../../util/routes';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { TX_TRANSITION_PREAUTHORIZE } from '../../util/propTypes';
|
||||
|
||||
const noop = () => null;
|
||||
|
||||
describe('InboxPage', () => {
|
||||
it('matches snapshot', () => {
|
||||
const flattenedRoutes = flattenRoutes(routesConfiguration);
|
||||
const provider = createUser('provider-user-id');
|
||||
const customer = createUser('customer-user-id');
|
||||
const currentUserProvider = createCurrentUser('provider-user-id');
|
||||
|
|
@ -76,9 +74,7 @@ describe('InboxPage', () => {
|
|||
|
||||
// Deeply render one InboxItem
|
||||
const orderItem = renderDeep(
|
||||
<RoutesProvider flattenedRoutes={flattenedRoutes}>
|
||||
<InboxItem type="order" tx={ordersProps.transactions[0]} intl={fakeIntl} />
|
||||
</RoutesProvider>
|
||||
<InboxItem type="order" tx={ordersProps.transactions[0]} intl={fakeIntl} />
|
||||
);
|
||||
expect(orderItem).toMatchSnapshot();
|
||||
|
||||
|
|
@ -126,9 +122,7 @@ describe('InboxPage', () => {
|
|||
|
||||
// Deeply render one InboxItem
|
||||
const saleItem = renderDeep(
|
||||
<RoutesProvider flattenedRoutes={flattenedRoutes}>
|
||||
<InboxItem type="sale" tx={salesProps.transactions[0]} intl={fakeIntl} />
|
||||
</RoutesProvider>
|
||||
<InboxItem type="sale" tx={salesProps.transactions[0]} intl={fakeIntl} />
|
||||
);
|
||||
expect(saleItem).toMatchSnapshot();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -6,10 +6,9 @@ import { injectIntl, intlShape } from 'react-intl';
|
|||
import { sendVerificationEmail } from '../../ducks/user.duck';
|
||||
import { logout, authenticationInProgress } from '../../ducks/Auth.duck';
|
||||
import { manageDisableScrolling, isScrollingDisabled } from '../../ducks/UI.duck';
|
||||
import { Page, HeroSection, Topbar } from '../../components';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { withFlattenedRoutes } from '../../util/contextHelpers';
|
||||
import config from '../../config';
|
||||
import { Page, HeroSection, Topbar } from '../../components';
|
||||
|
||||
import facebookImage from '../../assets/saunatimeFacebook-1200x630.jpg';
|
||||
import twitterImage from '../../assets/saunatimeTwitter-600x314.jpg';
|
||||
|
|
@ -22,7 +21,6 @@ export const LandingPageComponent = props => {
|
|||
currentUser,
|
||||
currentUserHasListings,
|
||||
currentUserHasOrders,
|
||||
flattenedRoutes,
|
||||
history,
|
||||
intl,
|
||||
isAuthenticated,
|
||||
|
|
@ -87,12 +85,7 @@ export const LandingPageComponent = props => {
|
|||
sendVerificationEmailError={sendVerificationEmailError}
|
||||
/>
|
||||
<div className={css.heroContainer}>
|
||||
<HeroSection
|
||||
className={css.hero}
|
||||
flattenedRoutes={flattenedRoutes}
|
||||
history={history}
|
||||
location={location}
|
||||
/>
|
||||
<HeroSection className={css.hero} history={history} location={location} />
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
|
|
@ -107,7 +100,7 @@ LandingPageComponent.defaultProps = {
|
|||
sendVerificationEmailError: null,
|
||||
};
|
||||
|
||||
const { array, bool, func, instanceOf, number, object } = PropTypes;
|
||||
const { bool, func, instanceOf, number, object } = PropTypes;
|
||||
|
||||
LandingPageComponent.propTypes = {
|
||||
authInfoError: instanceOf(Error),
|
||||
|
|
@ -125,9 +118,6 @@ LandingPageComponent.propTypes = {
|
|||
sendVerificationEmailError: instanceOf(Error),
|
||||
onResendVerificationEmail: func.isRequired,
|
||||
|
||||
// from withFlattenedRoutes
|
||||
flattenedRoutes: array.isRequired,
|
||||
|
||||
// from withRouter
|
||||
history: object.isRequired,
|
||||
location: object.isRequired,
|
||||
|
|
@ -168,11 +158,8 @@ const mapDispatchToProps = dispatch => ({
|
|||
onResendVerificationEmail: () => dispatch(sendVerificationEmail()),
|
||||
});
|
||||
|
||||
const LandingPage = compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
injectIntl,
|
||||
withRouter,
|
||||
withFlattenedRoutes
|
||||
)(LandingPageComponent);
|
||||
const LandingPage = compose(connect(mapStateToProps, mapDispatchToProps), injectIntl, withRouter)(
|
||||
LandingPageComponent
|
||||
);
|
||||
|
||||
export default LandingPage;
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { fakeIntl } from '../../util/test-data';
|
|||
import { renderShallow } from '../../util/test-helpers';
|
||||
import { LandingPageComponent } from './LandingPage';
|
||||
import { RoutesProvider } from '../../components';
|
||||
import routesConfiguration from '../../routesConfiguration';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
|
||||
const noop = () => null;
|
||||
|
||||
|
|
@ -11,7 +11,6 @@ describe('LandingPage', () => {
|
|||
it('matches snapshot', () => {
|
||||
const tree = renderShallow(
|
||||
<LandingPageComponent
|
||||
flattenedRoutes={[]}
|
||||
history={{ push: noop }}
|
||||
location={{ search: '' }}
|
||||
scrollingDisabled={false}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,6 @@ exports[`LandingPage matches snapshot 1`] = `
|
|||
<div>
|
||||
<HeroSection
|
||||
className={null}
|
||||
flattenedRoutes={Array []}
|
||||
history={
|
||||
Object {
|
||||
"push": [Function],
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import { connect } from 'react-redux';
|
|||
import { withRouter } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import config from '../../config';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { types } from '../../util/sdkLoader';
|
||||
import { createSlug } from '../../util/urlHelpers';
|
||||
|
|
@ -88,7 +89,7 @@ export const ActionBar = props => {
|
|||
}
|
||||
};
|
||||
|
||||
const { arrayOf, bool, func, instanceOf, number, object, oneOf, shape, string } = PropTypes;
|
||||
const { bool, func, instanceOf, number, object, oneOf, shape, string } = PropTypes;
|
||||
|
||||
ActionBar.propTypes = {
|
||||
isOwnListing: bool.isRequired,
|
||||
|
|
@ -114,7 +115,7 @@ export class ListingPageComponent extends Component {
|
|||
}
|
||||
|
||||
handleSubmit(values) {
|
||||
const { flattenedRoutes, history, getListing, params, useInitialValues } = this.props;
|
||||
const { history, getListing, params, useInitialValues } = this.props;
|
||||
const listingId = new UUID(params.id);
|
||||
const listing = getListing(listingId);
|
||||
|
||||
|
|
@ -130,15 +131,16 @@ export class ListingPageComponent extends Component {
|
|||
},
|
||||
};
|
||||
|
||||
const routes = routeConfiguration();
|
||||
// Customize checkout page state with current listing and selected bookingDates
|
||||
const { setInitialValues } = findRouteByRouteName('CheckoutPage', flattenedRoutes);
|
||||
const { setInitialValues } = findRouteByRouteName('CheckoutPage', routes);
|
||||
useInitialValues(setInitialValues, initialValues);
|
||||
|
||||
// Redirect to CheckoutPage
|
||||
history.push(
|
||||
createResourceLocatorString(
|
||||
'CheckoutPage',
|
||||
flattenedRoutes,
|
||||
routes,
|
||||
{ id: listing.id.uuid, slug: createSlug(listing.attributes.title) },
|
||||
{}
|
||||
)
|
||||
|
|
@ -167,7 +169,6 @@ export class ListingPageComponent extends Component {
|
|||
sendVerificationEmailInProgress,
|
||||
sendVerificationEmailError,
|
||||
onResendVerificationEmail,
|
||||
flattenedRoutes,
|
||||
} = this.props;
|
||||
const listingId = new UUID(params.id);
|
||||
const currentListing = ensureListing(getListing(listingId));
|
||||
|
|
@ -345,9 +346,13 @@ export class ListingPageComponent extends Component {
|
|||
{ title, price: formattedPrice, siteTitle }
|
||||
);
|
||||
|
||||
const canonicalPath = createResourceLocatorString('ListingPageCanonical', flattenedRoutes, {
|
||||
id: listingId.uuid,
|
||||
});
|
||||
const canonicalPath = createResourceLocatorString(
|
||||
'ListingPageCanonical',
|
||||
routeConfiguration(),
|
||||
{
|
||||
id: listingId.uuid,
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<Page
|
||||
|
|
@ -517,7 +522,6 @@ ListingPageComponent.propTypes = {
|
|||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
location: object.isRequired,
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
// from injectIntl
|
||||
intl: intlShape.isRequired,
|
||||
params: shape({
|
||||
|
|
|
|||
|
|
@ -4,10 +4,15 @@ import { FormattedMessage } from 'react-intl';
|
|||
import { types } from '../../util/sdkLoader';
|
||||
import { createUser, createCurrentUser, createListing, fakeIntl } from '../../util/test-data';
|
||||
import { renderShallow } from '../../util/test-helpers';
|
||||
import { ListingPageComponent, ActionBar } from './ListingPage';
|
||||
import { addMarketplaceEntities } from '../../ducks/marketplaceData.duck';
|
||||
import { showListingRequest, showListingError, showListing } from './ListingPage.duck';
|
||||
|
||||
// routeConfiguration needs to be imported before tests for ListingPageComponent can be made.
|
||||
// Otherwise, ListingPage itself is not initialized correctly when routeConfiguration is imported
|
||||
// (loadData call fails).
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { ListingPageComponent, ActionBar } from './ListingPage';
|
||||
|
||||
const { UUID } = types;
|
||||
const noop = () => null;
|
||||
|
||||
|
|
@ -16,17 +21,8 @@ describe('ListingPage', () => {
|
|||
const currentUser = createCurrentUser('user-2');
|
||||
const listing1 = createListing('listing1', {}, { author: createUser('user-1') });
|
||||
const getListing = () => listing1;
|
||||
|
||||
const props = {
|
||||
flattenedRoutes: [
|
||||
// Fake route since a circular dependency prevents using the
|
||||
// full routesConfiguration here
|
||||
{
|
||||
path: '/l/:id',
|
||||
exact: true,
|
||||
name: 'ListingPageCanonical',
|
||||
component: noop,
|
||||
},
|
||||
],
|
||||
location: { search: '' },
|
||||
history: {
|
||||
push: () => console.log('HistoryPush called'),
|
||||
|
|
|
|||
|
|
@ -1,18 +1,10 @@
|
|||
import React from 'react';
|
||||
import { renderDeep } from '../../util/test-helpers';
|
||||
import { RoutesProvider } from '../../components';
|
||||
import routesConfiguration from '../../routesConfiguration';
|
||||
import { flattenRoutes } from '../../util/routes';
|
||||
import LoginForm from './LoginForm';
|
||||
|
||||
describe('LoginForm', () => {
|
||||
it('matches snapshot', () => {
|
||||
const flattenedRoutes = flattenRoutes(routesConfiguration);
|
||||
const tree = renderDeep(
|
||||
<RoutesProvider flattenedRoutes={flattenedRoutes}>
|
||||
<LoginForm />
|
||||
</RoutesProvider>
|
||||
);
|
||||
const tree = renderDeep(<LoginForm />);
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import { injectIntl, intlShape, FormattedMessage } from 'react-intl';
|
|||
import { compose } from 'redux';
|
||||
import { connect } from 'react-redux';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { withFlattenedRoutes } from '../../util/contextHelpers';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { createResourceLocatorString } from '../../util/routes';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
import { sendVerificationEmail } from '../../ducks/user.duck';
|
||||
|
|
@ -40,7 +40,6 @@ export class NotFoundPageComponent extends Component {
|
|||
sendVerificationEmailInProgress,
|
||||
sendVerificationEmailError,
|
||||
onResendVerificationEmail,
|
||||
flattenedRoutes,
|
||||
intl,
|
||||
} = this.props;
|
||||
|
||||
|
|
@ -52,7 +51,9 @@ export class NotFoundPageComponent extends Component {
|
|||
const { search, selectedPlace } = values.location;
|
||||
const { origin, bounds, country } = selectedPlace;
|
||||
const searchParams = { address: search, origin, bounds, country };
|
||||
history.push(createResourceLocatorString('SearchPage', flattenedRoutes, {}, searchParams));
|
||||
history.push(
|
||||
createResourceLocatorString('SearchPage', routeConfiguration(), {}, searchParams)
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -99,7 +100,7 @@ NotFoundPageComponent.defaultProps = {
|
|||
sendVerificationEmailError: null,
|
||||
};
|
||||
|
||||
const { bool, func, instanceOf, number, object, shape, array } = PropTypes;
|
||||
const { bool, func, instanceOf, number, object, shape } = PropTypes;
|
||||
|
||||
NotFoundPageComponent.propTypes = {
|
||||
authInfoError: instanceOf(Error),
|
||||
|
|
@ -127,9 +128,6 @@ NotFoundPageComponent.propTypes = {
|
|||
push: func.isRequired,
|
||||
}).isRequired,
|
||||
location: shape({ state: object }).isRequired,
|
||||
|
||||
// from withFlattenedRoutes
|
||||
flattenedRoutes: array.isRequired,
|
||||
};
|
||||
|
||||
const mapStateToProps = state => {
|
||||
|
|
@ -166,11 +164,8 @@ const mapDispatchToProps = dispatch => ({
|
|||
onResendVerificationEmail: () => dispatch(sendVerificationEmail()),
|
||||
});
|
||||
|
||||
const NotFoundPage = compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
withRouter,
|
||||
injectIntl,
|
||||
withFlattenedRoutes
|
||||
)(NotFoundPageComponent);
|
||||
const NotFoundPage = compose(connect(mapStateToProps, mapDispatchToProps), withRouter, injectIntl)(
|
||||
NotFoundPageComponent
|
||||
);
|
||||
|
||||
export default NotFoundPage;
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ describe('NotFoundPageComponent', () => {
|
|||
sendVerificationEmailInProgress={false}
|
||||
onResendVerificationEmail={noop}
|
||||
intl={fakeIntl}
|
||||
flattenedRoutes={[]}
|
||||
/>
|
||||
);
|
||||
expect(tree).toMatchSnapshot();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { withRouter } from 'react-router-dom';
|
|||
import { debounce, isEqual, unionWith } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import config from '../../config';
|
||||
import { withFlattenedRoutes } from '../../util/contextHelpers';
|
||||
import routeConfiguration from '../../routeConfiguration';
|
||||
import { googleLatLngToSDKLatLng, googleBoundsToSDKBounds } from '../../util/googleMaps';
|
||||
import { createResourceLocatorString } from '../../util/routes';
|
||||
import { createSlug, parse, stringify } from '../../util/urlHelpers';
|
||||
|
|
@ -77,7 +77,7 @@ export class SearchPageComponent extends Component {
|
|||
// We are using Google Maps idle event instead of bounds_changed, since it will not be fired
|
||||
// too often (in the middle of map's pan or zoom activity)
|
||||
onIdle(googleMap) {
|
||||
const { flattenedRoutes, history, location } = this.props;
|
||||
const { history, location } = this.props;
|
||||
|
||||
const { address, country, boundsChanged } = parse(location.search, {
|
||||
latlng: ['origin'],
|
||||
|
|
@ -93,7 +93,9 @@ export class SearchPageComponent extends Component {
|
|||
const origin = googleLatLngToSDKLatLng(viewportBounds.getCenter());
|
||||
|
||||
const searchParams = { address, origin, bounds, country, boundsChanged: true };
|
||||
history.push(createResourceLocatorString('SearchPage', flattenedRoutes, {}, searchParams));
|
||||
history.push(
|
||||
createResourceLocatorString('SearchPage', routeConfiguration(), {}, searchParams)
|
||||
);
|
||||
} else {
|
||||
this.useLocationSearchBounds = false;
|
||||
this.modalOpenedBoundsChange = false;
|
||||
|
|
@ -137,7 +139,6 @@ export class SearchPageComponent extends Component {
|
|||
currentUser,
|
||||
currentUserHasListings,
|
||||
currentUserHasOrders,
|
||||
flattenedRoutes,
|
||||
history,
|
||||
intl,
|
||||
isAuthenticated,
|
||||
|
|
@ -245,7 +246,7 @@ export class SearchPageComponent extends Component {
|
|||
const schemaDescription = intl.formatMessage({ id: 'SearchPage.schemaDescription' });
|
||||
const schemaListings = listings.map((l, i) => {
|
||||
const title = l.attributes.title;
|
||||
const pathToItem = createResourceLocatorString('ListingPage', flattenedRoutes, {
|
||||
const pathToItem = createResourceLocatorString('ListingPage', routeConfiguration(), {
|
||||
id: l.id.uuid,
|
||||
slug: createSlug(title),
|
||||
});
|
||||
|
|
@ -368,7 +369,7 @@ SearchPageComponent.defaultProps = {
|
|||
sendVerificationEmailError: null,
|
||||
};
|
||||
|
||||
const { array, arrayOf, bool, func, instanceOf, number, oneOf, object, shape, string } = PropTypes;
|
||||
const { array, bool, func, instanceOf, number, oneOf, object, shape, string } = PropTypes;
|
||||
|
||||
SearchPageComponent.propTypes = {
|
||||
authInfoError: instanceOf(Error),
|
||||
|
|
@ -394,9 +395,6 @@ SearchPageComponent.propTypes = {
|
|||
sendVerificationEmailError: instanceOf(Error),
|
||||
onResendVerificationEmail: func.isRequired,
|
||||
|
||||
// from withFlattenedRoutes
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
|
||||
// from withRouter
|
||||
history: shape({
|
||||
push: func.isRequired,
|
||||
|
|
@ -462,12 +460,9 @@ const mapDispatchToProps = dispatch => ({
|
|||
onSearchMapListings: searchParams => dispatch(searchMapListings(searchParams)),
|
||||
});
|
||||
|
||||
const SearchPage = compose(
|
||||
connect(mapStateToProps, mapDispatchToProps),
|
||||
injectIntl,
|
||||
withFlattenedRoutes,
|
||||
withRouter
|
||||
)(SearchPageComponent);
|
||||
const SearchPage = compose(connect(mapStateToProps, mapDispatchToProps), injectIntl, withRouter)(
|
||||
SearchPageComponent
|
||||
);
|
||||
|
||||
SearchPage.loadData = (params, search) => {
|
||||
const queryParams = parse(search, {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,6 @@ describe('SearchPageComponent', () => {
|
|||
onLogout: noop,
|
||||
onManageDisableScrolling: noop,
|
||||
onSearchMapListings: noop,
|
||||
flattenedRoutes: [],
|
||||
sendVerificationEmailInProgress: false,
|
||||
onResendVerificationEmail: noop,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ import React, { PropTypes } from 'react';
|
|||
import { isEmpty } from 'lodash';
|
||||
import classNames from 'classnames';
|
||||
import { NamedLink } from '../../components';
|
||||
import * as allExamples from '../../examples';
|
||||
|
||||
import css from './StyleguidePage.css';
|
||||
|
||||
|
|
@ -161,6 +160,17 @@ const examplesFor = (examples, group, componentName, exampleName) => {
|
|||
};
|
||||
|
||||
const StyleguidePage = props => {
|
||||
// TODO: importing all the examples will affect the module bundling
|
||||
// since examples call routeConfiguration without function wrapping
|
||||
// Wurthermore, it would be nice to exclude styleguide away from actual app
|
||||
let allExamples = [];
|
||||
try {
|
||||
allExamples = require('../../examples'); // eslint-disable-line global-require
|
||||
} catch (e) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('require(): The file "../../examples.js" could not be loaded.');
|
||||
}
|
||||
|
||||
const { params, raw } = props;
|
||||
const group = params.group ? decodeURIComponent(params.group) : ALL;
|
||||
const componentName = params.component || ALL;
|
||||
|
|
|
|||
|
|
@ -1,79 +1,42 @@
|
|||
import AuthenticationPage from './AuthenticationPage/AuthenticationPage';
|
||||
import BookingDatesForm from './BookingDatesForm/BookingDatesForm';
|
||||
import CheckoutPage from './CheckoutPage/CheckoutPage';
|
||||
import ContactDetailsForm from './ContactDetailsForm/ContactDetailsForm';
|
||||
import ContactDetailsPage from './ContactDetailsPage/ContactDetailsPage';
|
||||
import EditListingDescriptionForm from './EditListingDescriptionForm/EditListingDescriptionForm';
|
||||
import EditListingLocationForm from './EditListingLocationForm/EditListingLocationForm';
|
||||
import EditListingPage from './EditListingPage/EditListingPage';
|
||||
import EditListingPhotosForm from './EditListingPhotosForm/EditListingPhotosForm';
|
||||
import EditListingPricingForm from './EditListingPricingForm/EditListingPricingForm';
|
||||
import EmailVerificationForm from './EmailVerificationForm/EmailVerificationForm';
|
||||
import EmailVerificationPage from './EmailVerificationPage/EmailVerificationPage';
|
||||
import InboxPage from './InboxPage/InboxPage';
|
||||
import LandingPage from './LandingPage/LandingPage';
|
||||
import ListingPage from './ListingPage/ListingPage';
|
||||
import LocationSearchForm from './LocationSearchForm/LocationSearchForm';
|
||||
import LoginForm from './LoginForm/LoginForm';
|
||||
import ManageListingsPage from './ManageListingsPage/ManageListingsPage';
|
||||
import NotFoundPage from './NotFoundPage/NotFoundPage';
|
||||
import OrderPage from './OrderPage/OrderPage';
|
||||
import PasswordChangeForm from './PasswordChangeForm/PasswordChangeForm';
|
||||
import PasswordChangePage from './PasswordChangePage/PasswordChangePage';
|
||||
import PasswordRecoveryForm from './PasswordRecoveryForm/PasswordRecoveryForm';
|
||||
import PasswordRecoveryPage from './PasswordRecoveryPage/PasswordRecoveryPage';
|
||||
import PasswordResetForm from './PasswordResetForm/PasswordResetForm';
|
||||
import PasswordResetPage from './PasswordResetPage/PasswordResetPage';
|
||||
import PayoutDetailsForm from './PayoutDetailsForm/PayoutDetailsForm';
|
||||
import PayoutPreferencesPage from './PayoutPreferencesPage/PayoutPreferencesPage';
|
||||
import ProfilePage from './ProfilePage/ProfilePage';
|
||||
import ProfileSettingsForm from './ProfileSettingsForm/ProfileSettingsForm';
|
||||
import ProfileSettingsPage from './ProfileSettingsPage/ProfileSettingsPage';
|
||||
import SalePage from './SalePage/SalePage';
|
||||
import SearchPage from './SearchPage/SearchPage';
|
||||
import SecurityPage from './SecurityPage/SecurityPage';
|
||||
import SignupForm from './SignupForm/SignupForm';
|
||||
import StripePaymentForm from './StripePaymentForm/StripePaymentForm';
|
||||
import StyleguidePage from './StyleguidePage/StyleguidePage';
|
||||
import TopbarSearchForm from './TopbarSearchForm/TopbarSearchForm';
|
||||
|
||||
export { default as AuthenticationPage } from './AuthenticationPage/AuthenticationPage';
|
||||
export { default as BookingDatesForm } from './BookingDatesForm/BookingDatesForm';
|
||||
export { default as CheckoutPage } from './CheckoutPage/CheckoutPage';
|
||||
export { default as ContactDetailsForm } from './ContactDetailsForm/ContactDetailsForm';
|
||||
export { default as ContactDetailsPage } from './ContactDetailsPage/ContactDetailsPage';
|
||||
export {
|
||||
AuthenticationPage,
|
||||
BookingDatesForm,
|
||||
CheckoutPage,
|
||||
ContactDetailsForm,
|
||||
ContactDetailsPage,
|
||||
EditListingDescriptionForm,
|
||||
EditListingLocationForm,
|
||||
EditListingPage,
|
||||
EditListingPhotosForm,
|
||||
EditListingPricingForm,
|
||||
EmailVerificationForm,
|
||||
EmailVerificationPage,
|
||||
InboxPage,
|
||||
LandingPage,
|
||||
ListingPage,
|
||||
LocationSearchForm,
|
||||
LoginForm,
|
||||
ManageListingsPage,
|
||||
NotFoundPage,
|
||||
OrderPage,
|
||||
PasswordChangeForm,
|
||||
PasswordChangePage,
|
||||
PasswordRecoveryForm,
|
||||
PasswordRecoveryPage,
|
||||
PasswordResetForm,
|
||||
PasswordResetPage,
|
||||
PayoutDetailsForm,
|
||||
PayoutPreferencesPage,
|
||||
ProfilePage,
|
||||
ProfileSettingsForm,
|
||||
ProfileSettingsPage,
|
||||
SalePage,
|
||||
SearchPage,
|
||||
SecurityPage,
|
||||
SignupForm,
|
||||
StripePaymentForm,
|
||||
StyleguidePage,
|
||||
TopbarSearchForm,
|
||||
};
|
||||
default as EditListingDescriptionForm,
|
||||
} from './EditListingDescriptionForm/EditListingDescriptionForm';
|
||||
export {
|
||||
default as EditListingLocationForm,
|
||||
} from './EditListingLocationForm/EditListingLocationForm';
|
||||
export { default as EditListingPage } from './EditListingPage/EditListingPage';
|
||||
export { default as EditListingPhotosForm } from './EditListingPhotosForm/EditListingPhotosForm';
|
||||
export { default as EditListingPricingForm } from './EditListingPricingForm/EditListingPricingForm';
|
||||
export { default as EmailVerificationForm } from './EmailVerificationForm/EmailVerificationForm';
|
||||
export { default as EmailVerificationPage } from './EmailVerificationPage/EmailVerificationPage';
|
||||
export { default as InboxPage } from './InboxPage/InboxPage';
|
||||
export { default as LandingPage } from './LandingPage/LandingPage';
|
||||
export { default as ListingPage } from './ListingPage/ListingPage';
|
||||
export { default as LocationSearchForm } from './LocationSearchForm/LocationSearchForm';
|
||||
export { default as LoginForm } from './LoginForm/LoginForm';
|
||||
export { default as ManageListingsPage } from './ManageListingsPage/ManageListingsPage';
|
||||
export { default as NotFoundPage } from './NotFoundPage/NotFoundPage';
|
||||
export { default as OrderPage } from './OrderPage/OrderPage';
|
||||
export { default as PasswordChangeForm } from './PasswordChangeForm/PasswordChangeForm';
|
||||
export { default as PasswordChangePage } from './PasswordChangePage/PasswordChangePage';
|
||||
export { default as PasswordRecoveryForm } from './PasswordRecoveryForm/PasswordRecoveryForm';
|
||||
export { default as PasswordRecoveryPage } from './PasswordRecoveryPage/PasswordRecoveryPage';
|
||||
export { default as PasswordResetForm } from './PasswordResetForm/PasswordResetForm';
|
||||
export { default as PasswordResetPage } from './PasswordResetPage/PasswordResetPage';
|
||||
export { default as PayoutDetailsForm } from './PayoutDetailsForm/PayoutDetailsForm';
|
||||
export { default as PayoutPreferencesPage } from './PayoutPreferencesPage/PayoutPreferencesPage';
|
||||
export { default as ProfilePage } from './ProfilePage/ProfilePage';
|
||||
export { default as ProfileSettingsForm } from './ProfileSettingsForm/ProfileSettingsForm';
|
||||
export { default as ProfileSettingsPage } from './ProfileSettingsPage/ProfileSettingsPage';
|
||||
export { default as SalePage } from './SalePage/SalePage';
|
||||
export { default as SearchPage } from './SearchPage/SearchPage';
|
||||
export { default as SecurityPage } from './SecurityPage/SecurityPage';
|
||||
export { default as SignupForm } from './SignupForm/SignupForm';
|
||||
export { default as StripePaymentForm } from './StripePaymentForm/StripePaymentForm';
|
||||
export { default as StyleguidePage } from './StyleguidePage/StyleguidePage';
|
||||
export { default as TopbarSearchForm } from './TopbarSearchForm/TopbarSearchForm';
|
||||
|
|
|
|||
11
src/index.js
11
src/index.js
|
|
@ -22,7 +22,7 @@ import * as sample from './util/sample';
|
|||
import config from './config';
|
||||
import { authInfo } from './ducks/Auth.duck';
|
||||
import { fetchCurrentUser } from './ducks/user.duck';
|
||||
import routeConfiguration from './routesConfiguration';
|
||||
import routeConfiguration from './routeConfiguration';
|
||||
import * as log from './util/log';
|
||||
|
||||
import './marketplaceIndex.css';
|
||||
|
|
@ -81,7 +81,14 @@ if (typeof window !== 'undefined') {
|
|||
|
||||
if (config.dev) {
|
||||
// Expose stuff for the browser REPL
|
||||
window.app = { config, sdk, sdkTypes: types, store, sample, routeConfiguration };
|
||||
window.app = {
|
||||
config,
|
||||
sdk,
|
||||
sdkTypes: types,
|
||||
store,
|
||||
sample,
|
||||
routeConfiguration: routeConfiguration(),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
331
src/routeConfiguration.js
Normal file
331
src/routeConfiguration.js
Normal file
|
|
@ -0,0 +1,331 @@
|
|||
import React from 'react';
|
||||
import {
|
||||
AuthenticationPage,
|
||||
CheckoutPage,
|
||||
ContactDetailsPage,
|
||||
EditListingPage,
|
||||
InboxPage,
|
||||
LandingPage,
|
||||
ListingPage,
|
||||
ManageListingsPage,
|
||||
NotFoundPage,
|
||||
OrderPage,
|
||||
PasswordChangePage,
|
||||
PasswordResetPage,
|
||||
PasswordRecoveryPage,
|
||||
PayoutPreferencesPage,
|
||||
ProfilePage,
|
||||
ProfileSettingsPage,
|
||||
SalePage,
|
||||
SearchPage,
|
||||
SecurityPage,
|
||||
StyleguidePage,
|
||||
EmailVerificationPage,
|
||||
} from './containers';
|
||||
|
||||
// routeConfiguration needs to initialize containers first
|
||||
// Otherwise, components will import form container eventually and
|
||||
// at that point css bundling / imports will happen in wrong order.
|
||||
import { NamedRedirect } from './components';
|
||||
|
||||
export const ACCOUNT_SETTINGS_PAGES = ['ContactDetailsPage', 'PasswordChangePage'];
|
||||
|
||||
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID
|
||||
const draftId = '00000000-0000-0000-0000-000000000000';
|
||||
const draftSlug = 'draft';
|
||||
|
||||
const RedirectToLandingPage = () => <NamedRedirect name="LandingPage" />;
|
||||
|
||||
const routeConfiguration = () => {
|
||||
return [
|
||||
{ path: '/', exact: true, name: 'LandingPage', component: props => <LandingPage {...props} /> },
|
||||
{
|
||||
path: '/s',
|
||||
exact: true,
|
||||
name: 'SearchPage',
|
||||
component: props => <SearchPage {...props} />,
|
||||
loadData: SearchPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/s/filters',
|
||||
exact: true,
|
||||
name: 'SearchFiltersPage',
|
||||
component: props => <SearchPage {...props} tab="filters" />,
|
||||
loadData: SearchPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/s/listings',
|
||||
exact: true,
|
||||
name: 'SearchListingsPage',
|
||||
component: props => <SearchPage {...props} tab="listings" />,
|
||||
loadData: SearchPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/s/map',
|
||||
exact: true,
|
||||
name: 'SearchMapPage',
|
||||
component: props => <SearchPage {...props} tab="map" />,
|
||||
loadData: SearchPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/l',
|
||||
exact: true,
|
||||
name: 'ListingBasePage',
|
||||
component: RedirectToLandingPage,
|
||||
},
|
||||
{
|
||||
path: '/l/:slug/:id',
|
||||
exact: true,
|
||||
name: 'ListingPage',
|
||||
component: props => <ListingPage {...props} tab="listing" />,
|
||||
loadData: ListingPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/l/:slug/:id/book',
|
||||
exact: true,
|
||||
name: 'ListingPage',
|
||||
component: props => <ListingPage {...props} tab="book" />,
|
||||
loadData: ListingPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/l/:slug/:id/checkout',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'CheckoutPage',
|
||||
component: props => <CheckoutPage {...props} />,
|
||||
setInitialValues: CheckoutPage.setInitialValues,
|
||||
},
|
||||
{
|
||||
auth: true,
|
||||
path: '/l/new',
|
||||
exact: true,
|
||||
name: 'NewListingPage',
|
||||
component: () => (
|
||||
<NamedRedirect
|
||||
name="EditListingPage"
|
||||
params={{ slug: draftSlug, id: draftId, type: 'new', tab: 'description' }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
auth: true,
|
||||
path: '/l/:slug/:id/:type/:tab',
|
||||
exact: true,
|
||||
name: 'EditListingPage',
|
||||
component: props => <EditListingPage {...props} />,
|
||||
loadData: EditListingPage.loadData,
|
||||
},
|
||||
|
||||
// Canonical path should be after the `/l/new` path since they
|
||||
// conflict and `new` is not a valid listing UUID.
|
||||
{
|
||||
path: '/l/:id',
|
||||
exact: true,
|
||||
name: 'ListingPageCanonical',
|
||||
component: props => <ListingPage {...props} tab="listing" />,
|
||||
loadData: ListingPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/u',
|
||||
exact: true,
|
||||
name: 'ProfileBasePage',
|
||||
component: RedirectToLandingPage,
|
||||
},
|
||||
{
|
||||
path: '/u/:displayName',
|
||||
exact: true,
|
||||
name: 'ProfilePage',
|
||||
component: props => <ProfilePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/profile-settings',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'ProfileSettingsPage',
|
||||
component: props => <ProfileSettingsPage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
exact: true,
|
||||
name: 'LoginPage',
|
||||
component: props => <AuthenticationPage {...props} tab="login" />,
|
||||
},
|
||||
{
|
||||
path: '/signup',
|
||||
exact: true,
|
||||
name: 'SignupPage',
|
||||
component: props => <AuthenticationPage {...props} tab="signup" />,
|
||||
},
|
||||
{
|
||||
path: '/recover-password',
|
||||
exact: true,
|
||||
name: 'PasswordRecoveryPage',
|
||||
component: props => <PasswordRecoveryPage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/inbox',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'InboxBasePage',
|
||||
component: () => <NamedRedirect name="InboxPage" params={{ tab: 'sales' }} />,
|
||||
},
|
||||
{
|
||||
path: '/inbox/:tab',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'InboxPage',
|
||||
component: props => <InboxPage {...props} />,
|
||||
loadData: InboxPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/order/:id',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'OrderPage',
|
||||
component: RedirectToLandingPage,
|
||||
},
|
||||
{
|
||||
path: '/order/:id/details',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'OrderDetailsPage',
|
||||
component: props => <OrderPage {...props} tab="details" />,
|
||||
loadData: OrderPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/order/:id/discussion',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'OrderDiscussionPage',
|
||||
component: props => <OrderPage {...props} tab="discussion" />,
|
||||
loadData: OrderPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/sale/:id',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'SalePage',
|
||||
component: props => <SalePage {...props} tab="discussion" />,
|
||||
},
|
||||
{
|
||||
path: '/sale/:id/details',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'SaleDetailsPage',
|
||||
component: props => <SalePage {...props} tab="details" />,
|
||||
loadData: SalePage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/sale/:id/discussion',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'SaleDiscussionPage',
|
||||
component: props => <SalePage {...props} tab="discussion" />,
|
||||
loadData: SalePage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/listings',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'ManageListingsPage',
|
||||
component: props => <ManageListingsPage {...props} />,
|
||||
loadData: ManageListingsPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/account',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'AccountSettingsPage',
|
||||
component: () => <NamedRedirect name="ContactDetailsPage" />,
|
||||
},
|
||||
{
|
||||
path: '/account/contact-details',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'ContactDetailsPage',
|
||||
component: props => <ContactDetailsPage {...props} />,
|
||||
loadData: ContactDetailsPage.loadData,
|
||||
},
|
||||
{
|
||||
path: '/account/change-password',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'PasswordChangePage',
|
||||
component: props => <PasswordChangePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/account/payout-preferences',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'PayoutPreferencesPage',
|
||||
component: props => <PayoutPreferencesPage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/account/security',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'SecurityPage',
|
||||
component: props => <SecurityPage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/styleguide',
|
||||
exact: true,
|
||||
name: 'Styleguide',
|
||||
component: props => <StyleguidePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/styleguide/g/:group',
|
||||
exact: true,
|
||||
name: 'StyleguideGroup',
|
||||
component: props => <StyleguidePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/styleguide/c/:component',
|
||||
exact: true,
|
||||
name: 'StyleguideComponent',
|
||||
component: props => <StyleguidePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/styleguide/c/:component/:example',
|
||||
exact: true,
|
||||
name: 'StyleguideComponentExample',
|
||||
component: props => <StyleguidePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/styleguide/c/:component/:example/raw',
|
||||
exact: true,
|
||||
name: 'StyleguideComponentExampleRaw',
|
||||
component: props => <StyleguidePage raw {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/notfound',
|
||||
exact: true,
|
||||
name: 'NotFoundPage',
|
||||
component: props => <NotFoundPage {...props} />,
|
||||
},
|
||||
|
||||
// Do not change this path!
|
||||
//
|
||||
// The API expects that the Starter App implements /reset-password endpoint
|
||||
{
|
||||
path: '/reset-password',
|
||||
exact: true,
|
||||
name: 'PasswordResetPage',
|
||||
component: props => <PasswordResetPage {...props} />,
|
||||
},
|
||||
|
||||
// Do not change this path!
|
||||
//
|
||||
// The API expects that the Starter App implements /verify-email endpoint
|
||||
{
|
||||
path: '/verify-email',
|
||||
auth: true,
|
||||
authPage: 'LoginPage',
|
||||
exact: true,
|
||||
name: 'EmailVerificationPage',
|
||||
component: props => <EmailVerificationPage {...props} />,
|
||||
},
|
||||
];
|
||||
};
|
||||
|
||||
export default routeConfiguration;
|
||||
|
|
@ -1,337 +0,0 @@
|
|||
import React from 'react';
|
||||
import { NamedRedirect } from './components';
|
||||
import {
|
||||
AuthenticationPage,
|
||||
CheckoutPage,
|
||||
ContactDetailsPage,
|
||||
EditListingPage,
|
||||
InboxPage,
|
||||
LandingPage,
|
||||
ListingPage,
|
||||
ManageListingsPage,
|
||||
NotFoundPage,
|
||||
OrderPage,
|
||||
PasswordChangePage,
|
||||
PasswordResetPage,
|
||||
PasswordRecoveryPage,
|
||||
PayoutPreferencesPage,
|
||||
ProfilePage,
|
||||
ProfileSettingsPage,
|
||||
SalePage,
|
||||
SearchPage,
|
||||
SecurityPage,
|
||||
StyleguidePage,
|
||||
EmailVerificationPage,
|
||||
} from './containers';
|
||||
|
||||
export const ACCOUNT_SETTINGS_PAGES = ['ContactDetailsPage', 'PasswordChangePage'];
|
||||
|
||||
// https://en.wikipedia.org/wiki/Universally_unique_identifier#Nil_UUID
|
||||
const draftId = '00000000-0000-0000-0000-000000000000';
|
||||
const draftSlug = 'draft';
|
||||
|
||||
const RedirectToLandingPage = () => <NamedRedirect name="LandingPage" />;
|
||||
|
||||
const routesConfiguration = [
|
||||
{ path: '/', exact: true, name: 'LandingPage', component: props => <LandingPage {...props} /> },
|
||||
{
|
||||
path: '/s',
|
||||
exact: true,
|
||||
name: 'SearchPage',
|
||||
component: props => <SearchPage {...props} />,
|
||||
loadData: (params, search) => SearchPage.loadData(params, search),
|
||||
routes: [
|
||||
{
|
||||
path: '/s/filters',
|
||||
exact: true,
|
||||
name: 'SearchFiltersPage',
|
||||
component: props => <SearchPage {...props} tab="filters" />,
|
||||
loadData: (params, search) => SearchPage.loadData(params, search),
|
||||
},
|
||||
{
|
||||
path: '/s/listings',
|
||||
exact: true,
|
||||
name: 'SearchListingsPage',
|
||||
component: props => <SearchPage {...props} tab="listings" />,
|
||||
loadData: (params, search) => SearchPage.loadData(params, search),
|
||||
},
|
||||
{
|
||||
path: '/s/map',
|
||||
exact: true,
|
||||
name: 'SearchMapPage',
|
||||
component: props => <SearchPage {...props} tab="map" />,
|
||||
loadData: (params, search) => SearchPage.loadData(params, search),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/l',
|
||||
exact: true,
|
||||
name: 'ListingBasePage',
|
||||
component: RedirectToLandingPage,
|
||||
routes: [
|
||||
{
|
||||
path: '/l/:slug/:id',
|
||||
exact: true,
|
||||
name: 'ListingPage',
|
||||
loadData: (params, search) => ListingPage.loadData(params, search),
|
||||
component: props => <ListingPage {...props} tab="listing" />,
|
||||
},
|
||||
{
|
||||
path: '/l/:slug/:id/book',
|
||||
exact: true,
|
||||
name: 'ListingPage',
|
||||
loadData: (params, search) => ListingPage.loadData(params, search),
|
||||
component: props => <ListingPage {...props} tab="book" />,
|
||||
},
|
||||
{
|
||||
path: '/l/:slug/:id/checkout',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'CheckoutPage',
|
||||
setInitialValues: initialValues => CheckoutPage.setInitialValues(initialValues),
|
||||
component: props => <CheckoutPage {...props} />,
|
||||
},
|
||||
{
|
||||
auth: true,
|
||||
path: '/l/new',
|
||||
exact: true,
|
||||
name: 'NewListingPage',
|
||||
component: () => (
|
||||
<NamedRedirect
|
||||
name="EditListingPage"
|
||||
params={{ slug: draftSlug, id: draftId, type: 'new', tab: 'description' }}
|
||||
/>
|
||||
),
|
||||
},
|
||||
{
|
||||
auth: true,
|
||||
path: '/l/:slug/:id/:type/:tab',
|
||||
exact: true,
|
||||
name: 'EditListingPage',
|
||||
component: props => <EditListingPage {...props} />,
|
||||
loadData: (params, search) => EditListingPage.loadData(params, search),
|
||||
},
|
||||
|
||||
// Canonical path should be after the `/l/new` path since they
|
||||
// conflict and `new` is not a valid listing UUID.
|
||||
{
|
||||
path: '/l/:id',
|
||||
exact: true,
|
||||
name: 'ListingPageCanonical',
|
||||
loadData: (params, search) => ListingPage.loadData(params, search),
|
||||
component: props => <ListingPage {...props} tab="listing" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/u',
|
||||
exact: true,
|
||||
name: 'ProfileBasePage',
|
||||
component: RedirectToLandingPage,
|
||||
routes: [
|
||||
{
|
||||
path: '/u/:displayName',
|
||||
exact: true,
|
||||
name: 'ProfilePage',
|
||||
component: props => <ProfilePage {...props} />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/profile-settings',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'ProfileSettingsPage',
|
||||
component: props => <ProfileSettingsPage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/login',
|
||||
exact: true,
|
||||
name: 'LoginPage',
|
||||
component: props => <AuthenticationPage {...props} tab="login" />,
|
||||
},
|
||||
{
|
||||
path: '/signup',
|
||||
exact: true,
|
||||
name: 'SignupPage',
|
||||
component: props => <AuthenticationPage {...props} tab="signup" />,
|
||||
},
|
||||
{
|
||||
path: '/recover-password',
|
||||
exact: true,
|
||||
name: 'PasswordRecoveryPage',
|
||||
component: props => <PasswordRecoveryPage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/inbox',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'InboxBasePage',
|
||||
component: () => <NamedRedirect name="InboxPage" params={{ tab: 'sales' }} />,
|
||||
},
|
||||
{
|
||||
path: '/inbox/:tab',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'InboxPage',
|
||||
component: props => <InboxPage {...props} />,
|
||||
loadData: (params, search) => InboxPage.loadData(params, search),
|
||||
},
|
||||
{
|
||||
path: '/order/:id',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'OrderPage',
|
||||
component: RedirectToLandingPage,
|
||||
routes: [
|
||||
{
|
||||
path: '/order/:id/details',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'OrderDetailsPage',
|
||||
component: props => <OrderPage {...props} tab="details" />,
|
||||
loadData: params => OrderPage.loadData(params),
|
||||
},
|
||||
{
|
||||
path: '/order/:id/discussion',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'OrderDiscussionPage',
|
||||
component: props => <OrderPage {...props} tab="discussion" />,
|
||||
loadData: params => OrderPage.loadData(params),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/sale/:id',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'SalePage',
|
||||
component: props => <SalePage {...props} tab="discussion" />,
|
||||
routes: [
|
||||
{
|
||||
path: '/sale/:id/details',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'SaleDetailsPage',
|
||||
component: props => <SalePage {...props} tab="details" />,
|
||||
loadData: params => SalePage.loadData(params),
|
||||
},
|
||||
{
|
||||
path: '/sale/:id/discussion',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'SaleDiscussionPage',
|
||||
component: props => <SalePage {...props} tab="discussion" />,
|
||||
loadData: params => SalePage.loadData(params),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/listings',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'ManageListingsPage',
|
||||
component: props => <ManageListingsPage {...props} />,
|
||||
loadData: (params, search) => ManageListingsPage.loadData(params, search),
|
||||
},
|
||||
{
|
||||
path: '/account',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'AccountSettingsPage',
|
||||
component: () => <NamedRedirect name="ContactDetailsPage" />,
|
||||
routes: [
|
||||
{
|
||||
path: '/account/contact-details',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'ContactDetailsPage',
|
||||
component: props => <ContactDetailsPage {...props} />,
|
||||
loadData: params => ContactDetailsPage.loadData(params),
|
||||
},
|
||||
{
|
||||
path: '/account/change-password',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'PasswordChangePage',
|
||||
component: props => <PasswordChangePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/account/payout-preferences',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'PayoutPreferencesPage',
|
||||
component: props => <PayoutPreferencesPage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/account/security',
|
||||
auth: true,
|
||||
exact: true,
|
||||
name: 'SecurityPage',
|
||||
component: props => <SecurityPage {...props} />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/styleguide',
|
||||
exact: true,
|
||||
name: 'Styleguide',
|
||||
component: props => <StyleguidePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/styleguide/g/:group',
|
||||
exact: true,
|
||||
name: 'StyleguideGroup',
|
||||
component: props => <StyleguidePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/styleguide/c/:component',
|
||||
exact: true,
|
||||
name: 'StyleguideComponent',
|
||||
component: props => <StyleguidePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/styleguide/c/:component/:example',
|
||||
exact: true,
|
||||
name: 'StyleguideComponentExample',
|
||||
component: props => <StyleguidePage {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/styleguide/c/:component/:example/raw',
|
||||
exact: true,
|
||||
name: 'StyleguideComponentExampleRaw',
|
||||
component: props => <StyleguidePage raw {...props} />,
|
||||
},
|
||||
{
|
||||
path: '/notfound',
|
||||
exact: true,
|
||||
name: 'NotFoundPage',
|
||||
component: props => <NotFoundPage {...props} />,
|
||||
},
|
||||
|
||||
// Do not change this path!
|
||||
//
|
||||
// The API expects that the Starter App implements /reset-password endpoint
|
||||
{
|
||||
path: '/reset-password',
|
||||
exact: true,
|
||||
name: 'PasswordResetPage',
|
||||
component: props => <PasswordResetPage {...props} />,
|
||||
},
|
||||
|
||||
// Do not change this path!
|
||||
//
|
||||
// The API expects that the Starter App implements /verify-email endpoint
|
||||
{
|
||||
path: '/verify-email',
|
||||
auth: true,
|
||||
authPage: 'LoginPage',
|
||||
exact: true,
|
||||
name: 'EmailVerificationPage',
|
||||
component: props => <EmailVerificationPage {...props} />,
|
||||
},
|
||||
];
|
||||
|
||||
export default routesConfiguration;
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
exports[`util/contextHelpers.js withFlattenedRoutes should inject the provided routes 1`] = `<withFlattenedRoutes(CompComp) />`;
|
||||
|
||||
exports[`util/contextHelpers.js withFlattenedRoutes should inject the provided routes 2`] = `
|
||||
<div>
|
||||
SomePage
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -1,29 +1,5 @@
|
|||
import React, { PropTypes, Component as ReactComponent } from 'react';
|
||||
import { throttle } from 'lodash';
|
||||
import * as propTypes from './propTypes';
|
||||
|
||||
/**
|
||||
* A higher order component (HOC) to take the flattened routes from
|
||||
* the context that the RoutesProvider component has provided.
|
||||
*
|
||||
* Injects the routes as the `flattenedRoutes` prop in the given
|
||||
* component. Works similarly as `withRouter` in React Router.
|
||||
*/
|
||||
export const withFlattenedRoutes = Component => {
|
||||
const WithFlattenedRoutesComponent = (props, context) => (
|
||||
<Component flattenedRoutes={context.flattenedRoutes} {...props} />
|
||||
);
|
||||
|
||||
WithFlattenedRoutesComponent.displayName = `withFlattenedRoutes(${Component.displayName || Component.name})`;
|
||||
|
||||
const { arrayOf } = PropTypes;
|
||||
|
||||
WithFlattenedRoutesComponent.contextTypes = {
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
};
|
||||
|
||||
return WithFlattenedRoutesComponent;
|
||||
};
|
||||
|
||||
/**
|
||||
* A higher order component (HOC) to take the togglePageClassNames function from
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { Page, RoutesProvider } from '../components';
|
||||
import routesConfiguration from '../routesConfiguration';
|
||||
import { flattenRoutes } from './routes';
|
||||
import { renderDeep, renderShallow } from './test-helpers';
|
||||
import * as propTypes from './propTypes';
|
||||
import { withFlattenedRoutes, withTogglePageClassNames } from './contextHelpers';
|
||||
|
||||
const { arrayOf, func } = PropTypes;
|
||||
|
||||
describe('util/contextHelpers.js', () => {
|
||||
describe('withFlattenedRoutes', () => {
|
||||
it('should inject the provided routes', () => {
|
||||
const CompComp = props => <div>{props.flattenedRoutes[0].name}</div>;
|
||||
CompComp.propTypes = { flattenedRoutes: arrayOf(propTypes.route).isRequired };
|
||||
const Comp = withFlattenedRoutes(CompComp);
|
||||
const routes = [{ name: 'SomePage', path: 'some-page', component: () => null }];
|
||||
const shallowTree = renderShallow(
|
||||
<RoutesProvider flattenedRoutes={routes}>
|
||||
<Comp />
|
||||
</RoutesProvider>
|
||||
);
|
||||
expect(shallowTree).toMatchSnapshot();
|
||||
const deepTree = renderDeep(
|
||||
<RoutesProvider flattenedRoutes={routes}>
|
||||
<Comp />
|
||||
</RoutesProvider>
|
||||
);
|
||||
expect(deepTree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
@ -3,31 +3,14 @@ import { matchPath } from 'react-router-dom';
|
|||
import pathToRegexp from 'path-to-regexp';
|
||||
import { stringify } from './urlHelpers';
|
||||
|
||||
// Flatten the routes config.
|
||||
// TODO: flatten the original config and remove this function
|
||||
export const flattenRoutes = routes =>
|
||||
routes.reduce(
|
||||
(flatRoutes, route) => {
|
||||
const r = { ...route };
|
||||
delete r.routes;
|
||||
flatRoutes.push(r);
|
||||
if (route.routes) {
|
||||
return flatRoutes.concat(flattenRoutes(route.routes));
|
||||
}
|
||||
return flatRoutes;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const findRouteByName = (nameToFind, flattenedRoutes) =>
|
||||
find(flattenedRoutes, route => route.name === nameToFind);
|
||||
const findRouteByName = (nameToFind, routes) => find(routes, route => route.name === nameToFind);
|
||||
|
||||
/**
|
||||
* E.g. ```const toListingPath = toPathByRouteName('ListingPage', routes);```
|
||||
* Then we can generate listing paths with given params (```toListingPath({ id: uuidX })```)
|
||||
*/
|
||||
const toPathByRouteName = (nameToFind, flattenedRoutes) => {
|
||||
const route = findRouteByName(nameToFind, flattenedRoutes);
|
||||
const toPathByRouteName = (nameToFind, routes) => {
|
||||
const route = findRouteByName(nameToFind, routes);
|
||||
if (!route) {
|
||||
throw new Error(`Path "${nameToFind}" was not found.`);
|
||||
}
|
||||
|
|
@ -37,8 +20,8 @@ const toPathByRouteName = (nameToFind, flattenedRoutes) => {
|
|||
/**
|
||||
* Shorthand for single path call. (```pathByRouteName('ListingPage', routes, { id: uuidX });```)
|
||||
*/
|
||||
export const pathByRouteName = (nameToFind, flattenedRoutes, params = {}) =>
|
||||
toPathByRouteName(nameToFind, flattenedRoutes)(params);
|
||||
export const pathByRouteName = (nameToFind, routes, params = {}) =>
|
||||
toPathByRouteName(nameToFind, routes)(params);
|
||||
|
||||
/**
|
||||
* Find the matching routes and their params for the given pathname
|
||||
|
|
@ -48,11 +31,8 @@ export const pathByRouteName = (nameToFind, flattenedRoutes, params = {}) =>
|
|||
*
|
||||
* @return {Array<{ route, params }>} - All matches as { route, params } objects
|
||||
*/
|
||||
export const matchPathname = (pathname, routesConfiguration) => {
|
||||
// TODO: remove flattening when routesConfiguration is flat
|
||||
const flattenedRoutes = flattenRoutes(routesConfiguration);
|
||||
|
||||
return flattenedRoutes.reduce(
|
||||
export const matchPathname = (pathname, routeConfiguration) => {
|
||||
return routeConfiguration.reduce(
|
||||
(matches, route) => {
|
||||
const { path, exact = false } = route;
|
||||
const match = matchPath(pathname, { path, exact });
|
||||
|
|
@ -74,13 +54,13 @@ export const matchPathname = (pathname, routesConfiguration) => {
|
|||
*/
|
||||
export const createResourceLocatorString = (
|
||||
routeName,
|
||||
flattenedRoutes,
|
||||
routes,
|
||||
pathParams = {},
|
||||
searchParams = {}
|
||||
) => {
|
||||
const searchQuery = stringify(searchParams);
|
||||
const includeSearchQuery = searchQuery.length > 0 ? `?${searchQuery}` : '';
|
||||
const path = pathByRouteName(routeName, flattenedRoutes, pathParams);
|
||||
const path = pathByRouteName(routeName, routes, pathParams);
|
||||
return `${path}${includeSearchQuery}`;
|
||||
};
|
||||
|
||||
|
|
@ -91,12 +71,12 @@ export const createResourceLocatorString = (
|
|||
* `dispatch(PageComponent.setInitialValues({ listing, bookingDates }));`
|
||||
*
|
||||
* @param {String} nameToFind - Route name
|
||||
* @param {Array<{ route }>} flattenedRoutes - Route configuration as flattened array.
|
||||
* @param {Array<{ route }>} routes - Route configuration as flat array.
|
||||
*
|
||||
* @return {Route} - Route that matches the given route name.
|
||||
*/
|
||||
export const findRouteByRouteName = (nameToFind, flattenedRoutes) => {
|
||||
const route = findRouteByName(nameToFind, flattenedRoutes);
|
||||
export const findRouteByRouteName = (nameToFind, routes) => {
|
||||
const route = findRouteByName(nameToFind, routes);
|
||||
if (!route) {
|
||||
throw new Error(`Component "${nameToFind}" was not found.`);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,64 +1,48 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { RoutesProvider } from '../components';
|
||||
import routesConfiguration from '../routesConfiguration';
|
||||
import routeConfiguration from '../routeConfiguration';
|
||||
import { renderDeep, renderShallow } from './test-helpers';
|
||||
import * as propTypes from './propTypes';
|
||||
import { createResourceLocatorString, flattenRoutes, findRouteByRouteName } from './routes';
|
||||
import { createResourceLocatorString, findRouteByRouteName } from './routes';
|
||||
|
||||
const { arrayOf } = PropTypes;
|
||||
|
||||
describe('util/routes.js', () => {
|
||||
describe('createResourceLocatorString', () => {
|
||||
const flattenedRoutes = flattenRoutes(routesConfiguration);
|
||||
const routes = routeConfiguration();
|
||||
|
||||
it('should return meaningful strings if parameters are not needed', () => {
|
||||
// default links without params in path or search query
|
||||
expect(
|
||||
createResourceLocatorString('SearchPage', flattenedRoutes, undefined, undefined)
|
||||
).toEqual('/s');
|
||||
expect(createResourceLocatorString('SearchPage', flattenedRoutes, {}, {})).toEqual('/s');
|
||||
expect(createResourceLocatorString('SearchPage', routes, undefined, undefined)).toEqual('/s');
|
||||
expect(createResourceLocatorString('SearchPage', routes, {}, {})).toEqual('/s');
|
||||
});
|
||||
|
||||
it('should return meaningful strings with path parameters', () => {
|
||||
expect(
|
||||
createResourceLocatorString(
|
||||
'ListingPage',
|
||||
flattenedRoutes,
|
||||
{ id: '1234', slug: 'nice-listing' },
|
||||
{}
|
||||
)
|
||||
createResourceLocatorString('ListingPage', routes, { id: '1234', slug: 'nice-listing' }, {})
|
||||
).toEqual('/l/nice-listing/1234');
|
||||
expect(() => createResourceLocatorString('ListingPage', routes, {}, {})).toThrowError(
|
||||
TypeError('Expected "slug" to be defined')
|
||||
);
|
||||
expect(() =>
|
||||
createResourceLocatorString('ListingPage', flattenedRoutes, {}, {})).toThrowError(
|
||||
createResourceLocatorString('ListingPage', routes, { id: '1234' }, {})).toThrowError(
|
||||
TypeError('Expected "slug" to be defined')
|
||||
);
|
||||
expect(() =>
|
||||
createResourceLocatorString(
|
||||
'ListingPage',
|
||||
flattenedRoutes,
|
||||
{ id: '1234' },
|
||||
{}
|
||||
)).toThrowError(TypeError('Expected "slug" to be defined'));
|
||||
expect(() =>
|
||||
createResourceLocatorString(
|
||||
'ListingPage',
|
||||
flattenedRoutes,
|
||||
routes,
|
||||
{ slug: 'nice-listing' },
|
||||
{}
|
||||
)).toThrowError(TypeError('Expected "id" to be defined'));
|
||||
});
|
||||
|
||||
it('should return meaningful strings with search parameters', () => {
|
||||
expect(createResourceLocatorString('SearchPage', flattenedRoutes, {}, { page: 2 })).toEqual(
|
||||
expect(createResourceLocatorString('SearchPage', routes, {}, { page: 2 })).toEqual(
|
||||
'/s?page=2'
|
||||
);
|
||||
expect(
|
||||
createResourceLocatorString(
|
||||
'SearchPage',
|
||||
flattenedRoutes,
|
||||
{},
|
||||
{ address: 'Helsinki', page: 2 }
|
||||
)
|
||||
createResourceLocatorString('SearchPage', routes, {}, { address: 'Helsinki', page: 2 })
|
||||
).toEqual('/s?address=Helsinki&page=2');
|
||||
});
|
||||
|
||||
|
|
@ -66,7 +50,7 @@ describe('util/routes.js', () => {
|
|||
expect(
|
||||
createResourceLocatorString(
|
||||
'ListingPage',
|
||||
flattenedRoutes,
|
||||
routes,
|
||||
{ id: '1234', slug: 'nice-listing' },
|
||||
{ extrainfo: true }
|
||||
)
|
||||
|
|
@ -75,15 +59,15 @@ describe('util/routes.js', () => {
|
|||
});
|
||||
|
||||
describe('findRouteByRouteName', () => {
|
||||
const flattenedRoutes = flattenRoutes(routesConfiguration);
|
||||
const routes = routeConfiguration();
|
||||
it('should return CheckoutPage route', () => {
|
||||
const foundRoute = findRouteByRouteName('CheckoutPage', flattenedRoutes);
|
||||
const foundRoute = findRouteByRouteName('CheckoutPage', routes);
|
||||
expect(foundRoute.name).toEqual('CheckoutPage');
|
||||
expect(typeof foundRoute.setInitialValues).toEqual('function');
|
||||
});
|
||||
|
||||
it('should throw exception for non-existing route (BlaaBlaaPage)', () => {
|
||||
expect(() => findRouteByRouteName('BlaaBlaaPage', flattenedRoutes)).toThrowError(
|
||||
expect(() => findRouteByRouteName('BlaaBlaaPage', routes)).toThrowError(
|
||||
'Component "BlaaBlaaPage" was not found.'
|
||||
);
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue