Skip to content

Commit 73daba7

Browse files
committed
[fixed] Show toggle button when using NavBrand
Also simplified the rendering code a bit.
1 parent e4ccf66 commit 73daba7

File tree

3 files changed

+125
-125
lines changed

3 files changed

+125
-125
lines changed

src/NavBrand.js

+21-27
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,32 @@
1-
import React, { cloneElement } from 'react';
2-
import BootstrapMixin from './BootstrapMixin';
31
import classNames from 'classnames';
2+
import React from 'react';
43

5-
const NavBrand = React.createClass({
6-
mixins: [BootstrapMixin],
7-
8-
propTypes: {
9-
bsRole: React.PropTypes.string,
10-
navbar: React.PropTypes.bool
11-
},
12-
13-
getDefaultProps() {
14-
return {
15-
bsRole: 'brand',
16-
navbar: false
17-
};
18-
},
19-
4+
class NavBrand extends React.Component {
205
render() {
21-
let brand;
6+
const {className, children, ...props} = this.props;
227

23-
if (React.isValidElement(this.props.children)) {
24-
brand = cloneElement(this.props.children, {
25-
className: classNames(this.props.children.props.className, 'navbar-brand'),
26-
bsRole: this.props.bsRole,
27-
navbar: this.props.navbar
8+
if (React.isValidElement(children)) {
9+
return React.cloneElement(children, {
10+
className: classNames(
11+
children.props.className, className, 'navbar-brand'
12+
)
2813
});
29-
} else {
30-
brand = <span {...this.props} className="navbar-brand">{this.props.children}</span>;
3114
}
3215

33-
return brand;
16+
return (
17+
<span {...props} className={classNames(className, 'navbar-brand')}>
18+
{children}
19+
</span>
20+
);
3421
}
22+
}
23+
24+
NavBrand.propTypes = {
25+
bsRole: React.PropTypes.string
26+
};
3527

36-
});
28+
NavBrand.defaultProps = {
29+
bsRole: 'brand'
30+
};
3731

3832
export default NavBrand;

src/Navbar.js

+90-74
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
import React, { cloneElement } from 'react';
2-
import BootstrapMixin from './BootstrapMixin';
31
import classNames from 'classnames';
2+
import React from 'react';
3+
import elementType from 'react-prop-types/lib/elementType';
4+
5+
import BootstrapMixin from './BootstrapMixin';
6+
import Grid from './Grid';
7+
import NavBrand from './NavBrand';
48

5-
import ValidComponentChildren from './utils/ValidComponentChildren';
69
import createChainedFunction from './utils/createChainedFunction';
7-
import elementType from 'react-prop-types/lib/elementType';
810
import deprecationWarning from './utils/deprecationWarning';
11+
import ValidComponentChildren from './utils/ValidComponentChildren';
912

1013
const Navbar = React.createClass({
1114
mixins: [BootstrapMixin],
@@ -58,7 +61,8 @@ const Navbar = React.createClass({
5861
return !this._isChanging;
5962
},
6063

61-
componentDidMount() {
64+
componentWillMount() {
65+
// TODO: Use the `deprecated` PropType once we're on React 0.14.
6266
if (this.props.brand) {
6367
deprecationWarning('Navbar brand attribute', 'NavBrand Component');
6468
}
@@ -80,106 +84,118 @@ const Navbar = React.createClass({
8084
return this.props.navExpanded != null ? this.props.navExpanded : this.state.navExpanded;
8185
},
8286

83-
navbrandChild() {
84-
let navChild =
85-
ValidComponentChildren.findValidComponents(this.props.children, child => {
86-
return child.props.bsRole === 'brand';
87-
});
88-
89-
return navChild;
90-
},
91-
92-
hasNavbrandChild() {
93-
return this.navbrandChild().length > 0;
87+
hasNavBrandChild() {
88+
return ValidComponentChildren.findValidComponents(
89+
this.props.children, child => child.props.bsRole === 'brand'
90+
).length > 0;
9491
},
9592

9693
render() {
97-
let classes = this.getBsClassSet();
98-
let ComponentClass = this.props.componentClass;
99-
100-
classes['navbar-fixed-top'] = this.props.fixedTop;
101-
classes['navbar-fixed-bottom'] = this.props.fixedBottom;
102-
classes['navbar-static-top'] = this.props.staticTop;
103-
classes['navbar-inverse'] = this.props.inverse;
104-
105-
let displayHeader = (this.props.brand || this.props.toggleButton || this.props.toggleNavKey != null) && !this.hasNavbrandChild();
94+
const {
95+
brand,
96+
toggleButton,
97+
toggleNavKey,
98+
fixedTop,
99+
fixedBottom,
100+
staticTop,
101+
inverse,
102+
componentClass: ComponentClass,
103+
fluid,
104+
className,
105+
children,
106+
...props
107+
} = this.props;
108+
109+
const classes = this.getBsClassSet();
110+
classes['navbar-fixed-top'] = fixedTop;
111+
classes['navbar-fixed-bottom'] = fixedBottom;
112+
classes['navbar-static-top'] = staticTop;
113+
classes['navbar-inverse'] = inverse;
114+
115+
const showHeader =
116+
(brand || toggleButton || toggleNavKey != null) &&
117+
!this.hasNavBrandChild();
106118

107119
return (
108-
<ComponentClass {...this.props} className={classNames(this.props.className, classes)}>
109-
<div className={this.props.fluid ? 'container-fluid' : 'container'}>
110-
{displayHeader ? this.renderHeader() : null}
111-
{ValidComponentChildren.map(this.props.children, this.renderChildren)}
112-
</div>
120+
<ComponentClass {...props} className={classNames(className, classes)}>
121+
<Grid fluid={fluid}>
122+
{showHeader ? this.renderBrandHeader() : null}
123+
{ValidComponentChildren.map(children, this.renderChild)}
124+
</Grid>
113125
</ComponentClass>
114126
);
115127
},
116128

117-
renderNavBrand(child, index) {
118-
let navbrandEl = cloneElement(child, {
119-
navbar: true,
120-
toggleNavKey: this.props.toggleNavKey,
121-
toggleButton: this.props.toggleButton,
122-
handleToggle: this.handleToggle,
123-
key: child.key ? child.key : index
124-
});
125-
126-
return this.renderHeader(navbrandEl);
127-
},
128-
129-
renderChild(child, index) {
130-
return cloneElement(child, {
131-
navbar: true,
132-
collapsible: this.props.toggleNavKey != null && this.props.toggleNavKey === child.props.eventKey,
133-
expanded: this.props.toggleNavKey != null && this.props.toggleNavKey === child.props.eventKey && this.isNavExpanded(),
134-
key: child.key ? child.key : index
135-
});
136-
},
129+
renderBrandHeader() {
130+
let {brand} = this.props;
131+
if (brand) {
132+
brand = <NavBrand>{brand}</NavBrand>;
133+
}
137134

138-
renderChildren(child, index) {
139-
return (child.props.navbrand) ? this.renderNavBrand(child, index) : this.renderChild(child, index);
135+
return this.renderHeader(brand);
140136
},
141137

142-
renderHeader(navbrandEl) {
143-
let brand = navbrandEl || '';
144-
145-
if (!brand && this.props.brand) {
146-
if (React.isValidElement(this.props.brand)) {
147-
brand = cloneElement(this.props.brand, {
148-
className: classNames(this.props.brand.props.className, 'navbar-brand')
149-
});
150-
} else {
151-
brand = <span className="navbar-brand">{this.props.brand}</span>;
152-
}
153-
}
138+
renderHeader(brand) {
139+
const hasToggle =
140+
this.props.toggleButton || this.props.toggleNavKey != null;
154141

155142
return (
156143
<div className="navbar-header">
157144
{brand}
158-
{(this.props.toggleButton || this.props.toggleNavKey != null) ? this.renderToggleButton() : null}
145+
{hasToggle ? this.renderToggleButton() : null}
159146
</div>
160147
);
161148
},
162149

163-
renderToggleButton() {
164-
let children;
150+
renderChild(child, index) {
151+
const key = child.key != null ? child.key : index;
152+
153+
if (child.props.bsRole === 'brand') {
154+
return React.cloneElement(this.renderHeader(child), {key});
155+
}
156+
157+
const {toggleNavKey} = this.props;
158+
const collapsible =
159+
toggleNavKey != null && toggleNavKey === child.props.eventKey;
165160

166-
if (React.isValidElement(this.props.toggleButton)) {
167-
return cloneElement(this.props.toggleButton, {
168-
className: classNames(this.props.toggleButton.props.className, 'navbar-toggle'),
169-
onClick: createChainedFunction(this.handleToggle, this.props.toggleButton.props.onClick)
161+
return React.cloneElement(child, {
162+
navbar: true,
163+
collapsible,
164+
expanded: collapsible && this.isNavExpanded(),
165+
key
166+
});
167+
},
168+
169+
renderToggleButton() {
170+
const {toggleButton} = this.props;
171+
172+
if (React.isValidElement(toggleButton)) {
173+
return React.cloneElement(toggleButton, {
174+
className: classNames(toggleButton.props.className, 'navbar-toggle'),
175+
onClick: createChainedFunction(
176+
this.handleToggle, toggleButton.props.onClick
177+
)
170178
});
171179
}
172180

173-
children = (this.props.toggleButton != null) ?
174-
this.props.toggleButton : [
181+
let children;
182+
if (toggleButton != null) {
183+
children = toggleButton;
184+
} else {
185+
children = [
175186
<span className="sr-only" key={0}>Toggle navigation</span>,
176187
<span className="icon-bar" key={1}></span>,
177188
<span className="icon-bar" key={2}></span>,
178189
<span className="icon-bar" key={3}></span>
179190
];
191+
}
180192

181193
return (
182-
<button className="navbar-toggle" type="button" onClick={this.handleToggle}>
194+
<button
195+
type="button"
196+
onClick={this.handleToggle}
197+
className="navbar-toggle"
198+
>
183199
{children}
184200
</button>
185201
);

test/NavbarSpec.js

+14-24
Original file line numberDiff line numberDiff line change
@@ -147,30 +147,6 @@ describe('Navbar', () => {
147147
assert.equal(React.findDOMNode(brands[0]).innerText, 'Brand');
148148
});
149149

150-
it('Should pass navbar prop to navbrand', () => {
151-
let instance = ReactTestUtils.renderIntoDocument(
152-
<Navbar>
153-
<NavBrand>Brand</NavBrand>
154-
</Navbar>
155-
);
156-
157-
let brand = ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'navbar-brand');
158-
159-
assert.ok(brand.props.navbar);
160-
});
161-
162-
it('Should pass navbar prop to navbrand inner element', () => {
163-
let instance = ReactTestUtils.renderIntoDocument(
164-
<Navbar>
165-
<NavBrand><a href>Brand</a></NavBrand>
166-
</Navbar>
167-
);
168-
169-
let brand = ReactTestUtils.findRenderedDOMComponentWithClass(instance, 'navbar-brand');
170-
171-
assert.ok(brand.props.navbar);
172-
});
173-
174150
it('Should pass navbar prop to navs', () => {
175151
let instance = ReactTestUtils.renderIntoDocument(
176152
<Navbar>
@@ -236,4 +212,18 @@ describe('Navbar', () => {
236212

237213
assert.ok(header);
238214
});
215+
216+
it('Should show toggle button when using NavBrand', () => {
217+
const instance = ReactTestUtils.renderIntoDocument(
218+
<Navbar toggleNavKey={0}>
219+
<NavBrand>Brand</NavBrand>
220+
<Nav eventKey={0} />
221+
</Navbar>
222+
);
223+
224+
const toggle = ReactTestUtils.findRenderedDOMComponentWithClass(
225+
instance, 'navbar-toggle'
226+
);
227+
expect(toggle).to.be.ok;
228+
});
239229
});

0 commit comments

Comments
 (0)