Skip to content

Commit 2742248

Browse files
committed
Adding BEM style class names, and supporting custom className
closes #42
1 parent 6fab2a0 commit 2742248

File tree

9 files changed

+174
-10
lines changed

9 files changed

+174
-10
lines changed

lib/components/Tab.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, {PropTypes} from 'react';
22
import { findDOMNode } from 'react-dom';
3+
import cx from 'classnames';
34

45
function syncNodeAttributes(node, props) {
56
if (props.selected) {
@@ -18,6 +19,7 @@ module.exports = React.createClass({
1819
displayName: 'Tab',
1920

2021
propTypes: {
22+
className: PropTypes.string,
2123
id: PropTypes.string,
2224
selected: PropTypes.bool,
2325
disabled: PropTypes.bool,
@@ -49,6 +51,14 @@ module.exports = React.createClass({
4951
render() {
5052
return (
5153
<li
54+
className={cx(
55+
'ReactTabs__Tab',
56+
this.props.className,
57+
{
58+
'ReactTabs__Tab--selected': this.props.selected,
59+
'ReactTabs__Tab--disabled': this.props.disabled
60+
}
61+
)}
5262
role="tab"
5363
id={this.props.id}
5464
aria-selected={this.props.selected ? 'true' : 'false'}

lib/components/TabList.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import React, {PropTypes} from 'react';
2+
import cx from 'classnames';
23

34
module.exports = React.createClass({
45
displayName: 'TabList',
56

67
propTypes: {
8+
className: PropTypes.string,
79
children: PropTypes.oneOfType([
810
PropTypes.object,
911
PropTypes.array
@@ -12,7 +14,13 @@ module.exports = React.createClass({
1214

1315
render() {
1416
return (
15-
<ul role="tablist">
17+
<ul
18+
className={cx(
19+
'ReactTabs__TabList',
20+
this.props.className
21+
)}
22+
role="tablist"
23+
>
1624
{this.props.children}
1725
</ul>
1826
);

lib/components/TabPanel.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import React, {PropTypes} from 'react';
2+
import cx from 'classnames';
23

34
module.exports = React.createClass({
45
displayName: 'TabPanel',
56

67
propTypes: {
8+
className: PropTypes.string,
79
selected: PropTypes.bool,
810
id: PropTypes.string,
911
tabId: PropTypes.string,
@@ -33,6 +35,13 @@ module.exports = React.createClass({
3335

3436
return (
3537
<div
38+
className={cx(
39+
'ReactTabs__TabPanel',
40+
this.props.className,
41+
{
42+
'ReactTabs__TabPanel--selected': this.props.selected
43+
}
44+
)}
3645
role="tabpanel"
3746
id={this.props.id}
3847
aria-labeledby={this.props.tabId}

lib/components/Tabs.js

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import React, {PropTypes, cloneElement} from 'react';
22
import { findDOMNode } from 'react-dom';
3+
import cx from 'classnames';
34
import jss from 'js-stylesheet';
45
import uuid from '../helpers/uuid';
56
import childrenPropType from '../helpers/childrenPropType';
@@ -20,6 +21,7 @@ module.exports = React.createClass({
2021
displayName: 'Tabs',
2122

2223
propTypes: {
24+
className: PropTypes.string,
2325
selectedIndex: PropTypes.number,
2426
onSelect: PropTypes.func,
2527
focus: PropTypes.bool,
@@ -284,7 +286,11 @@ module.exports = React.createClass({
284286

285287
return (
286288
<div
287-
className="react-tabs"
289+
className={cx(
290+
'ReactTabs',
291+
'react-tabs',
292+
this.props.className
293+
)}
288294
onClick={this.handleClick}
289295
onKeyDown={this.handleKeyDown}
290296
>

lib/components/__tests__/Tab-test.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import React from 'react';
2+
import { findDOMNode } from 'react-dom';
3+
import TestUtils from 'react-addons-test-utils';
4+
import { Tab } from '../../main';
5+
import { equal } from 'assert';
6+
7+
/* eslint func-names:0 */
8+
describe('Tab', function() {
9+
it('should have sane defaults', function() {
10+
const tab = TestUtils.renderIntoDocument(<Tab/>);
11+
const node = findDOMNode(tab);
12+
13+
equal(node.className, 'ReactTabs__Tab');
14+
equal(node.getAttribute('role'), 'tab');
15+
equal(node.getAttribute('aria-selected'), 'false');
16+
equal(node.getAttribute('aria-expanded'), 'false');
17+
equal(node.getAttribute('aria-disabled'), 'false');
18+
equal(node.getAttribute('aria-controls'), null);
19+
equal(node.getAttribute('id'), null);
20+
equal(node.innerHTML, '');
21+
});
22+
23+
it('should accept className', function() {
24+
const tab = TestUtils.renderIntoDocument(<Tab className="foobar"/>);
25+
const node = findDOMNode(tab);
26+
27+
equal(node.className, 'ReactTabs__Tab foobar');
28+
});
29+
30+
it('should support being selected', function() {
31+
const tab = TestUtils.renderIntoDocument(<Tab selected={true} id="abcd" panelId="1234">Hello</Tab>);
32+
const node = findDOMNode(tab);
33+
34+
equal(node.className, 'ReactTabs__Tab ReactTabs__Tab--selected');
35+
equal(node.getAttribute('aria-selected'), 'true');
36+
equal(node.getAttribute('aria-expanded'), 'true');
37+
equal(node.getAttribute('aria-controls'), '1234');
38+
equal(node.getAttribute('id'), 'abcd');
39+
equal(node.innerHTML, 'Hello');
40+
});
41+
42+
it('should support being disabled', function() {
43+
const tab = TestUtils.renderIntoDocument(<Tab disabled={true}/>);
44+
const node = findDOMNode(tab);
45+
46+
equal(node.className, 'ReactTabs__Tab ReactTabs__Tab--disabled');
47+
});
48+
});
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import React from 'react';
2+
import { findDOMNode } from 'react-dom';
3+
import TestUtils from 'react-addons-test-utils';
4+
import { TabList } from '../../main';
5+
import { equal } from 'assert';
6+
7+
/* eslint func-names:0 */
8+
describe('Tab', function() {
9+
it('should have sane defaults', function() {
10+
const tabList = TestUtils.renderIntoDocument(<TabList/>);
11+
const node = findDOMNode(tabList);
12+
13+
equal(node.className, 'ReactTabs__TabList');
14+
equal(node.getAttribute('role'), 'tablist');
15+
});
16+
17+
it('should accept className', function() {
18+
const tabList = TestUtils.renderIntoDocument(<TabList className="foobar"/>);
19+
const node = findDOMNode(tabList);
20+
21+
equal(node.className, 'ReactTabs__TabList foobar');
22+
});
23+
});
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import React from 'react';
2+
import { findDOMNode } from 'react-dom';
3+
import TestUtils from 'react-addons-test-utils';
4+
import { TabPanel } from '../../main';
5+
import { equal } from 'assert';
6+
7+
/* eslint func-names:0 */
8+
describe('Tab', function() {
9+
it('should have sane defaults', function() {
10+
const tabPanel = TestUtils.renderIntoDocument(<TabPanel>Hola</TabPanel>);
11+
const node = findDOMNode(tabPanel);
12+
13+
equal(node.className, 'ReactTabs__TabPanel');
14+
equal(node.getAttribute('role'), 'tabpanel');
15+
equal(node.getAttribute('aria-labeledby'), null);
16+
equal(node.getAttribute('id'), null);
17+
equal(node.innerHTML, '');
18+
equal(node.style.display, 'none');
19+
});
20+
21+
it('should accept className', function() {
22+
const tabPanel = TestUtils.renderIntoDocument(<TabPanel className="foobar"/>);
23+
const node = findDOMNode(tabPanel);
24+
25+
equal(node.className, 'ReactTabs__TabPanel foobar');
26+
});
27+
28+
it('should support being selected', function() {
29+
const tabPanel = TestUtils.renderIntoDocument(<TabPanel selected={true} id="abcd" tabId="1234">Hola</TabPanel>);
30+
const node = findDOMNode(tabPanel);
31+
32+
equal(node.className, 'ReactTabs__TabPanel ReactTabs__TabPanel--selected');
33+
equal(node.getAttribute('aria-labeledby'), '1234');
34+
equal(node.getAttribute('id'), 'abcd');
35+
equal(node.innerHTML, 'Hola');
36+
equal(node.style.display, '');
37+
});
38+
});
39+

lib/components/__tests__/Tabs-test.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@ import TestUtils from 'react-addons-test-utils';
44
import { Tabs, Tab, TabPanel, TabList } from '../../main';
55
import { ok, equal } from 'assert';
66

7-
function createTabs(props = {}) {
8-
const {selectedIndex=0, focus=false, onSelect=null, forceRenderTabPanel=false} = props;
9-
7+
function createTabs(props = {
8+
selectedIndex: 0,
9+
focus: false,
10+
onSelect: null,
11+
forceRenderTabPanel: false,
12+
className: null
13+
}) {
1014
return (
11-
<Tabs focus={focus} selectedIndex={selectedIndex} onSelect={onSelect} forceRenderTabPanel={forceRenderTabPanel}>
15+
<Tabs {...props}>
1216
<TabList>
1317
<Tab>Foo</Tab>
1418
<Tab>Bar</Tab>
@@ -48,15 +52,31 @@ describe('react-tabs', function() {
4852

4953
it('should call onSelect when selection changes', function() {
5054
const called = {index: -1, last: -1};
51-
const tabs = TestUtils.renderIntoDocument(createTabs({onSelect: function(index, last) {
52-
called.index = index;
53-
called.last = last;
54-
}}));
55+
const tabs = TestUtils.renderIntoDocument(createTabs({
56+
onSelect: function(index, last) {
57+
called.index = index;
58+
called.last = last;
59+
}
60+
}));
5561

5662
tabs.setSelected(2);
5763
equal(called.index, 2);
5864
equal(called.last, 0);
5965
});
66+
67+
it('should have a default className', function() {
68+
const tabs = TestUtils.renderIntoDocument(createTabs());
69+
const node = findDOMNode(tabs);
70+
71+
equal(node.className, 'ReactTabs react-tabs');
72+
});
73+
74+
it('should accept className', function() {
75+
const tabs = TestUtils.renderIntoDocument(createTabs({className: 'foobar'}));
76+
const node = findDOMNode(tabs);
77+
78+
equal(node.className, 'ReactTabs react-tabs foobar');
79+
});
6080
});
6181

6282
describe('a11y', function() {

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"react-addons-test-utils": "^0.14.0"
3636
},
3737
"dependencies": {
38+
"classnames": "2.2.0",
3839
"js-stylesheet": "0.0.1"
3940
}
4041
}

0 commit comments

Comments
 (0)