Skip to content

Commit 8125a11

Browse files
committed
Add <Link> component tests
Also, removed deprecated static <Link> props.
1 parent 76438f9 commit 8125a11

File tree

3 files changed

+76
-83
lines changed

3 files changed

+76
-83
lines changed

modules/components/Link.js

+35-83
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,7 @@
11
var React = require('react');
2-
var warning = require('react/lib/warning');
2+
var merge = require('react/lib/merge');
33
var ActiveState = require('../mixins/ActiveState');
4-
var RouteLookup = require('../mixins/RouteLookup');
54
var Transitions = require('../mixins/Transitions');
6-
var withoutProperties = require('../utils/withoutProperties');
7-
var hasOwnProperty = require('../utils/hasOwnProperty');
85

96
function isLeftClickEvent(event) {
107
return event.button === 0;
@@ -14,66 +11,29 @@ function isModifiedEvent(event) {
1411
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
1512
}
1613

17-
/**
18-
* DEPRECATED: A map of <Link> component props that are reserved for use by the
19-
* router and/or React. All other props are used as params that are
20-
* interpolated into the link's path.
21-
*/
22-
var RESERVED_PROPS = {
23-
to: true,
24-
key: true,
25-
className: true,
26-
activeClassName: true,
27-
query: true,
28-
onClick:true,
29-
children: true // ReactChildren
30-
};
31-
3214
/**
3315
* <Link> components are used to create an <a> element that links to a route.
3416
* When that route is active, the link gets an "active" class name (or the
3517
* value of its `activeClassName` prop).
3618
*
3719
* For example, assuming you have the following route:
3820
*
39-
* <Route name="showPost" path="/posts/:postId" handler={Post}/>
21+
* <Route name="showPost" path="/posts/:postID" handler={Post}/>
4022
*
4123
* You could use the following component to link to that route:
4224
*
43-
* <Link to="showPost" params={{postId: "123"}} />
25+
* <Link to="showPost" params={{ postID: "123" }} />
4426
*
4527
* In addition to params, links may pass along query string parameters
4628
* using the `query` prop.
4729
*
48-
* <Link to="showPost" params={{postId: "123"}} query={{show:true}}/>
30+
* <Link to="showPost" params={{ postID: "123" }} query={{ show:true }}/>
4931
*/
5032
var Link = React.createClass({
5133

5234
displayName: 'Link',
5335

54-
mixins: [ ActiveState, RouteLookup, Transitions ],
55-
56-
statics: {
57-
58-
// TODO: Deprecate passing props as params in v1.0
59-
getUnreservedProps: function (props) {
60-
var props = withoutProperties(props, RESERVED_PROPS);
61-
warning(
62-
Object.keys(props).length === 0,
63-
'Passing props for params on <Link>s is deprecated, '+
64-
'please use the `params` property.'
65-
);
66-
return props;
67-
},
68-
69-
/**
70-
* Returns a hash of URL parameters to use in this <Link>'s path.
71-
*/
72-
getParams: function (props) {
73-
return props.params || Link.getUnreservedProps(props);
74-
}
75-
76-
},
36+
mixins: [ ActiveState, Transitions ],
7737

7838
propTypes: {
7939
to: React.PropTypes.string.isRequired,
@@ -95,71 +55,63 @@ var Link = React.createClass({
9555
};
9656
},
9757

98-
/**
99-
* Returns the value of the "href" attribute to use on the DOM element.
100-
*/
101-
getHref: function () {
102-
return this.makeHref(this.props.to, Link.getParams(this.props), this.props.query);
103-
},
104-
105-
/**
106-
* Returns the value of the "class" attribute to use on the DOM element, which contains
107-
* the value of the activeClassName property when this <Link> is active.
108-
*/
109-
getClassName: function () {
110-
var className = this.props.className || '';
111-
112-
if (this.state.isActive)
113-
return className + ' ' + this.props.activeClassName;
114-
115-
return className;
116-
},
117-
118-
componentWillReceiveProps: function (nextProps) {
119-
var params = Link.getParams(nextProps);
120-
58+
updateActiveState: function () {
12159
this.setState({
122-
isActive: this.isActive(nextProps.to, params, nextProps.query)
60+
isActive: this.isActive(this.props.to, this.props.params, this.props.query)
12361
});
12462
},
12563

126-
updateActiveState: function () {
64+
componentWillReceiveProps: function (nextProps) {
12765
this.setState({
128-
isActive: this.isActive(this.props.to, Link.getParams(this.props), this.props.query)
66+
isActive: this.isActive(nextProps.to, nextProps.params, nextProps.query)
12967
});
13068
},
13169

13270
handleClick: function (event) {
13371
var allowTransition = true;
134-
var ret;
72+
var onClickResult;
13573

13674
if (this.props.onClick)
137-
ret = this.props.onClick(event);
75+
onClickResult = this.props.onClick(event);
13876

13977
if (isModifiedEvent(event) || !isLeftClickEvent(event))
14078
return;
14179

142-
if (ret === false || event.defaultPrevented === true)
80+
if (onClickResult === false || event.defaultPrevented === true)
14381
allowTransition = false;
14482

14583
event.preventDefault();
14684

14785
if (allowTransition)
148-
this.transitionTo(this.props.to, Link.getParams(this.props), this.props.query);
86+
this.transitionTo(this.props.to, this.props.params, this.props.query);
87+
},
88+
89+
/**
90+
* Returns the value of the "href" attribute to use on the DOM element.
91+
*/
92+
getHref: function () {
93+
return this.makeHref(this.props.to, this.props.params, this.props.query);
94+
},
95+
96+
/**
97+
* Returns the value of the "class" attribute to use on the DOM element, which contains
98+
* the value of the activeClassName property when this <Link> is active.
99+
*/
100+
getClassName: function () {
101+
var className = this.props.className || '';
102+
103+
if (this.state.isActive)
104+
className += ' ' + this.props.activeClassName;
105+
106+
return className;
149107
},
150108

151109
render: function () {
152-
var props = {
110+
var props = merge(this.props, {
153111
href: this.getHref(),
154112
className: this.getClassName(),
155113
onClick: this.handleClick
156-
};
157-
158-
// pull in props without overriding
159-
for (var propName in this.props) {
160-
if (hasOwnProperty(this.props, propName) && hasOwnProperty(props, propName) === false)
161-
props[propName] = this.props[propName];
162-
}
114+
});
163115

164116
return React.DOM.a(props, this.props.children);
165117
}
+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
var assert = require('assert');
2+
var expect = require('expect');
3+
var React = require('react/addons');
4+
var ReactTestUtils = React.addons.TestUtils;
5+
var Routes = require('../Routes');
6+
var DefaultRoute = require('../DefaultRoute');
7+
var Link = require('../Link');
8+
9+
describe('A Link', function () {
10+
describe('when its route is active', function () {
11+
var Home = React.createClass({
12+
render: function () {
13+
return Link({ ref: 'link', to: 'home', className: 'a-link', activeClassName: 'highlight' });
14+
}
15+
});
16+
17+
var component;
18+
beforeEach(function () {
19+
component = ReactTestUtils.renderIntoDocument(
20+
Routes(null,
21+
DefaultRoute({ name: 'home', handler: Home })
22+
)
23+
);
24+
});
25+
26+
afterEach(function () {
27+
React.unmountComponentAtNode(component.getDOMNode());
28+
});
29+
30+
it('is active', function () {
31+
var linkComponent = component.getActiveRoute().refs.link;
32+
assert(linkComponent.isActive);
33+
});
34+
35+
it('has its active class name', function () {
36+
var linkComponent = component.getActiveRoute().refs.link;
37+
expect(linkComponent.getClassName()).toEqual('a-link highlight');
38+
});
39+
});
40+
});

tests.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
require('./modules/components/__tests__/DefaultRoute-test');
2+
require('./modules/components/__tests__/Link-test');
23
require('./modules/components/__tests__/NotFoundRoute-test');
34
require('./modules/mixins/__tests__/ActiveDelegate-test');
45
require('./modules/mixins/__tests__/AsyncState-test');

0 commit comments

Comments
 (0)