mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
Merge pull request #15 from sharetribe/routes-as-config
Routes in separate config
This commit is contained in:
commit
fd86a9b3db
46 changed files with 850 additions and 1316 deletions
|
|
@ -9,6 +9,7 @@
|
|||
"helmet": "^3.3.0",
|
||||
"lodash": "^4.17.4",
|
||||
"qs": "^6.3.0",
|
||||
"path-to-regexp": "^1.5.3",
|
||||
"react": "^15.4.2",
|
||||
"react-dom": "^15.4.2",
|
||||
"react-helmet": "^4.0.0",
|
||||
|
|
|
|||
222
src/Routes.js
222
src/Routes.js
|
|
@ -1,31 +1,8 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { Match, Miss, Redirect } from 'react-router';
|
||||
import {
|
||||
AuthenticationPage,
|
||||
CheckoutPage,
|
||||
ConversationPage,
|
||||
ContactDetailsPage,
|
||||
EditProfilePage,
|
||||
InboxPage,
|
||||
LandingPage,
|
||||
ListingPage,
|
||||
ManageListingsPage,
|
||||
NotFoundPage,
|
||||
NotificationSettingsPage,
|
||||
OrderPage,
|
||||
PasswordChangePage,
|
||||
PasswordForgottenPage,
|
||||
PaymentMethodsPage,
|
||||
PayoutPreferencesPage,
|
||||
ProfilePage,
|
||||
SalesConversationPage,
|
||||
SearchPage,
|
||||
SecurityPage,
|
||||
} from './containers';
|
||||
|
||||
// This is only used for testing that redirects work correct in the
|
||||
// client and when rendering in the server.
|
||||
const RedirectLandingPage = () => <Redirect to="/" />;
|
||||
import { RouterProvider, RoutesProvider } from './components';
|
||||
import { NotFoundPage } from './containers';
|
||||
import routesConfiguration, { flattenRoutes, pathByRouteName } from './routesConfiguration';
|
||||
|
||||
// Fake authentication module
|
||||
// An example from react-router v4 repository
|
||||
|
|
@ -43,152 +20,63 @@ export const fakeAuth = {
|
|||
},
|
||||
};
|
||||
|
||||
/* eslint-disable react/prop-types, arrow-body-style */
|
||||
// User must be authenticated before he can see certain pages
|
||||
export const MatchWhenAuthorized = ({ component: Component, ...rest }) => (
|
||||
<Match
|
||||
{...rest}
|
||||
render={props => {
|
||||
return fakeAuth.isAuthenticated
|
||||
? <Component {...props} />
|
||||
: <Redirect to={{ pathname: '/login', state: { from: props.location } }} />;
|
||||
}}
|
||||
/>
|
||||
);
|
||||
/* eslint-enable react/prop-types, arrow-body-style */
|
||||
// wrap `Match` and use this everywhere instead, then when
|
||||
// sub routes are added to any route it'll work
|
||||
// This will also check if route needs authentication.
|
||||
/* eslint-disable arrow-body-style */
|
||||
const MatchWithSubRoutes = props => {
|
||||
const { auth, component: Component, ...rest } = props;
|
||||
const canShowComponent = !auth || (auth && fakeAuth.isAuthenticated);
|
||||
return (
|
||||
<Match
|
||||
{...rest}
|
||||
render={matchProps => {
|
||||
return canShowComponent
|
||||
? <Component {...matchProps} />
|
||||
: (
|
||||
<Redirect
|
||||
to={
|
||||
{
|
||||
pathname: pathByRouteName('LogInPage', routesConfiguration, {}),
|
||||
state: { from: matchProps.location },
|
||||
}
|
||||
}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
};
|
||||
/* eslint-enable arrow-body-style */
|
||||
|
||||
class Routes extends React.Component {
|
||||
getChildContext() {
|
||||
return { router: this.props.router };
|
||||
}
|
||||
MatchWithSubRoutes.defaultProps = { auth: false, exactly: false };
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div>
|
||||
<Match exactly pattern="/" component={LandingPage} />
|
||||
const { any, array, bool, func, node, oneOfType, string } = PropTypes;
|
||||
|
||||
{/* Search view */}
|
||||
<Match exactly pattern="/s" component={SearchPage} />
|
||||
MatchWithSubRoutes.propTypes = {
|
||||
pattern: string.isRequired,
|
||||
auth: bool,
|
||||
exactly: bool,
|
||||
name: string.isRequired,
|
||||
component: oneOfType([ func, node ]).isRequired,
|
||||
};
|
||||
|
||||
{/* Listing view */}
|
||||
<Match exactly pattern="/l/:slug" component={ListingPage} />
|
||||
<Match exactly pattern="/l" component={RedirectLandingPage} />
|
||||
const Routes = props => {
|
||||
const flattenedRoutes = flattenRoutes(props.routes);
|
||||
const matches = flattenedRoutes.map(route => <MatchWithSubRoutes key={route.name} {...route} />);
|
||||
|
||||
{/* profile / storefront view */}
|
||||
<Match exactly pattern="/u/:displayName" component={ProfilePage} />
|
||||
<MatchWhenAuthorized exactly pattern="/u/:displayName/edit" component={EditProfilePage} />
|
||||
<Match exactly pattern="/u" component={RedirectLandingPage} />
|
||||
return (
|
||||
<RouterProvider router={props.router}>
|
||||
<RoutesProvider routes={props.routes}>
|
||||
<div>
|
||||
{matches}
|
||||
<Miss component={NotFoundPage} />
|
||||
</div>
|
||||
</RoutesProvider>
|
||||
</RouterProvider>
|
||||
);
|
||||
};
|
||||
|
||||
{/* checkout */}
|
||||
<Match exactly pattern="/checkout/:listingId" component={CheckoutPage} />
|
||||
|
||||
{/* Login and signup */}
|
||||
<Match
|
||||
exactly
|
||||
pattern="/login"
|
||||
component={props => <AuthenticationPage {...props} tab="login" />}
|
||||
/>
|
||||
<Match
|
||||
exactly
|
||||
pattern="/signup"
|
||||
component={props => <AuthenticationPage {...props} tab="signup" />}
|
||||
/>
|
||||
|
||||
{/* Password forgotten */}
|
||||
<Match exactly pattern="/password/forgotten" component={PasswordForgottenPage} />
|
||||
|
||||
{/* Change password */}
|
||||
<Match exactly pattern="/password/change" component={PasswordChangePage} />
|
||||
|
||||
{/* Inbox and filtered views */}
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/inbox"
|
||||
component={props => <InboxPage {...props} filter="inbox" />}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/orders"
|
||||
component={props => <InboxPage {...props} filter="orders" />}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/sales"
|
||||
component={props => <InboxPage {...props} filter="sales" />}
|
||||
/>
|
||||
|
||||
{/* Order/Conversation and mobile views */}
|
||||
<MatchWhenAuthorized exactly pattern="/conversation/:id" component={ConversationPage} />
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/order/:id"
|
||||
component={props => <OrderPage {...props} tab="discussion" />}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/order/:id/discussion"
|
||||
component={props => <OrderPage {...props} tab="discussion" />}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/order/:id/details"
|
||||
component={props => <OrderPage {...props} tab="details" />}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/sale/:id"
|
||||
component={props => <SalesConversationPage {...props} tab="discussion" />}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/sale/:id/discussion"
|
||||
component={props => <SalesConversationPage {...props} tab="discussion" />}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/sale/:id/details"
|
||||
component={props => <SalesConversationPage {...props} tab="details" />}
|
||||
/>
|
||||
|
||||
{/* Manage listings */}
|
||||
<MatchWhenAuthorized exactly pattern="/listings" component={ManageListingsPage} />
|
||||
|
||||
{/* Account settings */}
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/account"
|
||||
component={() => <Redirect to="/account/contact-details" />}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/account/contact-details"
|
||||
component={ContactDetailsPage}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/account/notifications"
|
||||
component={NotificationSettingsPage}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/account/payment-methods"
|
||||
component={PaymentMethodsPage}
|
||||
/>
|
||||
<MatchWhenAuthorized
|
||||
exactly
|
||||
pattern="/account/payout-preferences"
|
||||
component={PayoutPreferencesPage}
|
||||
/>
|
||||
<MatchWhenAuthorized exactly pattern="/account/security" component={SecurityPage} />
|
||||
<Miss component={NotFoundPage} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const { any } = PropTypes;
|
||||
|
||||
Routes.propTypes = { router: any.isRequired };
|
||||
Routes.childContextTypes = { router: React.PropTypes.object };
|
||||
Routes.propTypes = { router: any.isRequired, routes: array.isRequired };
|
||||
|
||||
export default Routes;
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import { BrowserRouter, ServerRouter } from 'react-router';
|
|||
import { Provider } from 'react-redux';
|
||||
import configureStore from './store';
|
||||
import Routes from './Routes';
|
||||
import routesConfiguration from './routesConfiguration';
|
||||
|
||||
export const ClientApp = props => {
|
||||
const { store } = props;
|
||||
|
|
@ -12,7 +13,7 @@ export const ClientApp = props => {
|
|||
<BrowserRouter>
|
||||
{({ router }) => (
|
||||
<Provider store={store}>
|
||||
<Routes router={router} />
|
||||
<Routes router={router} routes={routesConfiguration} />
|
||||
</Provider>
|
||||
)}
|
||||
</BrowserRouter>
|
||||
|
|
@ -29,7 +30,7 @@ export const ServerApp = props => {
|
|||
<ServerRouter location={url} context={context}>
|
||||
{({ router }) => (
|
||||
<Provider store={store}>
|
||||
<Routes router={router} />
|
||||
<Routes router={router} routes={routesConfiguration} />
|
||||
</Provider>
|
||||
)}
|
||||
</ServerRouter>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ describe('Application', () => {
|
|||
const urlTitles = {
|
||||
'/': 'Landing page',
|
||||
'/s': 'Search page',
|
||||
'/l/1234': 'Listing page with listing id: #1234',
|
||||
'/l/listing-title-slug/1234': 'Listing page with listing id: #1234',
|
||||
'/u/1234': 'Profile page with display name: 1234',
|
||||
'/checkout/1234': 'Checkout page: 1234',
|
||||
'/login': 'Authentication page: login tab',
|
||||
|
|
@ -41,10 +41,8 @@ describe('Application', () => {
|
|||
});
|
||||
|
||||
const urlRedirects = {
|
||||
'/inbox': '/login',
|
||||
'/orders': '/login',
|
||||
'/sales': '/login',
|
||||
'/conversation/1234': '/login',
|
||||
'/order/1234': '/login',
|
||||
'/order/1234/discussion': '/login',
|
||||
'/order/1234/details': '/login',
|
||||
|
|
@ -54,8 +52,6 @@ describe('Application', () => {
|
|||
'/listings': '/login',
|
||||
'/account': '/login',
|
||||
'/account/contact-details': '/login',
|
||||
'/account/notifications': '/login',
|
||||
'/account/payment-methods': '/login',
|
||||
'/account/payout-preferences': '/login',
|
||||
'/account/security': '/login',
|
||||
};
|
||||
|
|
|
|||
20
src/components/NamedLink/NamedLink.js
Normal file
20
src/components/NamedLink/NamedLink.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { pathByRouteName } from '../../routesConfiguration';
|
||||
|
||||
const NamedLink = (props, context) => {
|
||||
const { name, params, ...rest } = props;
|
||||
const path = pathByRouteName(name, context.routes, params);
|
||||
|
||||
return <Link to={path} {...rest} />;
|
||||
};
|
||||
|
||||
const { array, object, string } = PropTypes;
|
||||
|
||||
NamedLink.contextTypes = { routes: array };
|
||||
|
||||
NamedLink.defaultProps = { params: {} };
|
||||
|
||||
NamedLink.propTypes = { name: string.isRequired, params: object };
|
||||
|
||||
export default NamedLink;
|
||||
28
src/components/NamedLink/NamedLink.test.js
Normal file
28
src/components/NamedLink/NamedLink.test.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
import React from 'react';
|
||||
import { BrowserRouter } from 'react-router';
|
||||
import { RoutesProvider } from '../index';
|
||||
import Routes from '../../Routes';
|
||||
import NamedLink from './NamedLink';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
describe('NamedLink', () => {
|
||||
it('should contain correct link', () => {
|
||||
const id = 12;
|
||||
const routesConf = [
|
||||
{ pattern: '/somepage/:id', name: 'SomePage', component: () => <div>blaa</div> },
|
||||
];
|
||||
const component = renderer.create(
|
||||
(
|
||||
<BrowserRouter>
|
||||
<RoutesProvider routes={routesConf}>
|
||||
<NamedLink name="SomePage" params={{ id }}>to SomePage</NamedLink>
|
||||
</RoutesProvider>
|
||||
</BrowserRouter>
|
||||
),
|
||||
);
|
||||
const json = component.toJSON();
|
||||
expect(json.type).toEqual('a');
|
||||
expect(json.props.href).toEqual(`/somepage/${id}`);
|
||||
expect(json.children).toEqual([ 'to SomePage' ]);
|
||||
});
|
||||
});
|
||||
21
src/components/RouterProvider/RouterProvider.js
Normal file
21
src/components/RouterProvider/RouterProvider.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
class RouterProvider extends Component {
|
||||
getChildContext() {
|
||||
return { router: this.props.router };
|
||||
}
|
||||
|
||||
render() {
|
||||
return React.Children.only(this.props.children);
|
||||
}
|
||||
}
|
||||
|
||||
const { any, node, object } = PropTypes;
|
||||
|
||||
RouterProvider.childContextTypes = { router: object };
|
||||
|
||||
RouterProvider.defaultProps = { children: {} };
|
||||
|
||||
RouterProvider.propTypes = { router: any.isRequired, children: node };
|
||||
|
||||
export default RouterProvider;
|
||||
18
src/components/RouterProvider/RouterProvider.test.js
Normal file
18
src/components/RouterProvider/RouterProvider.test.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import React from 'react';
|
||||
import RouterProvider from './RouterProvider';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
describe('RouterProvider', () => {
|
||||
it('should contain routes from context', () => {
|
||||
const router = { name: 'router in context' };
|
||||
const Child = (props, context) => {
|
||||
return <div>{context.router.name}</div>;
|
||||
};
|
||||
Child.contextTypes = { router: React.PropTypes.object };
|
||||
|
||||
const rendered = renderer
|
||||
.create(<RouterProvider router={router}><Child /></RouterProvider>)
|
||||
.toJSON();
|
||||
expect(rendered.children).toContain('router in context');
|
||||
});
|
||||
});
|
||||
21
src/components/RoutesProvider/RoutesProvider.js
Normal file
21
src/components/RoutesProvider/RoutesProvider.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
import React, { Component, PropTypes } from 'react';
|
||||
|
||||
class RoutesProvider extends Component {
|
||||
getChildContext() {
|
||||
return { routes: this.props.routes };
|
||||
}
|
||||
|
||||
render() {
|
||||
return React.Children.only(this.props.children);
|
||||
}
|
||||
}
|
||||
|
||||
const { array, node } = PropTypes;
|
||||
|
||||
RoutesProvider.childContextTypes = { routes: array };
|
||||
|
||||
RoutesProvider.defaultProps = { children: {} };
|
||||
|
||||
RoutesProvider.propTypes = { routes: array.isRequired, children: node };
|
||||
|
||||
export default RoutesProvider;
|
||||
18
src/components/RoutesProvider/RoutesProvider.test.js
Normal file
18
src/components/RoutesProvider/RoutesProvider.test.js
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import React from 'react';
|
||||
import RoutesProvider from './RoutesProvider';
|
||||
import renderer from 'react-test-renderer';
|
||||
|
||||
describe('RoutesProvider', () => {
|
||||
it('should contain routes from context', () => {
|
||||
const routesConf = [ { name: 'SomePage' } ];
|
||||
const Child = (props, context) => {
|
||||
return <div>{context.routes[0].name}</div>;
|
||||
};
|
||||
Child.contextTypes = { routes: React.PropTypes.array };
|
||||
|
||||
const rendered = renderer
|
||||
.create(<RoutesProvider routes={routesConf}><Child /></RoutesProvider>)
|
||||
.toJSON();
|
||||
expect(rendered.children).toContain('SomePage');
|
||||
});
|
||||
});
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
/* eslint-disable import/prefer-default-export */
|
||||
import NamedLink from './NamedLink/NamedLink';
|
||||
import PageLayout from './PageLayout/PageLayout';
|
||||
import RouterProvider from './RouterProvider/RouterProvider';
|
||||
import RoutesProvider from './RoutesProvider/RoutesProvider';
|
||||
|
||||
export { PageLayout };
|
||||
export { NamedLink, PageLayout, RouterProvider, RoutesProvider };
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`AuthenticationPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`AuthenticationPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`ContactDetailsPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`ContactDetailsPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { PageLayout } from '../../components';
|
||||
|
||||
const ConversationPage = props => {
|
||||
const { params } = props;
|
||||
return (
|
||||
<PageLayout title="Conversation page">
|
||||
<p>Conversation id: {params.id}</p>
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
const { shape, string } = PropTypes;
|
||||
|
||||
ConversationPage.propTypes = { params: shape({ id: string.isRequired }).isRequired };
|
||||
|
||||
export default ConversationPage;
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import React from 'react';
|
||||
import { BrowserRouter } from 'react-router';
|
||||
import renderer from 'react-test-renderer';
|
||||
import ConversationPage from './ConversationPage';
|
||||
|
||||
describe('ConversationPage', () => {
|
||||
it('matches snapshot', () => {
|
||||
const component = renderer.create(
|
||||
(
|
||||
<BrowserRouter>
|
||||
<ConversationPage params={{ id: 'some-conversation-id' }} />
|
||||
</BrowserRouter>
|
||||
),
|
||||
);
|
||||
const tree = component.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
exports[`ConversationPage matches snapshot 1`] = `
|
||||
<div
|
||||
className="">
|
||||
<div
|
||||
className={undefined}>
|
||||
<a
|
||||
className=""
|
||||
href="/"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Home
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/s"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Search
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/u/Bikerrs"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Profile
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/u/Bikerrs/edit"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Edit profile
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/checkout/lid1234"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Checkout
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/inbox"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Inbox
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/orders"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Orders
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/sales"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Sales
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/password/forgotten"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Request password
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/password/change"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Change password
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/listings"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Manage listings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Account settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/contact-details"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payout preferences
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/security"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Security
|
||||
</a>
|
||||
<button
|
||||
onClick={[Function]}>
|
||||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
<h1>
|
||||
Conversation page
|
||||
</h1>
|
||||
<p>
|
||||
Conversation id:
|
||||
some-conversation-id
|
||||
</p>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -19,7 +19,7 @@ exports[`EditProfilePage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`EditProfilePage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`InboxPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`InboxPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`LandingPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`LandingPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -2,21 +2,12 @@ import React, { PropTypes } from 'react';
|
|||
import { Link } from 'react-router';
|
||||
import { PageLayout } from '../../components';
|
||||
|
||||
const ListingPage = ({ params }) => {
|
||||
// Listing id should be located either in the end of slug
|
||||
// - https://example.com/l/listing-title-as-slug-12345
|
||||
// - https://example.com/l/12345
|
||||
const slugAndId = params.slug.split('-');
|
||||
const id = slugAndId[slugAndId.length - 1];
|
||||
|
||||
// TODO: Fetch data from SDK if no data is passed through props
|
||||
return (
|
||||
<PageLayout title={`Listing page with listing id: #${id}`}>
|
||||
<p>Slug: {params.slug}</p>
|
||||
<Link to={`/checkout/${id}`}><button>Book</button></Link>
|
||||
</PageLayout>
|
||||
);
|
||||
};
|
||||
const ListingPage = ({ params }) => (
|
||||
<PageLayout title={`Listing page with listing id: #${params.id}`}>
|
||||
<p>Slug: {params.slug}</p>
|
||||
<Link to={`/checkout/${params.id}`}><button>Book</button></Link>
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
const { shape, string } = PropTypes;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ describe('ListingPage', () => {
|
|||
const component = renderer.create(
|
||||
(
|
||||
<BrowserRouter>
|
||||
<ListingPage params={{ slug: 'really-nice-house-1234' }} />
|
||||
<ListingPage params={{ slug: 'really-nice-house-1234', id: 1234 }} />
|
||||
</BrowserRouter>
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`ListingPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`ListingPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`ManageListingsPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`ManageListingsPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`NotFoundPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`NotFoundPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
import React from 'react';
|
||||
import { PageLayout } from '../../components';
|
||||
|
||||
export default () => <PageLayout title="Notification settings" />
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import React from 'react';
|
||||
import { BrowserRouter } from 'react-router';
|
||||
import renderer from 'react-test-renderer';
|
||||
import NotificationSettingsPage from './NotificationSettingsPage';
|
||||
|
||||
describe('NotificationSettingsPage', () => {
|
||||
it('matches snapshot', () => {
|
||||
const component = renderer.create(
|
||||
(
|
||||
<BrowserRouter>
|
||||
<NotificationSettingsPage />
|
||||
</BrowserRouter>
|
||||
),
|
||||
);
|
||||
const tree = component.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
exports[`NotificationSettingsPage matches snapshot 1`] = `
|
||||
<div
|
||||
className="">
|
||||
<div
|
||||
className={undefined}>
|
||||
<a
|
||||
className=""
|
||||
href="/"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Home
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/s"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Search
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/u/Bikerrs"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Profile
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/u/Bikerrs/edit"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Edit profile
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/checkout/lid1234"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Checkout
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/inbox"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Inbox
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/orders"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Orders
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/sales"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Sales
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/password/forgotten"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Request password
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/password/change"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Change password
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/listings"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Manage listings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Account settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/contact-details"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payout preferences
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/security"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Security
|
||||
</a>
|
||||
<button
|
||||
onClick={[Function]}>
|
||||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
<h1>
|
||||
Notification settings
|
||||
</h1>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -19,7 +19,7 @@ exports[`OrderPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`OrderPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`PasswordChangePage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`PasswordChangePage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`PasswordForgottenPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`PasswordForgottenPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -1,4 +0,0 @@
|
|||
import React from 'react';
|
||||
import { PageLayout } from '../../components';
|
||||
|
||||
export default () => <PageLayout title="Payment methods" />
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import React from 'react';
|
||||
import { BrowserRouter } from 'react-router';
|
||||
import renderer from 'react-test-renderer';
|
||||
import PaymentMethodsPage from './PaymentMethodsPage';
|
||||
|
||||
describe('PaymentMethodsPage', () => {
|
||||
it('matches snapshot', () => {
|
||||
const component = renderer.create(
|
||||
(
|
||||
<BrowserRouter>
|
||||
<PaymentMethodsPage />
|
||||
</BrowserRouter>
|
||||
),
|
||||
);
|
||||
const tree = component.toJSON();
|
||||
expect(tree).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,141 +0,0 @@
|
|||
exports[`PaymentMethodsPage matches snapshot 1`] = `
|
||||
<div
|
||||
className="">
|
||||
<div
|
||||
className={undefined}>
|
||||
<a
|
||||
className=""
|
||||
href="/"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Home
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/s"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Search
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/u/Bikerrs"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Profile
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/u/Bikerrs/edit"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Edit profile
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/checkout/lid1234"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Checkout
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/inbox"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Inbox
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/orders"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Orders
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/sales"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Sales
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/password/forgotten"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Request password
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/password/change"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Change password
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/listings"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Manage listings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Account settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/contact-details"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payout preferences
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/security"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Security
|
||||
</a>
|
||||
<button
|
||||
onClick={[Function]}>
|
||||
Sign out
|
||||
</button>
|
||||
</div>
|
||||
<h1>
|
||||
Payment methods
|
||||
</h1>
|
||||
</div>
|
||||
`;
|
||||
|
|
@ -19,7 +19,7 @@ exports[`PayoutPreferencesPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`PayoutPreferencesPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`ProfilePage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`ProfilePage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`SalesConversationPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`SalesConversationPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
import React from 'react';
|
||||
import { Link } from 'react-router';
|
||||
import { connect } from 'react-redux';
|
||||
import { PageLayout } from '../../components';
|
||||
import { NamedLink, PageLayout } from '../../components';
|
||||
import { addFlashNotification } from '../../ducks/FlashNotification.ducks';
|
||||
import { addFilter } from './SearchPage.ducks';
|
||||
|
||||
export const SearchPageComponent = () => (
|
||||
<PageLayout title="Search page">
|
||||
<Link to="/l/Nice+studio-in-Helsinki-345">Nice studio in Helsinki</Link>
|
||||
<NamedLink name="ListingPage" params={{ slug: 'Nice-studio-in-Helsinki', id: 345 }}>
|
||||
Nice studio in Helsinki
|
||||
</NamedLink>
|
||||
<br />
|
||||
<NamedLink name="LandingPage">LandingPage</NamedLink>
|
||||
</PageLayout>
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -3,13 +3,17 @@ import { BrowserRouter } from 'react-router';
|
|||
import renderer from 'react-test-renderer';
|
||||
import { SearchPageComponent } from './SearchPage';
|
||||
import reducer, { ADD_FILTER, addFilter } from './SearchPage.ducks';
|
||||
import { RoutesProvider } from '../../components';
|
||||
import routesConfiguration from '../../routesConfiguration';
|
||||
|
||||
describe('SearchPageComponent', () => {
|
||||
it('matches snapshot', () => {
|
||||
const component = renderer.create(
|
||||
(
|
||||
<BrowserRouter>
|
||||
<SearchPageComponent />
|
||||
<RoutesProvider routes={routesConfiguration}>
|
||||
<SearchPageComponent />
|
||||
</RoutesProvider>
|
||||
</BrowserRouter>
|
||||
),
|
||||
);
|
||||
|
|
@ -38,7 +42,9 @@ describe('SearchPageDucs', () => {
|
|||
const reduced = reducer([], addFilter1);
|
||||
const reducedWithInitialContent = reducer({ filters: [ addFilter1.payload ] }, addFilter2);
|
||||
expect(reduced).toEqual({ filters: [ addFilter1.payload ] });
|
||||
expect(reducedWithInitialContent).toEqual({ filters: [ addFilter1.payload, addFilter2.payload ] });
|
||||
expect(
|
||||
reducedWithInitialContent,
|
||||
).toEqual({ filters: [ addFilter1.payload, addFilter2.payload ] });
|
||||
});
|
||||
|
||||
it('should handle duplicates ADD_FILTER', () => {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`SearchPageComponent matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`SearchPageComponent matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
@ -139,10 +125,18 @@ exports[`SearchPageComponent matches snapshot 1`] = `
|
|||
</h1>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Nice+studio-in-Helsinki-345"
|
||||
href="/l/Nice-studio-in-Helsinki/345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Nice studio in Helsinki
|
||||
</a>
|
||||
<br />
|
||||
<a
|
||||
className=""
|
||||
href="/"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
LandingPage
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`SecurityPage matches snapshot 1`] = `
|
|||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/l/Bike-Pelago-12345"
|
||||
href="/l/Bike-Pelago/12345"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Listing page
|
||||
|
|
@ -101,20 +101,6 @@ exports[`SecurityPage matches snapshot 1`] = `
|
|||
style={Object {}}>
|
||||
Contact details
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/notifications"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Notification settings
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payment-methods"
|
||||
onClick={[Function]}
|
||||
style={Object {}}>
|
||||
Payment methods
|
||||
</a>
|
||||
<a
|
||||
className=""
|
||||
href="/account/payout-preferences"
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ const Topbar = (props, context) => {
|
|||
<div className={css.container}>
|
||||
<Link to="/" {...linkProps}>Home</Link>
|
||||
<Link to="/s" {...linkProps}>Search</Link>
|
||||
<Link to="/l/Bike-Pelago-12345" {...linkProps}>Listing page</Link>
|
||||
<Link to="/l/Bike-Pelago/12345" {...linkProps}>Listing page</Link>
|
||||
<Link to="/u/Bikerrs" {...linkProps}>Profile</Link>
|
||||
<Link to="/u/Bikerrs/edit" {...linkProps}>Edit profile</Link>
|
||||
<Link to="/checkout/lid1234" {...linkProps}>Checkout</Link>
|
||||
|
|
@ -36,8 +36,6 @@ const Topbar = (props, context) => {
|
|||
<Link to="/listings" {...linkProps}>Manage listings</Link>
|
||||
<Link to="/account" {...linkProps}>Account settings</Link>
|
||||
<Link to="/account/contact-details" {...linkProps}>Contact details</Link>
|
||||
<Link to="/account/notifications" {...linkProps}>Notification settings</Link>
|
||||
<Link to="/account/payment-methods" {...linkProps}>Payment methods</Link>
|
||||
<Link to="/account/payout-preferences" {...linkProps}>Payout preferences</Link>
|
||||
<Link to="/account/security" {...linkProps}>Security</Link>
|
||||
<SignoutButton router={context.router} />
|
||||
|
|
|
|||
|
|
@ -1,17 +1,14 @@
|
|||
import AuthenticationPage from './AuthenticationPage/AuthenticationPage';
|
||||
import CheckoutPage from './CheckoutPage/CheckoutPage';
|
||||
import ConversationPage from './ConversationPage/ConversationPage';
|
||||
import ContactDetailsPage from './ContactDetailsPage/ContactDetailsPage';
|
||||
import EditProfilePage from './EditProfilePage/EditProfilePage';
|
||||
import InboxPage from './InboxPage/InboxPage';
|
||||
import LandingPage from './LandingPage/LandingPage';
|
||||
import ListingPage from './ListingPage/ListingPage';
|
||||
import ManageListingsPage from './ManageListingsPage/ManageListingsPage';
|
||||
import NotificationSettingsPage from './NotificationSettingsPage/NotificationSettingsPage';
|
||||
import OrderPage from './OrderPage/OrderPage';
|
||||
import PasswordChangePage from './PasswordChangePage/PasswordChangePage';
|
||||
import PasswordForgottenPage from './PasswordForgottenPage/PasswordForgottenPage';
|
||||
import PaymentMethodsPage from './PaymentMethodsPage/PaymentMethodsPage';
|
||||
import PayoutPreferencesPage from './PayoutPreferencesPage/PayoutPreferencesPage';
|
||||
import ProfilePage from './ProfilePage/ProfilePage';
|
||||
import SalesConversationPage from './SalesConversationPage/SalesConversationPage';
|
||||
|
|
@ -23,7 +20,6 @@ import Topbar from './Topbar/Topbar';
|
|||
export {
|
||||
AuthenticationPage,
|
||||
CheckoutPage,
|
||||
ConversationPage,
|
||||
ContactDetailsPage,
|
||||
EditProfilePage,
|
||||
InboxPage,
|
||||
|
|
@ -31,11 +27,9 @@ export {
|
|||
ListingPage,
|
||||
ManageListingsPage,
|
||||
NotFoundPage,
|
||||
NotificationSettingsPage,
|
||||
OrderPage,
|
||||
PasswordChangePage,
|
||||
PasswordForgottenPage,
|
||||
PaymentMethodsPage,
|
||||
PayoutPreferencesPage,
|
||||
ProfilePage,
|
||||
SalesConversationPage,
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ import './index.css';
|
|||
|
||||
// If we're in a browser already, render the client application.
|
||||
if (typeof window !== 'undefined') {
|
||||
const preloadedState = window.__PRELOADED_STATE__ || {}; // eslint-disable-line no-underscore-dangle
|
||||
// eslint-disable-next-line no-underscore-dangle
|
||||
const preloadedState = window.__PRELOADED_STATE__ || {};
|
||||
const store = configureStore(preloadedState);
|
||||
|
||||
ReactDOM.render(<ClientApp store={store} />, document.getElementById('root'));
|
||||
|
|
|
|||
230
src/routesConfiguration.js
Normal file
230
src/routesConfiguration.js
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
import React from 'react';
|
||||
import { find } from 'lodash';
|
||||
import { Redirect } from 'react-router';
|
||||
import pathToRegexp from 'path-to-regexp';
|
||||
import {
|
||||
AuthenticationPage,
|
||||
CheckoutPage,
|
||||
ContactDetailsPage,
|
||||
EditProfilePage,
|
||||
InboxPage,
|
||||
LandingPage,
|
||||
ListingPage,
|
||||
ManageListingsPage,
|
||||
OrderPage,
|
||||
PasswordChangePage,
|
||||
PasswordForgottenPage,
|
||||
PayoutPreferencesPage,
|
||||
ProfilePage,
|
||||
SalesConversationPage,
|
||||
SearchPage,
|
||||
SecurityPage,
|
||||
} from './containers';
|
||||
|
||||
// This is only used for testing that redirects work correct in the
|
||||
// client and when rendering in the server.
|
||||
const RedirectLandingPage = () => <Redirect to="/" />;
|
||||
|
||||
const routesConfiguration = [
|
||||
{ pattern: '/', exactly: true, name: 'LandingPage', component: LandingPage },
|
||||
{ pattern: '/s', exactly: true, name: 'SearchPage', component: SearchPage },
|
||||
{
|
||||
pattern: '/l',
|
||||
exactly: true,
|
||||
name: 'ListingBasePage',
|
||||
component: RedirectLandingPage,
|
||||
routes: [
|
||||
{ pattern: '/l/:slug/:id', exactly: true, name: 'ListingPage', component: ListingPage },
|
||||
],
|
||||
},
|
||||
{
|
||||
pattern: '/u',
|
||||
exactly: true,
|
||||
name: 'ProfileBasePage',
|
||||
component: RedirectLandingPage,
|
||||
routes: [
|
||||
{
|
||||
pattern: '/u/:displayName',
|
||||
exactly: true,
|
||||
name: 'ProfilePage',
|
||||
component: ProfilePage,
|
||||
routes: [
|
||||
{
|
||||
pattern: '/u/:displayName/edit',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'EditProfilePage',
|
||||
component: EditProfilePage,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
pattern: '/checkout',
|
||||
exactly: true,
|
||||
name: 'CheckoutBasePage',
|
||||
component: RedirectLandingPage,
|
||||
routes: [
|
||||
{
|
||||
pattern: '/checkout/:listingId',
|
||||
exactly: true,
|
||||
name: 'CheckoutPage',
|
||||
component: CheckoutPage,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
pattern: '/login',
|
||||
exactly: true,
|
||||
name: 'LogInPage',
|
||||
component: props => <AuthenticationPage {...props} tab="login" />,
|
||||
},
|
||||
{
|
||||
pattern: '/signup',
|
||||
exactly: true,
|
||||
name: 'SignUpPage',
|
||||
component: props => <AuthenticationPage {...props} tab="signup" />,
|
||||
},
|
||||
{ pattern: '/password', exactly: true, name: 'PasswordPage', component: PasswordForgottenPage },
|
||||
{
|
||||
pattern: '/password/forgotten',
|
||||
exactly: true,
|
||||
name: 'PasswordForgottenPage',
|
||||
component: PasswordForgottenPage,
|
||||
},
|
||||
{
|
||||
pattern: '/password/change',
|
||||
exactly: true,
|
||||
name: 'PasswordChangePage',
|
||||
component: PasswordChangePage,
|
||||
},
|
||||
{
|
||||
pattern: '/orders',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'OrdersPage',
|
||||
component: props => <InboxPage {...props} filter="orders" />,
|
||||
},
|
||||
{
|
||||
pattern: '/sales',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'SalesPage',
|
||||
component: props => <InboxPage {...props} filter="sales" />,
|
||||
},
|
||||
{
|
||||
pattern: '/order/:id',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'OrderPage',
|
||||
component: props => <OrderPage {...props} tab="discussion" />,
|
||||
routes: [
|
||||
{
|
||||
pattern: '/order/:id/details',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'OrderDetailsPage',
|
||||
component: props => <OrderPage {...props} tab="details" />,
|
||||
},
|
||||
{
|
||||
pattern: '/order/:id/discussion',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'OrderDiscussionPage',
|
||||
component: props => <OrderPage {...props} tab="discussion" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
pattern: '/sale/:id',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'SalePage',
|
||||
component: props => <SalesConversationPage {...props} tab="discussion" />,
|
||||
routes: [
|
||||
{
|
||||
pattern: '/sale/:id/details',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'SaleDetailsPage',
|
||||
component: props => <SalesConversationPage {...props} tab="discussion" />,
|
||||
},
|
||||
{
|
||||
pattern: '/sale/:id/discussion',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'SaleDiscussionPage',
|
||||
component: props => <SalesConversationPage {...props} tab="discussion" />,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
pattern: '/listings',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'ManageListingsPage',
|
||||
component: ManageListingsPage,
|
||||
},
|
||||
{
|
||||
pattern: '/account',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'AccountPage',
|
||||
component: () => <Redirect to="/account/contact-details" />,
|
||||
routes: [
|
||||
{
|
||||
pattern: '/account/contact-details',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'ContactDetailsPage',
|
||||
component: ContactDetailsPage,
|
||||
},
|
||||
{
|
||||
pattern: '/account/payout-preferences',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'PayoutPreferencesPage',
|
||||
component: PayoutPreferencesPage,
|
||||
},
|
||||
{
|
||||
pattern: '/account/security',
|
||||
auth: true,
|
||||
exactly: true,
|
||||
name: 'SecurityPage',
|
||||
component: SecurityPage,
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const flattenRoutes = routesArray =>
|
||||
routesArray.reduce((a, b) => a.concat(b.routes ? [ b ].concat(flattenRoutes(b.routes)) : b), []);
|
||||
|
||||
const findRouteByName = (nameToFind, routes) => {
|
||||
const flattenedRoutes = flattenRoutes(routes);
|
||||
return find(flattenedRoutes, 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, routes) => {
|
||||
const route = findRouteByName(nameToFind, routes);
|
||||
if (!route) {
|
||||
throw new Error(`Path "${nameToFind}" was not found.`);
|
||||
}
|
||||
return pathToRegexp.compile(route.pattern);
|
||||
};
|
||||
|
||||
/**
|
||||
* Shorthand for single path call. (```pathByRouteName('ListingPage', routes, { id: uuidX });```)
|
||||
*/
|
||||
const pathByRouteName = (nameToFind, routes, params = {}) =>
|
||||
toPathByRouteName(nameToFind, routes)(params);
|
||||
|
||||
// Exported helpers
|
||||
export { findRouteByName, flattenRoutes, toPathByRouteName, pathByRouteName };
|
||||
|
||||
export default routesConfiguration;
|
||||
Loading…
Add table
Reference in a new issue