From 0ac65d0a0058da2b741491e557afc90a8d5ea64e Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Mon, 25 Jul 2016 19:34:36 -0400 Subject: [PATCH] Add support for function to --- modules/Link.js | 19 ++++++++++----- modules/__tests__/Link-test.js | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/modules/Link.js b/modules/Link.js index 21b87cb82f..ef6b575647 100644 --- a/modules/Link.js +++ b/modules/Link.js @@ -22,6 +22,10 @@ function isEmptyObject(object) { return true } +function resolveToLocation(to, router) { + return typeof to === 'function' ? to(router.location) : to +} + /** * A is used to create an element that links to a route. * When that route is active, the link gets the value of its @@ -49,7 +53,7 @@ const Link = React.createClass({ }, propTypes: { - to: oneOfType([ string, object ]).isRequired, + to: oneOfType([ string, object, func ]).isRequired, query: object, hash: string, state: object, @@ -74,8 +78,9 @@ const Link = React.createClass({ if (event.defaultPrevented) return + const { router } = this.context invariant( - this.context.router, + router, 's rendered outside of a router context cannot navigate.' ) @@ -89,19 +94,21 @@ const Link = React.createClass({ event.preventDefault() - this.context.router.push(this.props.to) + router.push(resolveToLocation(this.props.to, router)) }, render() { const { to, activeClassName, activeStyle, onlyActiveOnIndex, ...props } = this.props - // Ignore if rendered outside the context of router, simplifies unit testing. + + // Ignore if rendered outside the context of router to simplify unit testing. const { router } = this.context if (router) { - props.href = router.createHref(to) + const toLocation = resolveToLocation(to, router) + props.href = router.createHref(toLocation) if (activeClassName || (activeStyle != null && !isEmptyObject(activeStyle))) { - if (router.isActive(to, onlyActiveOnIndex)) { + if (router.isActive(toLocation, onlyActiveOnIndex)) { if (activeClassName) { if (props.className) { props.className += ` ${activeClassName}` diff --git a/modules/__tests__/Link-test.js b/modules/__tests__/Link-test.js index 141deab8d5..a638db65f7 100644 --- a/modules/__tests__/Link-test.js +++ b/modules/__tests__/Link-test.js @@ -473,4 +473,47 @@ describe('A ', () => { }) }) + describe('with function to', () => { + const LinkWrapper = () => ( + ({ ...location, hash: '#hash' })} + activeClassName="active" + > + Link + + ) + + it('should have the correct href and active state', () => { + render(( + + + + ), node, () => { + const a = node.querySelector('a') + expect(a.getAttribute('href')).toEqual('/hello#hash') + expect(a.className.trim()).toEqual('active') + }) + }) + + it('should transition correctly on click', done => { + const steps = [ + () => { + click(node.querySelector('a'), { button: 0 }) + }, + ({ location }) => { + expect(location.pathname).toEqual('/hello') + expect(location.hash).toEqual('#hash') + } + ] + + const execNextStep = execSteps(steps, done) + + render(( + + + + ), node, execSteps) + }) + }) + })