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 130 131 132 133 134 135 136 137 138 139 140                                134x   134x                   134x                                                                                                       268x             16x     16x         16x       16x       16x                   31x                                                    
import _ from 'lodash';
import React from 'react';
import PropTypes from 'prop-types';
import { lucidClassNames } from '../../util/style-helpers';
import {
	createClass,
	getFirst,
	findTypes,
	omitProps,
} from '../../util/component-types';
import { buildHybridComponent } from '../../util/state-management';
import * as reducers from './VerticalTabs.reducers';
import {
	VerticalListMenuDumb as VerticalListMenu,
} from '../VerticalListMenu/VerticalListMenu';
 
const cx = lucidClassNames.bind('&-VerticalTabs');
 
const { string, number, bool, func } = 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 buildHybridComponent(VerticalTabs);
export { VerticalTabs as VerticalTabsDumb };