thinkful-ui
Version:
Shared navigation and UI resources for Thinkful.
144 lines (118 loc) • 3.62 kB
JSX
const cx = require('classnames');
const React = require('react');
const ReactDOM = require('react-dom');
require('./dropdown.less');
/**
* Shared dropdown menu element
* @property data {Array} of items to display in the dropdown,
containing `value` and `displayName`
* @property selectedInd {Int} of the selected index in the list
* @property defaultDisplay {String} of default value that should be displayed
* @property handleChange {Function} to handle dropdown click/change
*/
const Dropdown = React.createClass({
propTypes: {
data: React.PropTypes.arrayOf(
React.PropTypes.oneOfType([
React.PropTypes.string, React.PropTypes.object
])).isRequired,
initialSelectedInd: React.PropTypes.number,
selectedInd: React.PropTypes.number,
defaultDisplay: React.PropTypes.string,
handleChange: React.PropTypes.func.isRequired
},
getInitialState() {
return {open: false};
},
getDefaultProps() {
return {data: []};
},
componentDidMount() {
document.addEventListener('click', this._checkClickAway);
},
componentWillUnmount() {
document.removeEventListener('click', this._checkClickAway);
},
_checkClickAway(e) {
if (!ReactDOM.findDOMNode(this.dropdownButton).contains(e.target)) {
this.setState({open: false});
}
},
_generateNodes() {
let {data} = this.props;
// Translate `data` from an array of strings, if necessary.
data = data.map(item => {
return {
value: _.has(item, 'value') ? item.value : item,
displayName: _.has(item, 'displayName') ? item.displayName : item,
className: item.className,
}
});
return data.map((item, ind) => {
return (
<p
className={cx("dropdown-item", item.className)}
id={ind}
key={ind}
value={item.value}>
{item.displayName}
</p>
);
});
},
_toggleOpen() {
this.setState({open: !this.state.open});
},
_handleChange(event) {
let {handleChange} = this.props;
this._toggleOpen();
handleChange(event);
},
_determineSelectedInd() {
const { data, initialSelectedInd, selectedInd, value } = this.props;
// Case 1: selectedInd or initialSelectedInd provided by props
if (typeof(selectedInd) === 'number') {
return selectedInd;
}
if (typeof(initialSelectedInd) === 'number') {
return initialSelectedInd;
}
// Case 2: get index of `value` from data array
return _.map(data, item => item.value || item).indexOf(value)
},
_getDisplayText() {
const { data, defaultDisplay } = this.props;
const selectedInd = this._determineSelectedInd();
if (selectedInd === -1) {
return defaultDisplay;
}
return _.map(data,
item => item.displayName || item)[selectedInd] || defaultDisplay;
},
render() {
const { className } = this.props;
const dropdownClasses = cx(
'dd-open',
{hidden: !this.state.open});
return (
<div className={cx("dropdown-container", className)}>
<div
className="button dd-button"
onClick={this._toggleOpen}
ref={c => this.dropdownButton = c}
data-clickable>
<span className="dropdown-text">
{this._getDisplayText()}
</span>
<span className="icon-navigatedown" aria-hidden="true"></span>
</div>
<div
className={dropdownClasses}
onClick={this._handleChange}>
{this._generateNodes()}
</div>
</div>
);
}
});
module.exports = Dropdown;