Commit 9e82e255 by Ari Rizzitano

programmatic menuitem generation

parent 8444b7bd
...@@ -5,6 +5,9 @@ ...@@ -5,6 +5,9 @@
"jsx": true "jsx": true
} }
}, },
"rules": {
"react/no-array-index-key": "off"
},
"settings": { "settings": {
"import/resolver": "webpack" "import/resolver": "webpack"
}, },
......
import React from 'react'; import React from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import PropTypes from 'prop-types';
const classes = { const classes = {
dropdown: 'dropdown', dropdown: 'dropdown',
...@@ -19,18 +20,26 @@ const triggerKeys = { ...@@ -19,18 +20,26 @@ const triggerKeys = {
closeMenu: [ closeMenu: [
'Escape', 'Escape',
], ],
navigateDown: [
'ArrowDown',
],
navigateUp: [
'ArrowUp',
],
}; };
export default class Dropdown extends React.Component { class Dropdown extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.addEvents = this.addEvents.bind(this); this.addEvents = this.addEvents.bind(this);
this.handleDocumentClick = this.handleDocumentClick.bind(this); this.handleDocumentClick = this.handleDocumentClick.bind(this);
this.handleToggleKeyDown = this.handleToggleKeyDown.bind(this); this.handleToggleKeyDown = this.handleToggleKeyDown.bind(this);
this.handleMenuKeyDown = this.handleMenuKeyDown.bind(this);
this.removeEvents = this.removeEvents.bind(this); this.removeEvents = this.removeEvents.bind(this);
this.toggle = this.toggle.bind(this); this.toggle = this.toggle.bind(this);
this.menuItems = [];
this.state = { this.state = {
open: false, open: false,
}; };
...@@ -46,7 +55,7 @@ export default class Dropdown extends React.Component { ...@@ -46,7 +55,7 @@ export default class Dropdown extends React.Component {
componentDidUpdate() { componentDidUpdate() {
if (this.state.open) { if (this.state.open) {
this.firstItem.focus(); this.menuItems[0].focus();
} }
} }
...@@ -65,6 +74,12 @@ export default class Dropdown extends React.Component { ...@@ -65,6 +74,12 @@ export default class Dropdown extends React.Component {
this.toggle(); this.toggle();
} }
handleMenuKeyDown(e) {
if (this.state.open && triggerKeys.closeMenu.indexOf(e.key) > -1) {
this.toggle();
}
}
handleToggleKeyDown(e) { handleToggleKeyDown(e) {
if (!this.state.open && triggerKeys.openMenu.indexOf(e.key) > -1) { if (!this.state.open && triggerKeys.openMenu.indexOf(e.key) > -1) {
this.toggle(); this.toggle();
...@@ -80,52 +95,67 @@ export default class Dropdown extends React.Component { ...@@ -80,52 +95,67 @@ export default class Dropdown extends React.Component {
}); });
} }
generateMenuItems(menuItems) {
return menuItems.map((menuItem, i) => (
<li className={classes.menuItem} key={i}>
<a
role="menuitem"
href={menuItem.href}
onKeyDown={this.handleMenuKeyDown}
ref={(item) => {
this.menuItems[i] = item;
}}
>
{menuItem.label}
</a>
</li>
));
}
render() { render() {
const props = { const menuItems = this.generateMenuItems(this.props.menuItems);
title: 'Settings',
};
return ( return (
<div> <div
<div className={classNames([
classes.dropdown,
{ [classes.show]: this.state.open },
])}
ref={(container) => { this.container = container; }}
>
<button
aria-expanded={this.state.open}
aria-haspopup="true"
className={classNames([ className={classNames([
classes.dropdown, classes.toggle,
{ [classes.show]: this.state.open }, { [classes.active]: this.state.open },
])} ])}
ref={(container) => { this.container = container; }} onClick={this.toggle}
onKeyDown={this.handleToggleKeyDown}
type="button"
ref={(toggleElem) => { this.toggleElem = toggleElem; }}
>
{this.props.title}
</button>
<ul
aria-label={this.props.title}
aria-hidden={!this.state.open}
className={classes.menu}
role="menu"
> >
<button {menuItems}
aria-expanded={this.state.open} </ul>
aria-haspopup="true"
className={classNames([
classes.toggle,
{ [classes.active]: this.state.open },
])}
onClick={this.toggle}
onKeyDown={this.handleToggleKeyDown}
type="button"
ref={(toggleElem) => { this.toggleElem = toggleElem; }}
>
{props.title}
</button>
<ul
aria-label={props.title}
aria-hidden={!this.state.open}
className={classes.menu}
role="menu"
>
<li className={classes.menuItem}>
<a role="menuitem" href="http://google.com" ref={(firstItem) => { this.firstItem = firstItem; }}>Option 1</a>
</li>
<li className={classes.menuItem}>
<a role="menuitem" href="http://google.com">Option 2</a>
</li>
<li className={classes.menuItem}>
<a role="menuitem" href="http://google.com">Option 3</a>
</li>
</ul>
</div>
</div> </div>
); );
} }
} }
Dropdown.propTypes = {
title: PropTypes.string.isRequired,
menuItems: PropTypes.arrayOf(PropTypes.shape({
label: PropTypes.string,
href: PropTypes.string,
})).isRequired,
};
export default Dropdown;
...@@ -94,5 +94,21 @@ storiesOf('InputSelect', module) ...@@ -94,5 +94,21 @@ storiesOf('InputSelect', module)
storiesOf('Dropdown', module) storiesOf('Dropdown', module)
.add('basic usage', () => ( .add('basic usage', () => (
<Dropdown /> <Dropdown
title="Search Engines"
menuItems={[
{
label: 'Google',
href: 'https://google.com',
},
{
label: 'DuckDuckGo',
href: 'https://duckduckgo.com',
},
{
label: 'Yahoo',
href: 'https://yahoo.com',
},
]}
/>
)); ));
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment