Skip to content

Commit

Permalink
feat(react-tabs): Refactor Tabs component
Browse files Browse the repository at this point in the history
* Replace SimpleAltTabs with Tabs

[#85440704]

Signed-off-by: Nicole Sullivan <nsullivan@pivotal.io>
  • Loading branch information
Paul Meskers authored and stubbornella committed Jan 7, 2015
1 parent 611a501 commit 9ad17e6
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 71 deletions.
8 changes: 4 additions & 4 deletions src/pivotal-ui/components/tabs.scss
Original file line number Diff line number Diff line change
Expand Up @@ -501,8 +501,8 @@ You can use any 130px by 130px svg for the icon in the center of the tab. Please

/*doc
---
title: React Simple Alt Tabs
name: react_simple_alt_tabs
title: React Simple Tabs
name: react_simple_tabs
categories:
- Beta
---
Expand All @@ -513,13 +513,13 @@ categories:
```jsx_example
React.render(
<UI.SimpleAltTabs>
<UI.Tabs>
<UI.Tab heading="Tab 1">Wow!</UI.Tab>
<UI.Tab heading="Tab 2" active="true">
<h2>Neat!</h2>
<span>So much content.</span>
</UI.Tab>
</UI.SimpleAltTabs>,
</UI.Tabs>,
document.getElementById('simple-alt-tabs-example')
);
```
Expand Down
28 changes: 14 additions & 14 deletions src/pivotal-ui/javascripts/components.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,33 @@ var React = require('react');

module.exports = {
TableSortable: React.createFactory(require('./table-sortable.jsx')),

DefaultH1: require('./typography.jsx').DefaultH1,
DefaultH2: require('./typography.jsx').DefaultH2,
DefaultH3: require('./typography.jsx').DefaultH3,
DefaultH4: require('./typography.jsx').DefaultH4,
DefaultH5: require('./typography.jsx').DefaultH5,
DefaultH6: require('./typography.jsx').DefaultH6,

AlternateH1: require('./typography.jsx').AlternateH1,
AlternateH2: require('./typography.jsx').AlternateH2,
AlternateH3: require('./typography.jsx').AlternateH3,
AlternateH4: require('./typography.jsx').AlternateH4,
AlternateH5: require('./typography.jsx').AlternateH5,
AlternateH6: require('./typography.jsx').AlternateH6,

MarketingH1: require('./typography.jsx').MarketingH1,
MarketingH2: require('./typography.jsx').MarketingH2,
MarketingH3: require('./typography.jsx').MarketingH3,
MarketingH4: require('./typography.jsx').MarketingH4,
MarketingH5: require('./typography.jsx').MarketingH5,
MarketingH6: require('./typography.jsx').MarketingH6,

Heading: require('./typography.jsx').Heading,

BasePane: require('./panes.jsx').BasePane,
Pane: require('./panes.jsx').Pane,

UIButton: require('./buttons.jsx').UIButton,
DefaultButton: require('./buttons.jsx').DefaultButton,
DefaultAltButton: require('./buttons.jsx').DefaultAltButton,
Expand All @@ -37,20 +37,20 @@ module.exports = {
DangerButton: require('./buttons.jsx').DangerButton,
HighlightButton: require('./buttons.jsx').HighlightButton,
HighlightAltButton: require('./buttons.jsx').HighlightAltButton,

SearchInput: require('./inputs.jsx').SearchInput,

Divider: require('./dividers.jsx').Divider,
DividerInverse: require('./dividers.jsx').DividerInverse,

Row: require('./grids.jsx').Row,
Col: require('./grids.jsx').Col,

Media: require('./media.jsx').Media,
Flag: require('./media.jsx').Flag,

PivnetHomepage: require('./pivnet_homepage.jsx').PivnetHomepage,

Panel: require('./panels.jsx').Panel,
SimplePanel: require('./panels.jsx').SimplePanel,
ClickablePanel: require('./panels.jsx').ClickablePanel,
Expand All @@ -61,6 +61,6 @@ module.exports = {

Image: require('./images.jsx').Image,

SimpleAltTabs: require('./tabs.jsx').SimpleAltTabs,
Tab: require('./tabs.jsx').Tab
Tab: require('./tabs.jsx').Tab,
Tabs: require('./tabs.jsx').Tabs
};
95 changes: 60 additions & 35 deletions src/pivotal-ui/javascripts/tabs.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,62 +3,87 @@
var React = require('react');
var _ = require('lodash');

var SimpleAltTabs = React.createClass({
var TabMenu = React.createClass({
render: function () {
var activeIndex = _.findIndex(this.props.children, function (tab) {
return tab.props.active;
});

if (activeIndex === -1) {
activeIndex = 0;
}

var tabs = _.map(this.props.children, function(tab, index) {
var className = index === activeIndex ? 'active' : '';
var tabId = '#tab-' + index;

var tabs = _.map(this.props.tabs, function(tab, index) {
var className = (index === this.props.activeIndex) ? "active" : "";
var key = "tab-control-" + index;
return (
<li key={'tab-head-' + index} className={className}>
<a data-toggle="tab" href={tabId}>{tab.props.heading}</a>
<li key={key} className={className}>
<a onClick={this.props.onTabClick} data-index={index}>{tab}</a>
</li>
);
});
}, this);

return (
<ul className="nav nav-tabs">
{tabs}
</ul>
);
}
});

var contents = _.map(this.props.children, function(tab, index) {
var classes = ['tab-pane', 'fade'];
var tabId = 'tab-' + index;
if (index === activeIndex) {
classes.push('in');
classes.push('active');
}
var TabContents = React.createClass({
render: function () {
var panels = _.map(this.props.children, function(panel, index) {
var className = "tab-pane fade";
className += (this.props.activeIndex === index) ? " active in" : "";

var key = "tab-panel-" + index;

return (
<div key={'tab-content-' + index} id={tabId} className={classes.join(" ")}>
{tab.props.children}
<div key={key} className={className}>
{panel.props.children}
</div>
);
});
}, this);

return (
<div className="tab-simple-alt">
<ul className="nav nav-tabs">
{tabs}
</ul>
<div className="tab-content">
{contents}
</div>
<div className="tab-content">
{panels}
</div>
);
}
});

var Tabs = React.createClass({
getInitialState: function () {
return {
activeIndex: 0
};
},
componentWillMount: function () {
if (this.props.activeTab) {
this.setState({ activeIndex: this.props.activeTab });
}
},
updateTabs: function (event) {
var tabIndex = parseInt(event.target.getAttribute('data-index'));
this.setState({ activeIndex: tabIndex });
},
render: function () {
var tabLinks = _.pluck(_.pluck(this.props.children, 'props'), 'heading');

return (
<div className="tab-simple">
<TabMenu tabs={tabLinks} activeIndex={this.state.activeIndex} onTabClick={this.updateTabs}/>
<TabContents activeIndex={this.state.activeIndex}>{this.props.children}</TabContents>
</div>
);
}
});

var Tab = React.createClass({
render: function () {
return "";
return (
<div>
{this.props.children}
</div>
);
}
});

module.exports = {
SimpleAltTabs: SimpleAltTabs,
Tabs: Tabs,
Tab: Tab
};
59 changes: 41 additions & 18 deletions test/spec/javascripts/tabs_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ var $ = require('jquery');
var React = require('react/addons');
var TestUtils = React.addons.TestUtils;

var SimpleAltTabs = require('../../../src/pivotal-ui/javascripts/tabs.jsx').SimpleAltTabs;
var Tabs = require('../../../src/pivotal-ui/javascripts/tabs.jsx').Tabs;
var Tab = require('../../../src/pivotal-ui/javascripts/tabs.jsx').Tab;

describe('SimpleAltTabs', function() {
describe("Tabs", function() {
beforeEach(function() {
this.node = $('<div id="container"></div>').appendTo('body').get(0);
});
Expand All @@ -17,42 +17,65 @@ describe('SimpleAltTabs', function() {
document.body.removeChild(this.node);
});

describe("when the active flag is not set", function() {
describe("when the active tab is not set", function() {
beforeEach(function() {
React.render(
<SimpleAltTabs>
<Tabs>
<Tab heading="My first tab" >Content for first tab</Tab>
<Tab heading="My second tab" >Content for second tab</Tab>
</SimpleAltTabs>,
</Tabs>,
this.node
);
});

it("renders the correct tabs and content", function() {
expect($('#container .tab-simple-alt ul.nav.nav-tabs li.active a')).toHaveText("My first tab");
expect($('#container .tab-simple-alt ul.nav.nav-tabs li:not(.active) a')).toHaveText("My second tab");
it("renders the tabs and content with first tab active", function() {
expect($('#container .tab-simple ul.nav.nav-tabs li.active a')).toHaveText("My first tab");
expect($('#container .tab-simple ul.nav.nav-tabs li:not(.active) a')).toHaveText("My second tab");

expect($('#container li.active a').attr('href')).toEqual('#' + $('#container .tab-pane.active').attr("id"));
expect($('#container .tab-simple .tab-content .tab-pane.in.active')).toHaveText("Content for first tab");
expect($('#container .tab-simple .tab-content .tab-pane:not(.in.active)')).toHaveText("Content for second tab");
});
});

describe("when the active tab is set", function() {
beforeEach(function() {
React.render(
<Tabs activeTab={1}>
<Tab heading="My first tab" >Content for first tab</Tab>
<Tab heading="My second tab" >Content for second tab</Tab>
</Tabs>,
this.node
);
});

it("renders the tabs and content with specified tab active", function() {
expect($('#container .tab-simple ul.nav.nav-tabs li.active a')).toHaveText("My second tab");
expect($('#container .tab-simple ul.nav.nav-tabs li:not(.active) a')).toHaveText("My first tab");

expect($('#container .tab-simple-alt .tab-content .tab-pane.in.active')).toHaveText("Content for first tab");
expect($('#container .tab-simple-alt .tab-content .tab-pane:not(.in.active)')).toHaveText("Content for second tab");
expect($('#container .tab-simple .tab-content .tab-pane.in.active')).toHaveText("Content for second tab");
expect($('#container .tab-simple .tab-content .tab-pane:not(.in.active)')).toHaveText("Content for first tab");
});
});

describe("when the active flag is set on a tab", function() {
describe("clicking a non-active tab", function() {
beforeEach(function() {
React.render(
<SimpleAltTabs>
<Tabs>
<Tab heading="My first tab" >Content for first tab</Tab>
<Tab heading="My second tab" active="true">Content for second tab</Tab>
</SimpleAltTabs>,
<Tab heading="My second tab" >Content for second tab</Tab>
</Tabs>,
this.node
);

TestUtils.Simulate.click($('li:not(.active) a').get(0));
});

it("sets the specified tab as active", function() {
expect($('#container .tab-simple-alt ul.nav.nav-tabs li.active a')).toHaveText("My second tab");
expect($('#container .tab-simple-alt .tab-content .tab-pane.in.active')).toHaveText("Content for second tab");
it("updates the active tab and content", function() {
expect($('#container .tab-simple ul.nav.nav-tabs li.active a')).toHaveText("My second tab");
expect($('#container .tab-simple ul.nav.nav-tabs li:not(.active) a')).toHaveText("My first tab");

expect($('#container .tab-simple .tab-content .tab-pane.in.active')).toHaveText("Content for second tab");
expect($('#container .tab-simple .tab-content .tab-pane:not(.in.active)')).toHaveText("Content for first tab");
});
});
});

0 comments on commit 9ad17e6

Please sign in to comment.