Skip to content

Commit

Permalink
navigation: allow nesting of multiple levels
Browse files Browse the repository at this point in the history
  • Loading branch information
zemirco committed Dec 8, 2016
1 parent bd4aafb commit 4f2a187
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 27 deletions.
34 changes: 18 additions & 16 deletions components/navigation/Navigation.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
*/

nav {
color: #333;
border-right: 1px solid rgba(0, 0, 0, 0.14);
position: fixed;
top: 0;
Expand All @@ -13,6 +12,7 @@ nav {
width: $navigation-width;
background: #fff;
font-size: 13px;
font-weight: 500;
visibility: hidden;
transform: translateX(-$navigation-width);
transition-duration: 0.2s;
Expand Down Expand Up @@ -51,11 +51,7 @@ nav.is-visible {
.Navigation {
list-style: none;
margin: 0;
padding: 0;
padding: 12px 0 10px;
// hide submenu when collapsed
overflow: hidden;
transition: height 0.25s ease-in-out;
}

.Navigation-link {
Expand All @@ -66,7 +62,6 @@ nav.is-visible {
padding-left: 22px;
padding-right: 16px;
color: #333;
font-weight: 700;
}

.Navigation-link.active {
Expand All @@ -81,7 +76,7 @@ nav.is-visible {
.Navigation-chevron {
// point down \/
transform: rotate(90deg);
transition: transform 0.25s ease-in-out;
transition: transform 0.15s ease-in-out;
}

.Navigation-chevron.is-open {
Expand Down Expand Up @@ -123,18 +118,25 @@ nav.is-visible {
visibility: visible;
}

// make children in submenu grey
.Navigation-item .Navigation-item > .Navigation-link {
color: rgba(0, 0, 0, 0.54);
}

// add frame (border-top and border-bottom) to those navigation items that have sub menus
.Navigation-item.has-children {
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
}

// revert grey color for those children that have children themselves
.Navigation-item.has-children > .Navigation-link {
color: #333;
}

/**
* Nested navigation
*/
.Navigation .Navigation {
padding: 0;
}

.Navigation-item > .Navigation > .Navigation-item > .Navigation-link {
padding: 10px 24px 10px 46px;
font-weight: 400;
}

.Navigation-item > .Navigation > .Navigation-item > .Navigation-link.active {
font-weight: 500;
}
15 changes: 10 additions & 5 deletions components/navigation/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class Item extends React.Component {
this.setState({
isOpen: !this.state.isOpen
})
if (item.links && !subItem) {
if ((item.links && !subItem) || subItem && subItem.links) {
// simply open submenu without notifying parent or changing url
if (e) {
e.preventDefault()
Expand All @@ -46,7 +46,7 @@ class Item extends React.Component {
/**
* Update parent component and open submenu on initial render
*/
componentWillMount () {
componentDidMount () {
if (this.props.location.pathname.includes(this.props.link)) {
this.onClick(this.props.item, this.props.subItem)
}
Expand All @@ -56,7 +56,9 @@ class Item extends React.Component {
const {index, link, text, onClick, links, item, subItem, location} = this.props
const {isOpen} = this.state
return (
<li className='Navigation-item'>
<li className={classnames('Navigation-item', {
'has-children': links && links.length
})}>
<Link
activeClassName='active'
to={link}
Expand All @@ -76,15 +78,18 @@ class Item extends React.Component {
</Link>
{
links &&
<ul className='Navigation' style={{height: isOpen ? (links.length * height) : 0}}>
<ul className='Navigation' style={{
display: isOpen ? null : 'none'
}}>
{links.map((subItem, j) =>
<Item
index={index}
subIndex={j}
key={j}
onClick={onClick}
onClick={subItem.links ? this.onClick : onClick}
text={subItem.text}
link={`${link}${subItem.link}`}
links={subItem.links}
item={item}
subItem={subItem}
location={location}
Expand Down
59 changes: 59 additions & 0 deletions components/navigation/test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,4 +66,63 @@ describe('Navigation', () => {
wrapper.find('.Navigation-overlay').simulate('click')
assert.equal(wrapper.find('.Navigation-overlay').hasClass('is-visible'), false)
})

it('should go multiple levels deep', () => {
const location = {pathname: '/cars/tesla/models-s/p100d', search: '', hash: ''}
const wrapper = mount(
<LocationBroadcast value={location}>
<Navigation links={[
{text: 'Home', link: '/'},
{text: 'About', link: '/about'},
{
text: 'Cars',
link: '/cars',
links: [
{
text: 'Tesla',
link: '/tesla',
links: [
{
text: 'Model S',
link: '/model-s',
links: [
{text: '60', link: '/60'},
{text: '60D', link: '/60d'},
{text: '75', link: '/75'},
{text: '75D', link: '/75d'},
{text: '90D', link: '/90d'},
{text: 'P100D', link: '/p100d'}
]
},
{
text: 'Model X',
link: '/model-x',
links: [
{text: 'Ultra White Seats', link: '/white'},
{text: 'Tan Leather Seats', link: '/tan'},
{text: 'Black Leather Seats', link: '/black'}
]
},
{
text: 'Model 3',
link: '/model-3',
links: [
{text: 'Hardware', link: '/hardware'},
{text: 'Safety', link: '/safety'},
{text: 'Range', link: '/range'}
]
}
]
},
{
text: 'Audi',
link: '/audi'
}
]
}
]} />
</LocationBroadcast>
)
assert(wrapper.find('a[href]="/cars/tesla/model-s/p100d"'))
})
})
31 changes: 25 additions & 6 deletions examples/js/components/navigationRoute.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,30 @@ const defaultNavigation =
<HashRouter>
<div style={{position: 'relative'}}>
<Navigation links={[
{text: 'Home', link: '/'},
{text: 'About', link: '/about'},
{text: 'Settings', link: '/settings', links: [
{text: 'Device', link: '/device'},
{text: 'Ethernet', link: '/ethernet'}
{text: 'Home', link: '/navigation'},
{text: 'About', link: '/navigation/about'},
{text: 'Cars', link: '/navigation/cars', links: [
{text: 'Tesla', link: '/tesla', links: [
{text: 'Model S', link: '/model-s', links: [
{text: '60', link: '/60'},
{text: '60D', link: '/60d'},
{text: '75', link: '/75'},
{text: '75D', link: '/75d'},
{text: '90D', link: '/90d'},
{text: 'P100D', link: '/p100d'}
]},
{text: 'Model X', link: '/model-x', links: [
{text: 'Ultra White Seats', link: '/white'},
{text: 'Tan Leather Seats', link: '/tan'},
{text: 'Black Leather Seats', link: '/black'},
]},
{text: 'Model 3', link: '/model-3', links: [
{text: 'Hardware', link: '/hardware'},
{text: 'Safety', link: '/safety'},
{text: 'Range', link: '/range'},
]}
]},
{text: 'Audi', link: '/audi'}
]}
]} />
</div>
Expand All @@ -31,7 +50,7 @@ export default class NavigationRoute extends React.Component {
The navigation component is visible on large devices. It is automatically hidden on small devices and a hamburger button is displayed, which shows it on click.
</p>
<p>
The max. level of nesting is currently two. You can have a main menu and each menu item can have multiple sub menu items.
You can add as many sub menues as you like. There is not limit since sub menues simply call themselves when they have links. However sub menues do not have extra paddings and are aligned like their parent. Going mutiple levels deep might confuse the user.
</p>
<Playground
docClass={Navigation}
Expand Down

0 comments on commit 4f2a187

Please sign in to comment.