Skip to content

Commit 9b5eeae

Browse files
authored
Fix nesting of multiple instances of react-tabs (#103)
Add a check that checks if the tab belongs to the container. Fixes #54 Fixes #91
1 parent 40f7f68 commit 9b5eeae

File tree

2 files changed

+56
-3
lines changed

2 files changed

+56
-3
lines changed

src/components/Tabs.js

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ module.exports = React.createClass({
236236
},
237237

238238
handleKeyDown(e) {
239-
if (isTabNode(e.target)) {
239+
if (this.isTabFromContainer(e.target)) {
240240
let index = this.state.selectedIndex;
241241
let preventDefault = false;
242242

@@ -264,7 +264,7 @@ module.exports = React.createClass({
264264
handleClick(e) {
265265
let node = e.target;
266266
do { // eslint-disable-line no-cond-assign
267-
if (isTabNode(node)) {
267+
if (this.isTabFromContainer(node)) {
268268
if (isTabDisabled(node)) {
269269
return;
270270
}
@@ -302,6 +302,30 @@ module.exports = React.createClass({
302302
};
303303
},
304304

305+
/**
306+
* Determine if a node from event.target is a Tab element for the current Tabs container.
307+
* If the clicked element is not a Tab, it returns false.
308+
* If it finds another Tabs container between the Tab and `this`, it returns false.
309+
*/
310+
isTabFromContainer(node) {
311+
// return immediately if the clicked element is not a Tab.
312+
if (!isTabNode(node)) {
313+
return false;
314+
}
315+
316+
// Check if the first occurrence of a Tabs container is `this` one.
317+
let nodeAncestor = node.parentElement;
318+
const tabsNode = findDOMNode(this);
319+
do {
320+
if (nodeAncestor === tabsNode) return true;
321+
else if (nodeAncestor.getAttribute('data-tabs')) break;
322+
323+
nodeAncestor = nodeAncestor.parentElement;
324+
} while (nodeAncestor);
325+
326+
return false;
327+
},
328+
305329
render() {
306330
// This fixes an issue with focus management.
307331
//
@@ -331,6 +355,7 @@ module.exports = React.createClass({
331355
)}
332356
onClick={this.handleClick}
333357
onKeyDown={this.handleKeyDown}
358+
data-tabs
334359
>
335360
{this.getChildren()}
336361
</div>

src/components/__tests__/Tabs-test.js

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,6 @@ describe('react-tabs', () => {
171171
expect(wrapper.childAt(2).text()).toBe('Hello Bar');
172172
expect(wrapper.childAt(3).text()).toBe('');
173173

174-
175174
wrapper.childAt(0).childAt(2).simulate('click');
176175

177176
expect(wrapper.childAt(1).text()).toBe('');
@@ -241,5 +240,34 @@ describe('react-tabs', () => {
241240
</Tabs>
242241
)).not.toThrow();
243242
});
243+
244+
it('should support nested tabs', () => {
245+
const wrapper = mount(
246+
<Tabs className="first">
247+
<TabList>
248+
<Tab />
249+
<Tab />
250+
</TabList>
251+
<TabPanel>
252+
<Tabs className="second">
253+
<TabList>
254+
<Tab />
255+
<Tab />
256+
</TabList>
257+
<TabPanel />
258+
<TabPanel />
259+
</Tabs>
260+
</TabPanel>
261+
<TabPanel />
262+
</Tabs>
263+
);
264+
265+
const innerTabs = wrapper.childAt(1).childAt(0);
266+
267+
innerTabs.childAt(0).childAt(1).simulate('click');
268+
269+
assertTabSelected(wrapper, 0);
270+
assertTabSelected(innerTabs, 1);
271+
});
244272
});
245273
});

0 commit comments

Comments
 (0)