Skip to content

Commit 4fae871

Browse files
committed
[added] CollapsableNav implements bootstrap markup for navbar-collapse
1 parent 37c3947 commit 4fae871

File tree

7 files changed

+235
-0
lines changed

7 files changed

+235
-0
lines changed

docs/examples/CollapsableNav.js

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
var navbarInstance = (
2+
<Navbar brand="React-Bootstrap" toggleNavKey={0}>
3+
<CollapsableNav eventKey={0}> {/* This is the eventKey referenced */}
4+
<Nav navbar>
5+
<NavItem eventKey={1} href="#">Link</NavItem>
6+
<NavItem eventKey={2} href="#">Link</NavItem>
7+
<DropdownButton eventKey={3} title="Dropdown">
8+
<MenuItem eventKey="1">Action</MenuItem>
9+
<MenuItem eventKey="2">Another action</MenuItem>
10+
<MenuItem eventKey="3">Something else here</MenuItem>
11+
<MenuItem divider />
12+
<MenuItem eventKey="4">Separated link</MenuItem>
13+
</DropdownButton>
14+
</Nav>
15+
<Nav navbar right>
16+
<NavItem eventKey={1} href="#">Link Right</NavItem>
17+
<NavItem eventKey={2} href="#">Link Right</NavItem>
18+
</Nav>
19+
</CollapsableNav>
20+
</Navbar>
21+
);
22+
23+
React.render(navbarInstance, mountNode);

docs/src/ComponentsPage.js

+10
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,16 @@ var ComponentsPage = React.createClass({
357357
</pre>
358358
</div>
359359
<ReactPlayground codeText={fs.readFileSync(__dirname + '/../examples/NavbarCollapsable.js', 'utf8')} />
360+
361+
<h3>Mobile Friendly (Multiple Nav Components)</h3>
362+
<p>To have a mobile friendly Navbar that handles multiple <code>Nav</code> components use <code>CollapsableNav</code>. The <code>toggleNavKey</code> must still be set, however, the corresponding <code>eventKey</code> must now be on the <code>CollapsableNav</code> component.</p>
363+
<div className="bs-callout bs-callout-info">
364+
<h4>Div collapse</h4>
365+
<p>The <code>navbar-collapse</code> div gets created as the collapsable element which follows the <a href="http://getbootstrap.com/components/#navbar-default">bootstrap</a> collapsable navbar documentation.</p>
366+
<pre>&lt;div class="collapse navbar-collapse"&gt;&lt;/div&gt;</pre>
367+
</div>
368+
369+
<ReactPlayground codeText={fs.readFileSync(__dirname + '/../examples/CollapsableNav.js', 'utf8')} />
360370
</div>
361371

362372
{/* Tabbed Areas */}

docs/src/ReactPlayground.js

+1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ var Button = require('../../lib/Button');
99
var ButtonGroup = require('../../lib/ButtonGroup');
1010
var ButtonToolbar = require('../../lib/ButtonToolbar');
1111
var CollapsableMixin = require('../../lib/CollapsableMixin');
12+
var CollapsableNav = require('../../lib/CollapsableNav');
1213
var Carousel = require('../../lib/Carousel');
1314
var CarouselItem = require('../../lib/CarouselItem');
1415
var Col = require('../../lib/Col');

src/CollapsableNav.jsx

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
var React = require('react');
2+
var joinClasses = require('./utils/joinClasses');
3+
var BootstrapMixin = require('./BootstrapMixin');
4+
var CollapsableMixin = require('./CollapsableMixin');
5+
var classSet = require('./utils/classSet');
6+
var domUtils = require('./utils/domUtils');
7+
var cloneWithProps = require('./utils/cloneWithProps');
8+
9+
var ValidComponentChildren = require('./utils/ValidComponentChildren');
10+
var createChainedFunction = require('./utils/createChainedFunction');
11+
12+
13+
var CollapsableNav = React.createClass({
14+
mixins: [BootstrapMixin, CollapsableMixin],
15+
16+
propTypes: {
17+
onSelect: React.PropTypes.func,
18+
expanded: React.PropTypes.bool,
19+
eventKey: React.PropTypes.any
20+
},
21+
22+
getCollapsableDOMNode: function () {
23+
return this.getDOMNode();
24+
},
25+
26+
getCollapsableDimensionValue: function () {
27+
var height = 0;
28+
var nodes = this.refs;
29+
for (var key in nodes) {
30+
if (nodes.hasOwnProperty(key)) {
31+
32+
var n = nodes[key].getDOMNode()
33+
, h = n.offsetHeight
34+
, computedStyles = domUtils.getComputedStyles(n);
35+
36+
height += (h + parseInt(computedStyles.marginTop, 10) + parseInt(computedStyles.marginBottom, 10));
37+
}
38+
}
39+
return height;
40+
},
41+
42+
render: function () {
43+
/*
44+
* this.props.collapsable is set in NavBar when a eventKey is supplied.
45+
*/
46+
var classes = this.props.collapsable ? this.getCollapsableClassSet() : {};
47+
/*
48+
* prevent duplicating navbar-collapse call if passed as prop. kind of overkill... good cadidate to have check implemented as a util that can
49+
* also be used elsewhere.
50+
*/
51+
if (this.props.className == undefined || this.props.className.split(" ").indexOf('navbar-collapse') == -1)
52+
classes['navbar-collapse'] = this.props.collapsable;
53+
54+
return (
55+
<div eventKey={this.props.eventKey} className={joinClasses(this.props.className, classSet(classes))} >
56+
{ValidComponentChildren.map(this.props.children, (this.props.collapsable) ? this.renderCollapsableNavChildren : this.renderChildren )}
57+
</div>
58+
);
59+
},
60+
61+
getChildActiveProp: function (child) {
62+
if (child.props.active) {
63+
return true;
64+
}
65+
if (this.props.activeKey != null) {
66+
if (child.props.eventKey == this.props.activeKey) {
67+
return true;
68+
}
69+
}
70+
if (this.props.activeHref != null) {
71+
if (child.props.href === this.props.activeHref) {
72+
return true;
73+
}
74+
}
75+
76+
return child.props.active;
77+
},
78+
79+
renderChildren: function (child, index) {
80+
var key = child.key ? child.key : index;
81+
return cloneWithProps(
82+
child,
83+
{
84+
activeKey: this.props.activeKey,
85+
activeHref: this.props.activeHref,
86+
ref: 'nocollapse_' + key,
87+
key: key,
88+
navItem: true
89+
}
90+
);
91+
},
92+
93+
renderCollapsableNavChildren: function (child, index) {
94+
var key = child.key ? child.key : index;
95+
return cloneWithProps(
96+
child,
97+
{
98+
active: this.getChildActiveProp(child),
99+
activeKey: this.props.activeKey,
100+
activeHref: this.props.activeHref,
101+
onSelect: createChainedFunction(child.props.onSelect, this.props.onSelect),
102+
ref: 'collapsable_' + key,
103+
key: key,
104+
navItem: true
105+
}
106+
);
107+
}
108+
});
109+
110+
module.exports = CollapsableNav;

test/CollapsableNavSpec.jsx

+89
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
/*global describe, beforeEach, afterEach, it, assert */
2+
3+
var React = require('react');
4+
var ReactTestUtils = require('react/lib/ReactTestUtils');
5+
var Navbar = require('../lib/Navbar');
6+
var CollapsableNav = require('../lib/CollapsableNav');
7+
var Nav = require('../lib/Nav');
8+
var NavItem = require('../lib/NavItem');
9+
10+
describe('CollapsableNav', function () {
11+
it('Should create div and add collapse class', function () {
12+
var instance = ReactTestUtils.renderIntoDocument(
13+
<Navbar toggleNavKey={1}>
14+
<CollapsableNav eventKey={1}>
15+
<Nav>
16+
<NavItem eventKey={1} ref="item1">Item 1 content</NavItem>
17+
<NavItem eventKey={2} ref="item2">Item 2 content</NavItem>
18+
</Nav>
19+
</CollapsableNav>
20+
</Navbar>
21+
);
22+
assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'navbar-collapse'));
23+
});
24+
25+
it('Should handle multiple Nav elements', function () {
26+
var instance = ReactTestUtils.renderIntoDocument(
27+
<Navbar toggleNavKey={1}>
28+
<CollapsableNav eventKey={1} ref="collapsable_object">
29+
<Nav>
30+
<NavItem eventKey={1} ref="item1">Item 1 content</NavItem>
31+
<NavItem eventKey={2} ref="item2">Item 2 content</NavItem>
32+
</Nav>
33+
<Nav>
34+
<NavItem eventKey={1} ref="item1">Item 1 content</NavItem>
35+
<NavItem eventKey={2} ref="item2">Item 2 content</NavItem>
36+
</Nav>
37+
</CollapsableNav>
38+
</Navbar>
39+
);
40+
assert.ok(ReactTestUtils.findRenderedComponentWithType(instance.refs.collapsable_object.refs.collapsable_0, Nav));
41+
assert.ok(ReactTestUtils.findRenderedComponentWithType(instance.refs.collapsable_object.refs.collapsable_1, Nav));
42+
});
43+
44+
it('Should just render children and move along if not in <Navbar>', function () {
45+
var instance = ReactTestUtils.renderIntoDocument(
46+
<CollapsableNav eventKey={1}>
47+
<Nav>
48+
<NavItem eventKey={1} ref="item1">Item 1 content</NavItem>
49+
<NavItem eventKey={2} ref="item2">Item 2 content</NavItem>
50+
</Nav>
51+
</CollapsableNav>
52+
);
53+
assert.notOk(instance.getDOMNode().className.match(/\navbar-collapse\b/));
54+
assert.ok(ReactTestUtils.findRenderedComponentWithType(instance.refs.nocollapse_0, Nav));
55+
});
56+
57+
it('Should retain childrens classes set by className', function () {
58+
var instance = ReactTestUtils.renderIntoDocument(
59+
<Navbar toggleNavKey={1}>
60+
<CollapsableNav eventKey={1} ref="collapsable_object">
61+
<Nav>
62+
<NavItem eventKey={1} ref="item1" className="foo bar">Item 1 content</NavItem>
63+
<NavItem eventKey={2} ref="item2" className="baz">Item 2 content</NavItem>
64+
</Nav>
65+
</CollapsableNav>
66+
</Navbar>
67+
);
68+
assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance.refs.collapsable_object.refs.collapsable_0, 'foo'));
69+
assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance.refs.collapsable_object.refs.collapsable_0, 'bar'));
70+
assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance.refs.collapsable_object.refs.collapsable_0, 'baz'));
71+
});
72+
73+
it('Should should not duplicate classes', function () {
74+
var instance = ReactTestUtils.renderIntoDocument(
75+
<Navbar toggleNavKey={1}>
76+
<CollapsableNav eventKey={1} ref="collapsable_object" className="foo navbar-collapse">
77+
<Nav>
78+
<NavItem eventKey={1} ref="item1" className="foo bar">Item 1 content</NavItem>
79+
<NavItem eventKey={2} ref="item2" className="baz">Item 2 content</NavItem>
80+
</Nav>
81+
</CollapsableNav>
82+
</Navbar>
83+
);
84+
var classDOM = ReactTestUtils.findRenderedDOMComponentWithTag(instance.refs.collapsable_object, 'DIV').props.className
85+
, class_array = classDOM.split(" ")
86+
, idx = class_array.indexOf('navbar-collapse');
87+
assert.equal(class_array.indexOf('navbar-collapse',idx+1), -1);
88+
});
89+
});

tools/amd/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ define(function (require) {
1616
Carousel: require('./lib/Carousel'),
1717
CarouselItem: require('./lib/CarouselItem'),
1818
Col: require('./lib/Col'),
19+
CollapsableNav: require('./lib/CollapsableNav'),
1920
CollapsableMixin: require('./lib/CollapsableMixin'),
2021
DropdownButton: require('./lib/DropdownButton'),
2122
DropdownMenu: require('./lib/DropdownMenu'),

tools/cjs/main.js

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ module.exports = {
1111
Carousel: require('./Carousel'),
1212
CarouselItem: require('./CarouselItem'),
1313
Col: require('./Col'),
14+
CollapsableNav: require('./CollapsableNav'),
1415
CollapsableMixin: require('./CollapsableMixin'),
1516
DropdownButton: require('./DropdownButton'),
1617
DropdownMenu: require('./DropdownMenu'),

0 commit comments

Comments
 (0)