All files / components/SearchField SearchField.jsx

100% Statements 14/14
100% Branches 4/4
100% Functions 0/0
100% Lines 14/14
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157                    120x                 120x               120x                                                                                                                                 120x                                           21x         21x   21x                 21x 21x 21x 21x 21x 21x         21x                                
import _ from 'lodash';
import React, { createElement } from 'react';
import { lucidClassNames } from '../../util/style-helpers';
import { createClass, getFirst, omitProps } from '../../util/component-types';
 
import TextField from '../TextField/TextField';
import SearchIcon from '../Icon/SearchIcon/SearchIcon';
 
import reducers from './SearchField.reducers';
 
const cx = lucidClassNames.bind('&-SearchField');
 
const {
	bool,
	func,
	node,
	number,
	oneOfType,
	string,
} = React.PropTypes;
 
/**
* {"categories": ["controls", "text"], "madeFrom": ["TextField", "SearchIcon"]}
*
* This is a wrapper around TextField that styles it for a search use-case. The
* icon and TextField are customizable through child components.
*/
const SearchField = createClass({
	displayName: 'SearchField',
 
	components: {
		/**
		 * Allows for full control over the TextField that's used under the hood.
		 */
		TextField,
		/**
		 * Icon this is displayed on the right side of the SearchField. Any of the
		 * lucid `*Icon` components should work.
		 */
		Icon: createClass({
			displayName: 'SearchField.Icon',
			propName: 'Icon',
		}),
	},
 
	reducers,
 
	propTypes: {
		/**
		 * Fires an event every time the user types text into the TextField.
		 *
		 * Signature: `(value, { event, props }) => {}`
		 */
		onChange: func,
		/**
		 * Fires an event when the user hits "enter" from the SearchField.
		 *
		 * Signature: `(value, { event, props }) => {}`
		 */
		onSubmit: func,
		/**
		 * Set the value of the input.
		 */
		value: oneOfType([
			number,
			string,
		]),
		/**
		 * Controls the highlighting of the search icon. Should be passed `true` when
		 * the search text is valid, e.g. contains enough characters to perform a search.
		 */
		isValid: bool,
		/**
		 * Disables the SearchField by greying it out.
		 */
		isDisabled: bool,
		/**
		 * placeholder value
		 */
		placeholder: string,
		/**
		* Appended to the component-specific class names set on the root
		* element.
		*/
		className: string,
		/**
		* Passed through to the root element.
		*/
		Icon: node,
	},
 
	getDefaultProps() {
		return {
			isDisabled: false,
			onChange: _.noop,
			onSubmit: _.noop,
			value: '',
		};
	},
 
	render() {
 
		const {
			props,
			props: {
				className,
				isDisabled,
				isValid,
				onChange,
				onSubmit,
				placeholder,
				value,
				...passThroughs,
			},
		} = this;
 
		const {
			Icon,
			TextField,
		} = SearchField;
 
		const textFieldProps = {
			isDisabled,
			onChange,
			onSubmit,
			placeholder,
			isMultiLine: false,
			value,
		};
 
		const textFieldElement = getFirst(props, TextField, <TextField {...textFieldProps} />);
		const isIconActive = _.isUndefined(isValid) ? !_.isEmpty(_.get(textFieldElement, 'props.value')) : isValid;
		const defaultIcon = <SearchIcon className={cx('&-Icon', { '&-Icon-active': isIconActive })} />;
		const iconElement = getFirst(props, Icon);
		const iconChildren = _.get(iconElement, 'props.children');
		const icon = iconChildren ? createElement(iconChildren.type, {
			...iconChildren.props,
			className: cx('&-Icon', { '&-Icon-active': isIconActive }, iconChildren.props.className),
		}) : defaultIcon;
 
		return (
			<div
				{...omitProps(passThroughs, SearchField)}
				className={cx('&', className)}
			>
				{textFieldElement}
				<div className={cx('&-Icon-container')}>
					{icon}
				</div>
			</div>
		);
 
	},
});
 
export default SearchField;