Skip to content

Commit

Permalink
Merge pull request react-bootstrap#1017 from AlexKVal/tabbedArea
Browse files Browse the repository at this point in the history
Tabbed area. Fix for animated panes rendering.
  • Loading branch information
taion committed Jul 29, 2015
2 parents 952798b + 2558f32 commit 191f2ee
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 16 deletions.
49 changes: 33 additions & 16 deletions src/TabbedArea.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,22 @@ const TabbedArea = React.createClass({

componentWillReceiveProps(nextProps) {
if (nextProps.activeKey != null && nextProps.activeKey !== this.props.activeKey) {
// check if the 'previousActiveKey' child still exists
let previousActiveKey = this.props.activeKey;
React.Children.forEach(nextProps.children, (child) => {
if (React.isValidElement(child)) {
if (child.props.eventKey === previousActiveKey) {
this.setState({
previousActiveKey
});
return;
}
}
});

// if the 'previousActiveKey' child does not exist anymore
this.setState({
previousActiveKey: this.props.activeKey
previousActiveKey: null
});
}
},
Expand All @@ -66,15 +80,12 @@ const TabbedArea = React.createClass({
render() {
let { id, ...props } = this.props;

let activeKey =
this.props.activeKey != null ? this.props.activeKey : this.state.activeKey;

function renderTabIfSet(child) {
return child.props.tab != null ? this.renderTab(child) : null;
}

let nav = (
<Nav {...props} activeKey={activeKey} onSelect={this.handleSelect} ref="tabs">
<Nav {...props} activeKey={this.getActiveKey()} onSelect={this.handleSelect} ref="tabs">
{ValidComponentChildren.map(this.props.children, renderTabIfSet, this)}
</Nav>
);
Expand All @@ -94,21 +105,22 @@ const TabbedArea = React.createClass({
},

renderPane(child, index) {
let activeKey = this.getActiveKey();
let previousActiveKey = this.state.previousActiveKey;

let active = (child.props.eventKey === activeKey &&
(this.state.previousActiveKey == null || !this.props.animation));
let shouldPaneBeSetActive = child.props.eventKey === this.getActiveKey();
let thereIsNoActivePane = previousActiveKey == null;

let paneIsAlreadyActive = previousActiveKey != null && child.props.eventKey === previousActiveKey;

return cloneElement(
child,
{
active,
active: shouldPaneBeSetActive && (thereIsNoActivePane || !this.props.animation),
id: panelId(this.props, child),
'aria-labelledby': tabId(this.props, child),
key: child.key ? child.key : index,
animation: this.props.animation,
onAnimateOutEnd: (this.state.previousActiveKey != null &&
child.props.eventKey === this.state.previousActiveKey) ? this.handlePaneAnimateOutEnd : null
onAnimateOutEnd: paneIsAlreadyActive ? this.handlePaneAnimateOutEnd : null
}
);
},
Expand All @@ -134,15 +146,20 @@ const TabbedArea = React.createClass({
return !this._isChanging;
},

handleSelect(key) {
handleSelect(selectedKey) {
if (this.props.onSelect) {
this._isChanging = true;
this.props.onSelect(key);
this.props.onSelect(selectedKey);
this._isChanging = false;
} else if (key !== this.getActiveKey()) {
return;
}

// if there is no external handler, then use embedded one
let previousActiveKey = this.getActiveKey();
if (selectedKey !== previousActiveKey) {
this.setState({
activeKey: key,
previousActiveKey: this.getActiveKey()
activeKey: selectedKey,
previousActiveKey
});
}
}
Expand Down
43 changes: 43 additions & 0 deletions test/TabbedAreaSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import TabbedArea from '../src/TabbedArea';
import NavItem from '../src/NavItem';
import TabPane from '../src/TabPane';
import ValidComponentChildren from '../src/utils/ValidComponentChildren';
import { render } from './helpers';

describe('TabbedArea', function () {
it('Should show the correct tab', function () {
Expand Down Expand Up @@ -230,6 +231,48 @@ describe('TabbedArea', function () {
assert.equal(tabbedArea.refs.tabs.props.activeKey, 2);
});

describe('animation', function () {
let mountPoint;

beforeEach(()=>{
mountPoint = document.createElement('div');
document.body.appendChild(mountPoint);
});

afterEach(function () {
React.unmountComponentAtNode(mountPoint);
document.body.removeChild(mountPoint);
});

function checkTabRemovingWithAnimation(animation) {
it(`should correctly set "active" after tabPane is removed with "animation=${animation}"`, function() {
let instance = render(
<TabbedArea activeKey={2} animation={animation}>
<TabPane tab="Tab 1" eventKey={1}>Tab 1 content</TabPane>
<TabPane tab="Tab 2" eventKey={2}>Tab 2 content</TabPane>
</TabbedArea>
, mountPoint);

let panes = ReactTestUtils.scryRenderedComponentsWithType(instance, TabPane);

assert.equal(panes[0].props.active, false);
assert.equal(panes[1].props.active, true);

// second tab has been removed
render(
<TabbedArea activeKey={1} animation={animation}>
<TabPane tab="Tab 1" eventKey={1}>Tab 1 content</TabPane>
</TabbedArea>
, mountPoint);

assert.equal(panes[0].props.active, true);
});
}

checkTabRemovingWithAnimation(true);
checkTabRemovingWithAnimation(false);
});

describe('Web Accessibility', function(){

it('Should generate ids from parent id', function () {
Expand Down

0 comments on commit 191f2ee

Please sign in to comment.