UNPKG

thinkful-ui

Version:

Shared navigation and UI resources for Thinkful.

144 lines (118 loc) 3.62 kB
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;