Skip to content

Commit 5e2ba03

Browse files
committed
feat(Navs): Add nav components
1 parent d3fcd8d commit 5e2ba03

9 files changed

Lines changed: 388 additions & 0 deletions

File tree

lib/Nav.jsx

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
import React, { PropTypes } from 'react';
2+
import classNames from 'classnames';
3+
4+
const propTypes = {
5+
inline: PropTypes.bool,
6+
disabled: PropTypes.bool,
7+
tabs: PropTypes.bool,
8+
pills: PropTypes.bool,
9+
stacked: PropTypes.bool,
10+
navbar: PropTypes.bool,
11+
tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string])
12+
};
13+
14+
const defaultProps = {
15+
tag: 'ul'
16+
};
17+
18+
class Nav extends React.Component {
19+
constructor(props) {
20+
super(props);
21+
}
22+
23+
render() {
24+
const {
25+
className,
26+
tabs,
27+
pills,
28+
inline,
29+
stacked,
30+
navbar,
31+
tag: Tag,
32+
...attributes
33+
} = this.props;
34+
35+
const classes = classNames(
36+
className,
37+
'nav',
38+
{
39+
'navbar-nav': navbar,
40+
'nav-tabs': tabs,
41+
'nav-pills': pills,
42+
'nav-inline': inline,
43+
'nav-stacked': stacked,
44+
'disabled': attributes.disabled
45+
}
46+
);
47+
48+
return (
49+
<Tag {...attributes} className={classes}/>
50+
);
51+
}
52+
}
53+
54+
Nav.propTypes = propTypes;
55+
Nav.defaultProps = defaultProps;
56+
57+
export default Nav;

lib/NavDropdown.jsx

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import React, { PropTypes } from 'react';
2+
import classNames from 'classnames';
3+
import Dropdown from './Dropdown';
4+
5+
const propTypes = {
6+
children: PropTypes.node,
7+
tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string])
8+
};
9+
10+
const defaultProps = {
11+
tag: 'li'
12+
};
13+
14+
class NavDropdown extends React.Component {
15+
constructor(props) {
16+
super(props);
17+
}
18+
19+
render() {
20+
const {
21+
className,
22+
tag: Tag,
23+
...attributes
24+
} = this.props;
25+
26+
const classes = classNames(
27+
className,
28+
'nav-item'
29+
);
30+
31+
return (
32+
<Dropdown {...attributes} tag={Tag} className={classes} />
33+
);
34+
}
35+
}
36+
37+
NavDropdown.propTypes = propTypes;
38+
NavDropdown.defaultProps = defaultProps;
39+
40+
export default NavDropdown;

lib/NavItem.jsx

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import React, { PropTypes } from 'react';
2+
import classNames from 'classnames';
3+
4+
const propTypes = {
5+
tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string])
6+
};
7+
8+
const defaultProps = {
9+
tag: 'li'
10+
};
11+
12+
class NavItem extends React.Component {
13+
render() {
14+
const {
15+
className,
16+
tag: Tag,
17+
...attributes
18+
} = this.props;
19+
20+
const classes = classNames(
21+
className,
22+
'nav-item'
23+
);
24+
25+
return (
26+
<Tag {...attributes} className={classes}/>
27+
);
28+
}
29+
}
30+
31+
NavItem.propTypes = propTypes;
32+
NavItem.defaultProps = defaultProps;
33+
34+
export default NavItem;

lib/NavLink.jsx

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import React, { PropTypes } from 'react';
2+
import classNames from 'classnames';
3+
4+
const propTypes = {
5+
tag: PropTypes.oneOfType([PropTypes.func, PropTypes.string]),
6+
disabled: PropTypes.bool,
7+
active: PropTypes.bool
8+
};
9+
10+
const defaultProps = {
11+
tag: 'a'
12+
};
13+
14+
class NavLink extends React.Component {
15+
constructor(props) {
16+
super(props);
17+
18+
this.onClick = this.onClick.bind(this);
19+
}
20+
21+
onClick(e) {
22+
if (this.props.disabled) {
23+
return e.preventDefault();
24+
}
25+
26+
if (this.props.href === '#') {
27+
e.preventDefault();
28+
}
29+
30+
if (this.props.onClick) {
31+
this.props.onClick(e);
32+
}
33+
}
34+
35+
render() {
36+
let {
37+
className,
38+
active,
39+
tag: Tag,
40+
...attributes
41+
} = this.props;
42+
43+
const classes = classNames(
44+
className,
45+
'nav-link',
46+
{
47+
'disabled': attributes.disabled,
48+
'active': active
49+
}
50+
);
51+
52+
return (
53+
<Tag {...attributes} onClick={this.onClick} className={classes} />
54+
);
55+
}
56+
}
57+
58+
NavLink.propTypes = propTypes;
59+
NavLink.defaultProps = defaultProps;
60+
61+
export default NavLink;

lib/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
import Container from './Container';
22
import Row from './Row';
33
import Col from './Col';
4+
import Nav from './Nav';
5+
import NavItem from './NavItem';
6+
import NavDropdown from './NavDropdown';
7+
import NavLink from './NavLink';
48
import Button from './Button';
59
import ButtonDropdown from './ButtonDropdown';
610
import ButtonGroup from './ButtonGroup';
@@ -25,6 +29,10 @@ export {
2529
Container,
2630
Row,
2731
Col,
32+
Nav,
33+
NavItem,
34+
NavDropdown,
35+
NavLink,
2836
Button,
2937
ButtonDropdown,
3038
ButtonGroup,

test/Nav.spec.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* eslint react/no-multi-comp: 0, react/prop-types: 0 */
2+
import React from 'react';
3+
import { shallow } from 'enzyme';
4+
import { Nav } from 'reactstrap';;
5+
6+
describe('Nav', () => {
7+
it('should render .nav markup', () => {
8+
const wrapper = shallow(<Nav/>);
9+
10+
expect(wrapper.html()).toBe('<ul class="nav"></ul>');
11+
});
12+
13+
it('should render custom tag', () => {
14+
const wrapper = shallow(<Nav tag="nav"/>);
15+
16+
expect(wrapper.html()).toBe('<nav class="nav"></nav>');
17+
});
18+
19+
it('should render children', () => {
20+
const wrapper = shallow(<Nav>Children</Nav>);
21+
22+
expect(wrapper.html()).toBe('<ul class="nav">Children</ul>');
23+
});
24+
25+
it('should pass additional classNames', () => {
26+
const wrapper = shallow(<Nav className="extra"/>);
27+
28+
expect(wrapper.hasClass('extra')).toBe(true);
29+
expect(wrapper.hasClass('nav')).toBe(true);
30+
});
31+
});

test/NavDropdown.spec.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/* eslint react/no-multi-comp: 0, react/prop-types: 0 */
2+
import React from 'react';
3+
import { mount } from 'enzyme';
4+
import { NavDropdown, DropdownToggle, DropdownMenu, DropdownItem } from 'reactstrap';;
5+
6+
7+
describe('NavDropdown', () => {
8+
let isOpen;
9+
let toggle;
10+
11+
beforeEach(() => {
12+
isOpen = false;
13+
toggle = () => isOpen = !isOpen;
14+
});
15+
16+
it('should render a single child', () => {
17+
const wrapper = mount(<NavDropdown isOpen={isOpen} toggle={toggle}>Ello world</NavDropdown>);
18+
19+
expect(wrapper.text()).toBe('Ello world');
20+
expect(wrapper.find('.nav-item').length).toBe(1);
21+
});
22+
23+
it('should render multiple children when isOpen', () => {
24+
isOpen = true;
25+
const wrapper = mount(
26+
<NavDropdown isOpen={isOpen} toggle={toggle}>
27+
<DropdownToggle>Toggle</DropdownToggle>
28+
<DropdownMenu>
29+
<DropdownItem>Test</DropdownItem>
30+
</DropdownMenu>
31+
</NavDropdown>
32+
);
33+
34+
expect(wrapper.find('.btn').text()).toBe('Toggle');
35+
expect(wrapper.find('.nav-item').length).toBe(1);
36+
expect(wrapper.find('.dropdown-item').length).toBe(1);
37+
expect(wrapper.children().length).toBe(2);
38+
});
39+
});

test/NavItem.spec.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/* eslint react/no-multi-comp: 0, react/prop-types: 0 */
2+
import React from 'react';
3+
import { shallow } from 'enzyme';
4+
import { NavItem } from 'reactstrap';;
5+
6+
describe('NavItem', () => {
7+
it('should render .nav-item markup', () => {
8+
const wrapper = shallow(<NavItem/>);
9+
10+
expect(wrapper.html()).toBe('<li class="nav-item"></li>');
11+
});
12+
13+
it('should render custom tag', () => {
14+
const wrapper = shallow(<NavItem tag="div"/>);
15+
16+
expect(wrapper.html()).toBe('<div class="nav-item"></div>');
17+
});
18+
19+
it('sholid render children', () => {
20+
const wrapper = shallow(<NavItem>Children</NavItem>);
21+
22+
expect(wrapper.html()).toBe('<li class="nav-item">Children</li>');
23+
});
24+
25+
it('should pass additional classNames', () => {
26+
const wrapper = shallow(<NavItem className="extra"/>);
27+
28+
expect(wrapper.hasClass('extra')).toBe(true);
29+
expect(wrapper.hasClass('nav-item')).toBe(true);
30+
});
31+
});

0 commit comments

Comments
 (0)