Skip to content

Instantly share code, notes, and snippets.

@rafaell-lycan
Last active July 10, 2020 01:52
Show Gist options
  • Save rafaell-lycan/fa4afbe3e4a1d94c4a5b5609afd017ed to your computer and use it in GitHub Desktop.
Save rafaell-lycan/fa4afbe3e4a1d94c4a5b5609afd017ed to your computer and use it in GitHub Desktop.
Source code for implementing a React <Tabs/> component.
import React from 'react';
import { render } from 'react-dom';
import Tabs from './Tabs';
const App = () => {
return (
<Tabs>
<Tabs.Tab label={'Tab 1'}>
<p>Content 1</p>
</Tabs.Tab>
<Tabs.Tab label={'Tab 2'}>
<ul>
<li>List</li>
<li>Content</li>
<li>2</li>
</ul>
</Tabs.Tab>
<Tabs.Tab label={'Tab 3'}>
<figure>
<img src={'http://placehold.it/200'} alt={'Image Content 3'} />
<figcaption>Image Content 3</figcaption>
</figure>
</Tabs.Tab>
</Tabs>
);
};
render(<App/>, document.body);
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
const Tab = (props) => {
const { className, label, isActive, onClick } = props;
const cssTabClass = cx('tabs__tab', className);
const activeLinkClass = cx('tabs__tab-link', {
['tabs__tab-link--active']: isActive
});
return (
<li className={cssTabClass}>
<a className={activeLinkClass} onClick={onClick}>{label}</a>
</li>
);
};
Tab.propTypes = {
className: PropTypes.string,
label: PropTypes.string.isRequired,
isActive: PropTypes.bool,
onClick: PropTypes.func
};
export default Tab;
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
import Tab from './Tab';
class Tabs extends Component {
constructor(props) {
super(props);
this.state = { activeIndex: props.defaultActiveIndex || 0 };
}
handleTabClick = (tabIndex) => {
if (tabIndex !== this.state.activeIndex) {
this.setState({ activeIndex: tabIndex });
}
}
cloneTabElement = (tab, index = 0) => {
const { activeIndex } = this.state;
return (
cloneElement(tab, {
onClick: () => this.handleTabClick(index),
tabIndex: index,
isActive: index === activeIndex,
})
);
}
renderChildrenTabs = () => {
const { children } = this.props;
if (!Array.isArray(children)) {
return this.cloneTabElement(children);
}
return children.map(this.cloneTabElement);
}
renderActiveTabContent(): any {
const { children } = this.props;
const { activeIndex } = this.state;
if (children[activeIndex]) {
return children[activeIndex].props.children;
}
return children.props.children;
}
render() {
const { className } = this.props;
const cssClass = cx('tabs', className);
return (
<section className={cssClass}>
<ul className={'tabs__list'}>
{this.renderChildrenTabs()}
</ul>
<div className={'tabs__content'}>
{this.renderActiveTabContent()}
</div>
</section>
);
}
};
Tabs.propTypes = {
className: PropTypes.string,
defaultActiveIndex: PropTypes.number
};
Tabs.Tab = Tab;
export default Tabs;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment