mirror of
https://github.com/kingomarnajjar/flex-template-web.git
synced 2026-07-26 06:47:17 +10:00
Add support for activeClassName for the NamedLink component
This commit is contained in:
parent
48d6b3a3bd
commit
4045146364
13 changed files with 137 additions and 47 deletions
|
|
@ -3,13 +3,13 @@ exports[`FilterPanel matches snapshot 1`] = `
|
|||
<h1>
|
||||
Filters
|
||||
</h1>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="SearchListingsPage">
|
||||
See studios
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="SearchListingsPage">
|
||||
X
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ exports[`ListingCard matches snapshot 1`] = `
|
|||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -18,7 +18,7 @@ exports[`ListingCard matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
Banyan Studios
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<div>
|
||||
55€ / day
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ exports[`ListingCardSmall matches snapshot 1`] = `
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="ListingPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -17,7 +17,7 @@ exports[`ListingCardSmall matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
Banyan Studios
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<div>
|
||||
(
|
||||
<span>
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ exports[`MapPanel matches snapshot 1`] = `
|
|||
Map
|
||||
</div>
|
||||
<div />
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="SearchFiltersPage">
|
||||
Filters
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="SearchListingsPage">
|
||||
X
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,30 +1,80 @@
|
|||
/**
|
||||
* This component wraps React-Router's Link by providing name-based routing.
|
||||
* (Helps to narrow down the scope of possible format changes to routes.)
|
||||
*
|
||||
* The `name` prop should match a route in the flattened
|
||||
* routesConfiguration passed in context by the RoutesProvider
|
||||
* component. The `params` props is the route params for the route
|
||||
* path of the given route name.
|
||||
*
|
||||
* The `to` prop is an object with the same shape as Link requires,
|
||||
* but without `pathname` that will be generated from the given route
|
||||
* name.
|
||||
*
|
||||
* Some additional props can be passed for the <a> element like
|
||||
* `className` and `style`.
|
||||
*
|
||||
* The component can also be given the `activeClassName` prop that
|
||||
* will be added to the element className if the current URL matches
|
||||
* the one in the generated pathname of the link.
|
||||
*/
|
||||
import React, { PropTypes } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import classNames from 'classnames';
|
||||
import { pathByRouteName, withFlattenedRoutes } from '../../util/routes';
|
||||
import * as propTypes from '../../util/propTypes';
|
||||
|
||||
const NamedLink = props => {
|
||||
const { name, search, hash, state, params, flattenedRoutes, ...rest } = props;
|
||||
export const NamedLinkComponent = props => {
|
||||
const { name, params, flattenedRoutes } = props;
|
||||
|
||||
// Link props
|
||||
const { to, children } = props;
|
||||
|
||||
const pathname = pathByRouteName(name, flattenedRoutes, params);
|
||||
const locationDescriptor = { pathname, search, hash, state };
|
||||
return <Link to={locationDescriptor} {...rest} />;
|
||||
const { match } = props;
|
||||
const active = match.url && match.url === pathname;
|
||||
|
||||
// <a> element props
|
||||
const { className, style, activeClassName } = props;
|
||||
const aElemProps = { className: classNames(className, { [activeClassName]: active }), style };
|
||||
|
||||
return <Link to={{ pathname, ...to }} {...aElemProps}>{children}</Link>;
|
||||
};
|
||||
|
||||
const { arrayOf, object, string } = PropTypes;
|
||||
const { arrayOf, object, string, shape, any } = PropTypes;
|
||||
|
||||
NamedLink.defaultProps = { search: '', hash: '', state: {}, params: {} };
|
||||
NamedLinkComponent.defaultProps = {
|
||||
params: {},
|
||||
to: {},
|
||||
children: null,
|
||||
className: '',
|
||||
style: {},
|
||||
activeClassName: 'NamedLink_active',
|
||||
match: {},
|
||||
};
|
||||
|
||||
NamedLink.propTypes = {
|
||||
// This ensures a nice display name in snapshots etc.
|
||||
NamedLinkComponent.displayName = 'NamedLink';
|
||||
|
||||
NamedLinkComponent.propTypes = {
|
||||
// name of the route in routesConfiguration
|
||||
name: string.isRequired,
|
||||
search: string,
|
||||
hash: string,
|
||||
state: object,
|
||||
// params object for the named route
|
||||
params: object,
|
||||
// Link component props
|
||||
to: shape({ search: string, hash: string, state: object }),
|
||||
children: any,
|
||||
// Note: The Link `replace` prop conflicts with the replace function
|
||||
// from withRouter. If the replace boolean is required for the Link
|
||||
// component, add it here with another name.
|
||||
|
||||
// generic props for the underlying <a> element
|
||||
className: string,
|
||||
style: object,
|
||||
activeClassName: string,
|
||||
// from withFlattenedRoutes
|
||||
flattenedRoutes: arrayOf(propTypes.route).isRequired,
|
||||
// from withRouter
|
||||
match: object,
|
||||
};
|
||||
|
||||
export default withFlattenedRoutes(NamedLink);
|
||||
export default withFlattenedRoutes(withRouter(NamedLinkComponent));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,41 @@
|
|||
import React from 'react';
|
||||
import { RoutesProvider } from '../index';
|
||||
import { renderDeep } from '../../util/test-helpers';
|
||||
import NamedLink from './NamedLink';
|
||||
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',
|
||||
activeClassName,
|
||||
flattenedRoutes: routesConf,
|
||||
match: { url: '/a' },
|
||||
};
|
||||
const bProps = {
|
||||
name: 'BPage',
|
||||
activeClassName,
|
||||
flattenedRoutes: routesConf,
|
||||
match: { url: '/a' },
|
||||
};
|
||||
const tree = renderDeep(
|
||||
<div>
|
||||
<NamedLinkComponent {...aProps}>link to a</NamedLinkComponent>
|
||||
<NamedLinkComponent {...bProps}>link to b</NamedLinkComponent>
|
||||
</div>,
|
||||
);
|
||||
const aLink = tree.children[0];
|
||||
const bLink = tree.children[1];
|
||||
expect(aLink.type).toEqual('a');
|
||||
expect(bLink.type).toEqual('a');
|
||||
expect(aLink.props.className).toEqual(activeClassName);
|
||||
expect(bLink.props.className).toEqual('');
|
||||
});
|
||||
});
|
||||
|
||||
describe('NamedLink', () => {
|
||||
it('should contain correct link', () => {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ exports[`OrderDetailsPanel matches snapshot 1`] = `
|
|||
<p>
|
||||
Cancel booking
|
||||
</p>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="OrderDiscussionPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -36,6 +36,6 @@ exports[`OrderDetailsPanel matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
You have a new message!
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@ exports[`SearchResultsPanel matches snapshot 1`] = `
|
|||
<div>
|
||||
<Menu />
|
||||
<div>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="SearchFiltersPage">
|
||||
Filters
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="SearchMapPage">
|
||||
Map
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
<p>
|
||||
By confirming I accept the booking terms and conditions.
|
||||
</p>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="OrderDetailsPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -27,6 +27,6 @@ exports[`CheckoutPage matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
Confirm & Pay
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</Connect(withRouter(PageLayout))>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ exports[`ListingPage matches snapshot 1`] = `
|
|||
<div>
|
||||
Map
|
||||
</div>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="OrderDetailsPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -131,6 +131,6 @@ exports[`ListingPage matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
Book Banyan Studios
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</Connect(withRouter(PageLayout))>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
exports[`NotFoundPage matches snapshot 1`] = `
|
||||
<Connect(withRouter(PageLayout))
|
||||
title="Page not found">
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="LandingPage">
|
||||
Home
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
</Connect(withRouter(PageLayout))>
|
||||
`;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
exports[`OrderPage matches snapshot 1`] = `
|
||||
<Connect(withRouter(PageLayout))
|
||||
title="Your Banyan Studios booking is confirmed!">
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="OrderDetailsPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -14,8 +14,8 @@ exports[`OrderPage matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
Booking details
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
<withFlattenedRoutes(NamedLink)
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<withFlattenedRoutes(withRouter(NamedLink))
|
||||
name="OrderDiscussionPage"
|
||||
params={
|
||||
Object {
|
||||
|
|
@ -23,7 +23,7 @@ exports[`OrderPage matches snapshot 1`] = `
|
|||
}
|
||||
}>
|
||||
Discussion
|
||||
</withFlattenedRoutes(NamedLink)>
|
||||
</withFlattenedRoutes(withRouter(NamedLink))>
|
||||
<OrderDetailsPanel
|
||||
className="undefined"
|
||||
confirmationCode="X2587X"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,17 @@
|
|||
import React, { PropTypes } from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Link, withRouter } from 'react-router-dom';
|
||||
import { withRouter } from 'react-router-dom';
|
||||
import { logout } from '../../ducks/Auth.ducks';
|
||||
import { NamedLink } from '../../components';
|
||||
|
||||
import css from './Topbar.css';
|
||||
|
||||
/* eslint-disable react/no-danger */
|
||||
const House = () => <span dangerouslySetInnerHTML={{ __html: '🏠' }} />;
|
||||
/* eslint-enable react/no-danger */
|
||||
|
||||
const Topbar = props => {
|
||||
const { isAuthenticated, onLogout, user, push: historyPush } = props;
|
||||
const house = { dangerouslySetInnerHTML: { __html: '🏠' } };
|
||||
const hamburger = { dangerouslySetInnerHTML: { __html: '🍔' } };
|
||||
|
||||
const handleChange = e => {
|
||||
|
|
@ -24,7 +28,9 @@ const Topbar = props => {
|
|||
return (
|
||||
<div className={css.container}>
|
||||
<div>
|
||||
<Link className={css.home} to="/" {...house} />
|
||||
<NamedLink className={css.home} name="LandingPage">
|
||||
<House />
|
||||
</NamedLink>
|
||||
<select value="default" className={css.navDropDown} onChange={handleChange}>
|
||||
<option value="default" {...hamburger} />
|
||||
<option value="/s">Search</option>
|
||||
|
|
@ -51,7 +57,7 @@ const Topbar = props => {
|
|||
{user.email}
|
||||
<button className={css.logoutButton} onClick={handleLogout}>Logout</button>
|
||||
</div>
|
||||
: <Link to="/login">Login</Link>}
|
||||
: <NamedLink name="LogInPage">Login</NamedLink>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue