Skip to content

Commit

Permalink
[added] CollapsableNav implements bootstrap markup for navbar-collapse
Browse files Browse the repository at this point in the history
  • Loading branch information
jamesakers committed Mar 12, 2015
1 parent 37c3947 commit 4fae871
Show file tree
Hide file tree
Showing 7 changed files with 235 additions and 0 deletions.
23 changes: 23 additions & 0 deletions docs/examples/CollapsableNav.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
var navbarInstance = (
<Navbar brand="React-Bootstrap" toggleNavKey={0}>
<CollapsableNav eventKey={0}> {/* This is the eventKey referenced */}
<Nav navbar>
<NavItem eventKey={1} href="#">Link</NavItem>
<NavItem eventKey={2} href="#">Link</NavItem>
<DropdownButton eventKey={3} title="Dropdown">
<MenuItem eventKey="1">Action</MenuItem>
<MenuItem eventKey="2">Another action</MenuItem>
<MenuItem eventKey="3">Something else here</MenuItem>
<MenuItem divider />
<MenuItem eventKey="4">Separated link</MenuItem>
</DropdownButton>
</Nav>
<Nav navbar right>
<NavItem eventKey={1} href="#">Link Right</NavItem>
<NavItem eventKey={2} href="#">Link Right</NavItem>
</Nav>
</CollapsableNav>
</Navbar>
);

React.render(navbarInstance, mountNode);
10 changes: 10 additions & 0 deletions docs/src/ComponentsPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,16 @@ var ComponentsPage = React.createClass({
</pre>
</div>
<ReactPlayground codeText={fs.readFileSync(__dirname + '/../examples/NavbarCollapsable.js', 'utf8')} />

<h3>Mobile Friendly (Multiple Nav Components)</h3>
<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>
<div className="bs-callout bs-callout-info">
<h4>Div collapse</h4>
<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>
<pre>&lt;div class="collapse navbar-collapse"&gt;&lt;/div&gt;</pre>
</div>

<ReactPlayground codeText={fs.readFileSync(__dirname + '/../examples/CollapsableNav.js', 'utf8')} />
</div>

{/* Tabbed Areas */}
Expand Down
1 change: 1 addition & 0 deletions docs/src/ReactPlayground.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ var Button = require('../../lib/Button');
var ButtonGroup = require('../../lib/ButtonGroup');
var ButtonToolbar = require('../../lib/ButtonToolbar');
var CollapsableMixin = require('../../lib/CollapsableMixin');
var CollapsableNav = require('../../lib/CollapsableNav');
var Carousel = require('../../lib/Carousel');
var CarouselItem = require('../../lib/CarouselItem');
var Col = require('../../lib/Col');
Expand Down
110 changes: 110 additions & 0 deletions src/CollapsableNav.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
var React = require('react');
var joinClasses = require('./utils/joinClasses');
var BootstrapMixin = require('./BootstrapMixin');
var CollapsableMixin = require('./CollapsableMixin');
var classSet = require('./utils/classSet');
var domUtils = require('./utils/domUtils');
var cloneWithProps = require('./utils/cloneWithProps');

var ValidComponentChildren = require('./utils/ValidComponentChildren');
var createChainedFunction = require('./utils/createChainedFunction');


var CollapsableNav = React.createClass({
mixins: [BootstrapMixin, CollapsableMixin],

propTypes: {
onSelect: React.PropTypes.func,
expanded: React.PropTypes.bool,
eventKey: React.PropTypes.any
},

getCollapsableDOMNode: function () {
return this.getDOMNode();
},

getCollapsableDimensionValue: function () {
var height = 0;
var nodes = this.refs;
for (var key in nodes) {
if (nodes.hasOwnProperty(key)) {

var n = nodes[key].getDOMNode()
, h = n.offsetHeight
, computedStyles = domUtils.getComputedStyles(n);

height += (h + parseInt(computedStyles.marginTop, 10) + parseInt(computedStyles.marginBottom, 10));
}
}
return height;
},

render: function () {
/*
* this.props.collapsable is set in NavBar when a eventKey is supplied.
*/
var classes = this.props.collapsable ? this.getCollapsableClassSet() : {};
/*
* prevent duplicating navbar-collapse call if passed as prop. kind of overkill... good cadidate to have check implemented as a util that can
* also be used elsewhere.
*/
if (this.props.className == undefined || this.props.className.split(" ").indexOf('navbar-collapse') == -1)
classes['navbar-collapse'] = this.props.collapsable;

return (
<div eventKey={this.props.eventKey} className={joinClasses(this.props.className, classSet(classes))} >
{ValidComponentChildren.map(this.props.children, (this.props.collapsable) ? this.renderCollapsableNavChildren : this.renderChildren )}
</div>
);
},

getChildActiveProp: function (child) {
if (child.props.active) {
return true;
}
if (this.props.activeKey != null) {
if (child.props.eventKey == this.props.activeKey) {
return true;
}
}
if (this.props.activeHref != null) {
if (child.props.href === this.props.activeHref) {
return true;
}
}

return child.props.active;
},

renderChildren: function (child, index) {
var key = child.key ? child.key : index;
return cloneWithProps(
child,
{
activeKey: this.props.activeKey,
activeHref: this.props.activeHref,
ref: 'nocollapse_' + key,
key: key,
navItem: true
}
);
},

renderCollapsableNavChildren: function (child, index) {
var key = child.key ? child.key : index;
return cloneWithProps(
child,
{
active: this.getChildActiveProp(child),
activeKey: this.props.activeKey,
activeHref: this.props.activeHref,
onSelect: createChainedFunction(child.props.onSelect, this.props.onSelect),
ref: 'collapsable_' + key,
key: key,
navItem: true
}
);
}
});

module.exports = CollapsableNav;
89 changes: 89 additions & 0 deletions test/CollapsableNavSpec.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/*global describe, beforeEach, afterEach, it, assert */

var React = require('react');
var ReactTestUtils = require('react/lib/ReactTestUtils');
var Navbar = require('../lib/Navbar');
var CollapsableNav = require('../lib/CollapsableNav');
var Nav = require('../lib/Nav');
var NavItem = require('../lib/NavItem');

describe('CollapsableNav', function () {
it('Should create div and add collapse class', function () {
var instance = ReactTestUtils.renderIntoDocument(
<Navbar toggleNavKey={1}>
<CollapsableNav eventKey={1}>
<Nav>
<NavItem eventKey={1} ref="item1">Item 1 content</NavItem>
<NavItem eventKey={2} ref="item2">Item 2 content</NavItem>
</Nav>
</CollapsableNav>
</Navbar>
);
assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'navbar-collapse'));
});

it('Should handle multiple Nav elements', function () {
var instance = ReactTestUtils.renderIntoDocument(
<Navbar toggleNavKey={1}>
<CollapsableNav eventKey={1} ref="collapsable_object">
<Nav>
<NavItem eventKey={1} ref="item1">Item 1 content</NavItem>
<NavItem eventKey={2} ref="item2">Item 2 content</NavItem>
</Nav>
<Nav>
<NavItem eventKey={1} ref="item1">Item 1 content</NavItem>
<NavItem eventKey={2} ref="item2">Item 2 content</NavItem>
</Nav>
</CollapsableNav>
</Navbar>
);
assert.ok(ReactTestUtils.findRenderedComponentWithType(instance.refs.collapsable_object.refs.collapsable_0, Nav));
assert.ok(ReactTestUtils.findRenderedComponentWithType(instance.refs.collapsable_object.refs.collapsable_1, Nav));
});

it('Should just render children and move along if not in <Navbar>', function () {
var instance = ReactTestUtils.renderIntoDocument(
<CollapsableNav eventKey={1}>
<Nav>
<NavItem eventKey={1} ref="item1">Item 1 content</NavItem>
<NavItem eventKey={2} ref="item2">Item 2 content</NavItem>
</Nav>
</CollapsableNav>
);
assert.notOk(instance.getDOMNode().className.match(/\navbar-collapse\b/));
assert.ok(ReactTestUtils.findRenderedComponentWithType(instance.refs.nocollapse_0, Nav));
});

it('Should retain childrens classes set by className', function () {
var instance = ReactTestUtils.renderIntoDocument(
<Navbar toggleNavKey={1}>
<CollapsableNav eventKey={1} ref="collapsable_object">
<Nav>
<NavItem eventKey={1} ref="item1" className="foo bar">Item 1 content</NavItem>
<NavItem eventKey={2} ref="item2" className="baz">Item 2 content</NavItem>
</Nav>
</CollapsableNav>
</Navbar>
);
assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance.refs.collapsable_object.refs.collapsable_0, 'foo'));
assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance.refs.collapsable_object.refs.collapsable_0, 'bar'));
assert.ok(ReactTestUtils.findRenderedDOMComponentWithClass(instance.refs.collapsable_object.refs.collapsable_0, 'baz'));
});

it('Should should not duplicate classes', function () {
var instance = ReactTestUtils.renderIntoDocument(
<Navbar toggleNavKey={1}>
<CollapsableNav eventKey={1} ref="collapsable_object" className="foo navbar-collapse">
<Nav>
<NavItem eventKey={1} ref="item1" className="foo bar">Item 1 content</NavItem>
<NavItem eventKey={2} ref="item2" className="baz">Item 2 content</NavItem>
</Nav>
</CollapsableNav>
</Navbar>
);
var classDOM = ReactTestUtils.findRenderedDOMComponentWithTag(instance.refs.collapsable_object, 'DIV').props.className
, class_array = classDOM.split(" ")
, idx = class_array.indexOf('navbar-collapse');
assert.equal(class_array.indexOf('navbar-collapse',idx+1), -1);
});
});
1 change: 1 addition & 0 deletions tools/amd/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ define(function (require) {
Carousel: require('./lib/Carousel'),
CarouselItem: require('./lib/CarouselItem'),
Col: require('./lib/Col'),
CollapsableNav: require('./lib/CollapsableNav'),
CollapsableMixin: require('./lib/CollapsableMixin'),
DropdownButton: require('./lib/DropdownButton'),
DropdownMenu: require('./lib/DropdownMenu'),
Expand Down
1 change: 1 addition & 0 deletions tools/cjs/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
Carousel: require('./Carousel'),
CarouselItem: require('./CarouselItem'),
Col: require('./Col'),
CollapsableNav: require('./CollapsableNav'),
CollapsableMixin: require('./CollapsableMixin'),
DropdownButton: require('./DropdownButton'),
DropdownMenu: require('./DropdownMenu'),
Expand Down

0 comments on commit 4fae871

Please sign in to comment.