All files / components/VerticalTabs VerticalTabs.jsx

100% Statements 10/10
100% Branches 2/2
100% Functions 1/1
100% Lines 10/10
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              120x             120x                   120x                                                                                                       120x                       13x     13x   13x       13x       13x                   19x                                  
import _ from 'lodash';
import React from 'react';
import { lucidClassNames } from '../../util/style-helpers';
import { createClass, getFirst, findTypes, omitProps } from '../../util/component-types';
import * as reducers from './VerticalTabs.reducers';
import VerticalListMenu from '../VerticalListMenu/VerticalListMenu';
 
const cx = lucidClassNames.bind('&-VerticalTabs');
 
const {
	string,
	number,
	bool,
	func,
} = React.PropTypes;
 
/**
 *
 * {"categories": ["navigation"], "madeFrom": ["VerticalListMenu"]}
 *
 * `VerticalTabs` provides vertically tabbed navigation. It has a flexible
 * interface that allows tab content to be passed as regular React children or
 * through props.
 */
const VerticalTabs = createClass({
	displayName: 'VerticalTabs',
 
	components: {
		/**
		 * Content that will be rendered in a tab. Be sure to nest a Title inside
		 * each Tab or provide it as a prop.
		 */
		Tab: createClass({
			displayName: 'VerticalTabs.Tab',
			propName: 'Tab',
			propTypes: {
				/**
				 * Determines if the Tab is selected.
				 */
				isSelected: bool,
			},
		}),
		/**
		 * Titles can be provided as a child or prop to a Tab.
		 */
		Title: createClass({
			displayName: 'VerticalTabs.Title',
			propName: 'Title',
		}),
	},
 
	reducers,
 
	propTypes: {
		/**
		 * Class names that are appended to the defaults.
		 */
		className: string,
 
		/**
		 * Indicates which of the `VerticalTabs.Tab` children is currently
		 * selected. The index of the last `VerticalTabs.Tab` child with
		 * `isSelected` equal to `true` takes precedence over this prop.
		 */
		selectedIndex: number,
 
		/**
		 * Callback for when the user clicks a tab. Called with the index of the
		 * tab that was clicked.
		 *
		 * Signature: `(index, { event, props}) => {}`
		 */
		onSelect: func,
	},
 
	getDefaultProps() {
		return {
			selectedIndex: 0,
			onSelect: _.noop,
		};
	},
 
	render() {
		const {
			className,
			onSelect,
			selectedIndex,
			...passThroughs,
		} = this.props;
 
		// Grab props array from each Tab
		const tabChildProps = _.map(findTypes(this.props, VerticalTabs.Tab), 'props');
 
		const selectedIndexFromChildren = _.findLastIndex(tabChildProps, {
			isSelected: true,
		});
 
		const actualSelectedIndex = selectedIndexFromChildren !== -1
			? selectedIndexFromChildren
			: selectedIndex;
 
		return (
			<div
				{...omitProps(passThroughs, VerticalTabs)}
				className={cx('&', className)}
			>
				<VerticalListMenu
					selectedIndices={[actualSelectedIndex]}
					onSelect={onSelect}
				>
					{_.map(tabChildProps, (tabChildProp, index) => (
						<VerticalListMenu.Item
							className={cx('&-Tab', {'&-Tab-is-active': actualSelectedIndex === index})}
							key={index}
						>
							<span className={cx('&-Tab-content')}>{_.get(getFirst(tabChildProp, VerticalTabs.Title), 'props.children', '')}</span>
						</VerticalListMenu.Item>
					))}
				</VerticalListMenu>
				<div className={cx('&-content')}>
					{_.get(tabChildProps, [actualSelectedIndex, 'children'])}
				</div>
			</div>
		);
	},
});
 
export default VerticalTabs;