@@ -59,13 +59,13 @@ const App = React.createClass({
openModal() {
this.setState({
- isModalOpen: true
+ isModalOpen: true,
});
},
closeModal() {
this.setState({
- isModalOpen: false
+ isModalOpen: false,
});
},
@@ -75,10 +75,10 @@ const App = React.createClass({
this.state.tabs.push({
label: label,
- content: content
+ content: content,
});
this.setState({
- selectedIndex: this.state.tabs.length - 1
+ selectedIndex: this.state.tabs.length - 1,
});
this.closeModal();
},
@@ -86,7 +86,7 @@ const App = React.createClass({
removeTab(index) {
this.state.tabs.splice(index, 1);
this.forceUpdate();
- }
+ },
});
-ReactDOM.render(
, document.getElementById('example'));
diff --git a/examples/focus/app.js b/examples/focus/app.js
index 11ea454b53..361145f14f 100644
--- a/examples/focus/app.js
+++ b/examples/focus/app.js
@@ -1,6 +1,6 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import { Tab, Tabs, TabList, TabPanel } from '../../lib/main';
+import { Tab, Tabs, TabList, TabPanel } from '../../src/main';
const App = React.createClass({
handleInputChange() {
@@ -9,7 +9,7 @@ const App = React.createClass({
render() {
return (
-
+
First
@@ -28,7 +28,7 @@ const App = React.createClass({
);
- }
+ },
});
-ReactDOM.render(
, document.getElementById('example'));
+ReactDOM.render(
, document.getElementById('example'));
diff --git a/lib/components/__tests__/Tab-test.js b/lib/components/__tests__/Tab-test.js
deleted file mode 100644
index 5127ab324e..0000000000
--- a/lib/components/__tests__/Tab-test.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import React from 'react';
-import { findDOMNode } from 'react-dom';
-import TestUtils from 'react-addons-test-utils';
-import { Tab } from '../../main';
-import { equal } from 'assert';
-
-/* eslint func-names:0 */
-describe('Tab', function() {
- it('should have sane defaults', function() {
- const tab = TestUtils.renderIntoDocument(
);
- const node = findDOMNode(tab);
-
- equal(node.className, 'ReactTabs__Tab');
- equal(node.getAttribute('role'), 'tab');
- equal(node.getAttribute('aria-selected'), 'false');
- equal(node.getAttribute('aria-expanded'), 'false');
- equal(node.getAttribute('aria-disabled'), 'false');
- equal(node.getAttribute('aria-controls'), null);
- equal(node.getAttribute('id'), null);
- equal(node.innerHTML, '');
- });
-
- it('should accept className', function() {
- const tab = TestUtils.renderIntoDocument(
);
- const node = findDOMNode(tab);
-
- equal(node.className, 'ReactTabs__Tab foobar');
- });
-
- it('should support being selected', function() {
- const tab = TestUtils.renderIntoDocument(
Hello );
- const node = findDOMNode(tab);
-
- equal(node.className, 'ReactTabs__Tab ReactTabs__Tab--selected');
- equal(node.getAttribute('aria-selected'), 'true');
- equal(node.getAttribute('aria-expanded'), 'true');
- equal(node.getAttribute('aria-controls'), '1234');
- equal(node.getAttribute('id'), 'abcd');
- equal(node.innerHTML, 'Hello');
- });
-
- it('should support being disabled', function() {
- const tab = TestUtils.renderIntoDocument(
);
- const node = findDOMNode(tab);
-
- equal(node.className, 'ReactTabs__Tab ReactTabs__Tab--disabled');
- });
-});
diff --git a/lib/components/__tests__/TabList-test.js b/lib/components/__tests__/TabList-test.js
deleted file mode 100644
index f9ce2dccbb..0000000000
--- a/lib/components/__tests__/TabList-test.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import React from 'react';
-import { findDOMNode } from 'react-dom';
-import TestUtils from 'react-addons-test-utils';
-import { TabList } from '../../main';
-import { equal } from 'assert';
-
-/* eslint func-names:0 */
-describe('Tab', function() {
- it('should have sane defaults', function() {
- const tabList = TestUtils.renderIntoDocument(
);
- const node = findDOMNode(tabList);
-
- equal(node.className, 'ReactTabs__TabList');
- equal(node.getAttribute('role'), 'tablist');
- });
-
- it('should accept className', function() {
- const tabList = TestUtils.renderIntoDocument(
);
- const node = findDOMNode(tabList);
-
- equal(node.className, 'ReactTabs__TabList foobar');
- });
-});
diff --git a/lib/components/__tests__/TabPanel-test.js b/lib/components/__tests__/TabPanel-test.js
deleted file mode 100644
index 2071adf78e..0000000000
--- a/lib/components/__tests__/TabPanel-test.js
+++ /dev/null
@@ -1,39 +0,0 @@
-import React from 'react';
-import { findDOMNode } from 'react-dom';
-import TestUtils from 'react-addons-test-utils';
-import { TabPanel } from '../../main';
-import { equal } from 'assert';
-
-/* eslint func-names:0 */
-describe('Tab', function() {
- it('should have sane defaults', function() {
- const tabPanel = TestUtils.renderIntoDocument(
Hola );
- const node = findDOMNode(tabPanel);
-
- equal(node.className, 'ReactTabs__TabPanel');
- equal(node.getAttribute('role'), 'tabpanel');
- equal(node.getAttribute('aria-labelledby'), null);
- equal(node.getAttribute('id'), null);
- equal(node.innerHTML, '');
- equal(node.style.display, 'none');
- });
-
- it('should accept className', function() {
- const tabPanel = TestUtils.renderIntoDocument(
);
- const node = findDOMNode(tabPanel);
-
- equal(node.className, 'ReactTabs__TabPanel foobar');
- });
-
- it('should support being selected', function() {
- const tabPanel = TestUtils.renderIntoDocument(
Hola );
- const node = findDOMNode(tabPanel);
-
- equal(node.className, 'ReactTabs__TabPanel ReactTabs__TabPanel--selected');
- equal(node.getAttribute('aria-labelledby'), '1234');
- equal(node.getAttribute('id'), 'abcd');
- equal(node.innerHTML, 'Hola');
- equal(node.style.display, '');
- });
-});
-
diff --git a/lib/components/__tests__/Tabs-test.js b/lib/components/__tests__/Tabs-test.js
deleted file mode 100644
index 2e78244c5d..0000000000
--- a/lib/components/__tests__/Tabs-test.js
+++ /dev/null
@@ -1,260 +0,0 @@
-import React from 'react';
-import { findDOMNode } from 'react-dom';
-import TestUtils from 'react-addons-test-utils';
-import { Tabs, Tab, TabPanel, TabList } from '../../main';
-import { ok, equal } from 'assert';
-
-function createTabs(props = {
- selectedIndex: 0,
- focus: false,
- onSelect: null,
- forceRenderTabPanel: false,
- className: null
-}) {
- return (
-
-
- Foo
- Bar
- Baz
- Qux
-
- Hello Foo
- Hello Bar
- Hello Baz
- Hello Qux
-
- );
-}
-
-function assertTabSelected(tabs, index) {
- equal(findDOMNode(tabs.getTab(index)).getAttribute('tabindex'), '0');
- equal(findDOMNode(tabs.getTab(index)).getAttribute('selected'), 'selected');
- equal(findDOMNode(tabs.getTab(index)).getAttribute('aria-selected'), 'true');
- equal(findDOMNode(tabs.getTab(index)).getAttribute('aria-expanded'), 'true');
- equal(findDOMNode(tabs.getPanel(index)).style.display, '');
-}
-
-/* eslint func-names:0 */
-describe('react-tabs', function() {
- describe('props', function() {
- it('should default to selectedIndex being 0', function() {
- const tabs = TestUtils.renderIntoDocument(createTabs());
-
- assertTabSelected(tabs, 0);
- });
-
- it('should honor selectedIndex prop', function() {
- const tabs = TestUtils.renderIntoDocument(createTabs({selectedIndex: 1}));
-
- assertTabSelected(tabs, 1);
- });
-
- it('should call onSelect when selection changes', function() {
- const called = {index: -1, last: -1};
- const tabs = TestUtils.renderIntoDocument(createTabs({
- onSelect: function(index, last) {
- called.index = index;
- called.last = last;
- }
- }));
-
- tabs.setSelected(2);
- equal(called.index, 2);
- equal(called.last, 0);
- });
-
- it('should have a default className', function() {
- const tabs = TestUtils.renderIntoDocument(createTabs());
- const node = findDOMNode(tabs);
-
- equal(node.className, 'ReactTabs react-tabs');
- });
-
- it('should accept className', function() {
- const tabs = TestUtils.renderIntoDocument(createTabs({className: 'foobar'}));
- const node = findDOMNode(tabs);
-
- equal(node.className, 'ReactTabs react-tabs foobar');
- });
- });
-
- describe('a11y', function() {
- it('should have appropriate role and aria attributes', function() {
- const tabs = TestUtils.renderIntoDocument(createTabs());
-
- equal(findDOMNode(tabs.getTabList()).getAttribute('role'), 'tablist');
-
- for (let i = 0, l = tabs.getTabsCount(); i < l; i++) {
- const tab = findDOMNode(tabs.getTab(i));
- const panel = findDOMNode(tabs.getPanel(i));
-
- equal(tab.getAttribute('role'), 'tab');
- equal(panel.getAttribute('role'), 'tabpanel');
-
- equal(tab.getAttribute('aria-controls'), panel.getAttribute('id'));
- equal(panel.getAttribute('aria-labelledby'), tab.getAttribute('id'));
- }
-
- equal(findDOMNode(tabs.getTab(3)).getAttribute('aria-disabled'), 'true');
- });
- });
-
- describe('interaction', function() {
- it('should update selectedIndex when clicked', function() {
- const tabs = TestUtils.renderIntoDocument(createTabs());
-
- TestUtils.Simulate.click(findDOMNode(tabs.getTab(1)));
- assertTabSelected(tabs, 1);
- });
-
- it('should update selectedIndex when tab child is clicked', function() {
- const tabs = TestUtils.renderIntoDocument(createTabs());
-
- TestUtils.Simulate.click(findDOMNode(tabs.getTab(2)).firstChild);
- assertTabSelected(tabs, 2);
- });
-
- it('should not change selectedIndex when clicking a disabled tab', function() {
- const tabs = TestUtils.renderIntoDocument(createTabs({selectedIndex: 0}));
-
- TestUtils.Simulate.click(findDOMNode(tabs.getTab(3)));
- assertTabSelected(tabs, 0);
- });
-
- // TODO: Can't seem to make this fail when removing fix :`(
- // See https://github.com/rackt/react-tabs/pull/7
- // it('should preserve selectedIndex when typing', function () {
- // let App = React.createClass({
- // handleKeyDown: function () { this.forceUpdate(); },
- // render: function () {
- // return (
- //
- //
- // First
- // Second
- //
- // 1st
- //
- //
- // );
- // }
- // });
- //
- // let tabs = TestUtils.renderIntoDocument(
).refs.tabs;
- // let input = tabs.getDOMNode().querySelector('input');
- //
- // input.focus();
- // TestUtils.Simulate.keyDown(input, {
- // keyCode: 'a'.charCodeAt()
- // });
- //
- // assertTabSelected(tabs, 1);
- // });
- });
-
- describe('performance', function() {
- it('should only render the active tab panel', function() {
- const tabs = TestUtils.renderIntoDocument(createTabs());
-
- equal(findDOMNode(tabs.getPanel(0)).innerHTML, 'Hello Foo');
- equal(findDOMNode(tabs.getPanel(1)).innerHTML, '');
- equal(findDOMNode(tabs.getPanel(2)).innerHTML, '');
-
- TestUtils.Simulate.click(findDOMNode(tabs.getTab(1)));
- equal(findDOMNode(tabs.getPanel(0)).innerHTML, '');
- equal(findDOMNode(tabs.getPanel(1)).innerHTML, 'Hello Bar');
- equal(findDOMNode(tabs.getPanel(2)).innerHTML, '');
-
- TestUtils.Simulate.click(findDOMNode(tabs.getTab(2)));
- equal(findDOMNode(tabs.getPanel(0)).innerHTML, '');
- equal(findDOMNode(tabs.getPanel(1)).innerHTML, '');
- equal(findDOMNode(tabs.getPanel(2)).innerHTML, 'Hello Baz');
- });
-
- it('should render all tabs if forceRenderTabPanel is true', function() {
- const tabs = TestUtils.renderIntoDocument(createTabs({forceRenderTabPanel: true}));
- equal(findDOMNode(tabs.getPanel(0)).innerHTML, 'Hello Foo');
- equal(findDOMNode(tabs.getPanel(1)).innerHTML, 'Hello Bar');
- equal(findDOMNode(tabs.getPanel(2)).innerHTML, 'Hello Baz');
- });
- });
-
- describe('validation', function() {
- it('should result with warning when tabs/panels are imbalanced', function() {
- const tabs = TestUtils.renderIntoDocument(
-
-
- Foo
-
-
- );
-
- const result = Tabs.propTypes.children(tabs.props, 'children', 'Tabs');
- ok(result instanceof Error);
- });
-
- it('should result with a warning when wrong element is found', function() {
- const tabs = TestUtils.renderIntoDocument(
-
-
-
-
-
-
-
- );
-
- const result = Tabs.propTypes.children(tabs.props, 'children', 'Tabs');
- ok(result instanceof Error);
- });
-
- it('should be okay with rendering without any children', function() {
- let error = false;
- try {
- TestUtils.renderIntoDocument(
-
- );
- } catch (e) {
- error = true;
- }
-
- ok(!error);
- });
-
- it('should be okay with rendering just TabList', function() {
- let error = false;
- try {
- TestUtils.renderIntoDocument(
-
-
-
- );
- } catch (e) {
- error = true;
- }
-
- ok(!error);
- });
-
- it('should gracefully render null', function() {
- let error = false;
- try {
- TestUtils.renderIntoDocument(
-
-
- Tab A
- { false && Tab B }
-
- Content A
- { false && Content B }
-
- );
- } catch (e) {
- error = true;
- }
-
- ok(!error);
- });
- });
-});
diff --git a/lib/main.js b/lib/main.js
deleted file mode 100644
index f33cdcc393..0000000000
--- a/lib/main.js
+++ /dev/null
@@ -1,6 +0,0 @@
-module.exports = {
- Tabs: require('./components/Tabs'),
- TabList: require('./components/TabList'),
- Tab: require('./components/Tab'),
- TabPanel: require('./components/TabPanel')
-};
diff --git a/package.json b/package.json
index 0f28abbc1e..1e3dce3da3 100644
--- a/package.json
+++ b/package.json
@@ -4,8 +4,17 @@
"description": "React tabs component",
"main": "lib/main.js",
"scripts": {
- "test": "./node_modules/.bin/rackt test --single-run --browsers Firefox",
- "start": "./node_modules/.bin/rackt server"
+ "clean": "rimraf lib",
+ "build:commonjs": "babel src/ --out-dir lib/ --ignore __tests__,__mocks__",
+ "build:umd": "webpack --devtool source-map --config webpack.build.js",
+ "build:umd:min": "cross-env MINIFY=1 webpack --devtool source-map --config webpack.build.js",
+ "build": "npm run clean && npm run build:commonjs && npm run bundle",
+ "bundle": "mkdir -p dist && npm run build:umd && npm run build:umd:min",
+ "lint": "eslint src",
+ "preversion": "npm run lint && npm test && npm run bundle && git add dist/ && git commit -m 'Publish: build bower distribution'",
+ "prepublish": "npm run build",
+ "test": "jest",
+ "start": "webpack-dev-server --inline --content-base examples/"
},
"repository": {
"type": "git",
@@ -16,6 +25,10 @@
"bugs": {
"url": "https://github.com/rackt/react-tabs/issues"
},
+ "files": [
+ "dist",
+ "lib"
+ ],
"homepage": "https://github.com/rackt/react-tabs",
"keywords": [
"react",
@@ -28,14 +41,40 @@
"react-dom": "^0.14.7"
},
"devDependencies": {
- "rackt-cli": "^0.5.4",
+ "babel-cli": "^6.9.0",
+ "babel-core": "^6.9.1",
+ "babel-jest": "^12.1.0",
+ "babel-loader": "^6.2.4",
+ "babel-preset-es2015": "^6.9.0",
+ "babel-preset-react": "^6.5.0",
+ "cross-env": "^1.0.8",
+ "enzyme": "^2.3.0",
+ "eslint": "^2.11.1",
+ "eslint-config-airbnb": "^9.0.1",
+ "eslint-plugin-import": "^1.8.0",
+ "eslint-plugin-jsx-a11y": "^1.2.2",
+ "eslint-plugin-react": "^5.1.1",
+ "jest-cli": "^12.1.1",
"react": "^0.14.7",
+ "react-addons-test-utils": "^0.14.7",
"react-dom": "^0.14.7",
- "react-modal": "rackt/react-modal",
- "react-addons-test-utils": "^0.14.7"
+ "react-modal": "reactjs/react-modal",
+ "rimraf": "^2.5.2",
+ "webpack": "^1.13.1",
+ "webpack-dev-server": "^1.14.1"
},
"dependencies": {
"classnames": "^2.2",
"js-stylesheet": "^0.0.1"
+ },
+ "jest": {
+ "automock": false,
+ "testPathDirs": [
+ "src"
+ ],
+ "unmockedModulePathPatterns": [
+ "node_modules",
+ "babel"
+ ]
}
-}
\ No newline at end of file
+}
diff --git a/src/__tests__/main-test.js b/src/__tests__/main-test.js
new file mode 100644
index 0000000000..e94db46825
--- /dev/null
+++ b/src/__tests__/main-test.js
@@ -0,0 +1,15 @@
+/* global describe, it, expect */
+import { Tab, Tabs, TabList, TabPanel } from '../main';
+import TabComponent from '../components/Tab';
+import TabListComponent from '../components/TabList';
+import TabsComponent from '../components/Tabs';
+import TabPanelComponent from '../components/TabPanel';
+
+describe('
', () => {
+ it('should correctly export all components', () => {
+ expect(Tab).toEqual(TabComponent);
+ expect(TabList).toEqual(TabListComponent);
+ expect(Tabs).toEqual(TabsComponent);
+ expect(TabPanel).toEqual(TabPanelComponent);
+ });
+});
diff --git a/lib/components/Tab.js b/src/components/Tab.js
similarity index 90%
rename from lib/components/Tab.js
rename to src/components/Tab.js
index 51090143ed..f323a36da1 100644
--- a/lib/components/Tab.js
+++ b/src/components/Tab.js
@@ -1,4 +1,4 @@
-import React, {PropTypes} from 'react';
+import React, { PropTypes } from 'react';
import { findDOMNode } from 'react-dom';
import cx from 'classnames';
@@ -27,8 +27,8 @@ module.exports = React.createClass({
children: PropTypes.oneOfType([
PropTypes.array,
PropTypes.object,
- PropTypes.string
- ])
+ PropTypes.string,
+ ]),
},
getDefaultProps() {
@@ -36,7 +36,7 @@ module.exports = React.createClass({
focus: false,
selected: false,
id: null,
- panelId: null
+ panelId: null,
};
},
@@ -56,7 +56,7 @@ module.exports = React.createClass({
this.props.className,
{
'ReactTabs__Tab--selected': this.props.selected,
- 'ReactTabs__Tab--disabled': this.props.disabled
+ 'ReactTabs__Tab--disabled': this.props.disabled,
}
)}
role="tab"
@@ -69,5 +69,5 @@ module.exports = React.createClass({
{this.props.children}
);
- }
+ },
});
diff --git a/lib/components/TabList.js b/src/components/TabList.js
similarity index 84%
rename from lib/components/TabList.js
rename to src/components/TabList.js
index 2f98267b11..f5b96a673e 100644
--- a/lib/components/TabList.js
+++ b/src/components/TabList.js
@@ -1,4 +1,4 @@
-import React, {PropTypes} from 'react';
+import React, { PropTypes } from 'react';
import cx from 'classnames';
module.exports = React.createClass({
@@ -8,8 +8,8 @@ module.exports = React.createClass({
className: PropTypes.string,
children: PropTypes.oneOfType([
PropTypes.object,
- PropTypes.array
- ])
+ PropTypes.array,
+ ]),
},
render() {
@@ -24,5 +24,5 @@ module.exports = React.createClass({
{this.props.children}
);
- }
+ },
});
diff --git a/lib/components/TabPanel.js b/src/components/TabPanel.js
similarity index 81%
rename from lib/components/TabPanel.js
rename to src/components/TabPanel.js
index 7ed7888b06..81343d2f6e 100644
--- a/lib/components/TabPanel.js
+++ b/src/components/TabPanel.js
@@ -1,4 +1,4 @@
-import React, {PropTypes} from 'react';
+import React, { PropTypes } from 'react';
import cx from 'classnames';
module.exports = React.createClass({
@@ -12,19 +12,19 @@ module.exports = React.createClass({
children: PropTypes.oneOfType([
PropTypes.array,
PropTypes.object,
- PropTypes.string
- ])
+ PropTypes.string,
+ ]),
},
contextTypes: {
- forceRenderTabPanel: PropTypes.bool
+ forceRenderTabPanel: PropTypes.bool,
},
getDefaultProps() {
return {
selected: false,
id: null,
- tabId: null
+ tabId: null,
};
},
@@ -39,16 +39,16 @@ module.exports = React.createClass({
'ReactTabs__TabPanel',
this.props.className,
{
- 'ReactTabs__TabPanel--selected': this.props.selected
+ 'ReactTabs__TabPanel--selected': this.props.selected,
}
)}
role="tabpanel"
id={this.props.id}
aria-labelledby={this.props.tabId}
- style={{display: this.props.selected ? null : 'none'}}
+ style={{ display: this.props.selected ? null : 'none' }}
>
{children}
);
- }
+ },
});
diff --git a/lib/components/Tabs.js b/src/components/Tabs.js
similarity index 92%
rename from lib/components/Tabs.js
rename to src/components/Tabs.js
index ad99d53fbe..200a2c9ef8 100644
--- a/lib/components/Tabs.js
+++ b/src/components/Tabs.js
@@ -1,4 +1,4 @@
-import React, {PropTypes, cloneElement} from 'react';
+import React, { PropTypes, cloneElement } from 'react';
import { findDOMNode } from 'react-dom';
import cx from 'classnames';
import jss from 'js-stylesheet';
@@ -26,24 +26,24 @@ module.exports = React.createClass({
onSelect: PropTypes.func,
focus: PropTypes.bool,
children: childrenPropType,
- forceRenderTabPanel: PropTypes.bool
+ forceRenderTabPanel: PropTypes.bool,
},
childContextTypes: {
- forceRenderTabPanel: PropTypes.bool
+ forceRenderTabPanel: PropTypes.bool,
},
statics: {
setUseDefaultStyles(use) {
useDefaultStyles = use;
- }
+ },
},
getDefaultProps() {
return {
selectedIndex: -1,
focus: false,
- forceRenderTabPanel: false
+ forceRenderTabPanel: false,
};
},
@@ -53,13 +53,13 @@ module.exports = React.createClass({
getChildContext() {
return {
- forceRenderTabPanel: this.props.forceRenderTabPanel
+ forceRenderTabPanel: this.props.forceRenderTabPanel,
};
},
componentDidMount() {
if (useDefaultStyles) {
- jss(require('../helpers/styles.js'));
+ jss(require('../helpers/styles.js')); // eslint-disable-line global-require
}
},
@@ -67,47 +67,6 @@ module.exports = React.createClass({
this.setState(this.copyPropsToState(newProps));
},
- handleClick(e) {
- let node = e.target;
- do {
- if (isTabNode(node)) {
- if (isTabDisabled(node)) {
- return;
- }
-
- const index = [].slice.call(node.parentNode.children).indexOf(node);
- this.setSelected(index);
- return;
- }
- } while ((node = node.parentNode) !== null);
- },
-
- handleKeyDown(e) {
- if (isTabNode(e.target)) {
- let index = this.state.selectedIndex;
- let preventDefault = false;
-
- // Select next tab to the left
- if (e.keyCode === 37 || e.keyCode === 38) {
- index = this.getPrevTab(index);
- preventDefault = true;
- }
- // Select next tab to the right
- /* eslint brace-style:0 */
- else if (e.keyCode === 39 || e.keyCode === 40) {
- index = this.getNextTab(index);
- preventDefault = true;
- }
-
- // This prevents scrollbars from moving around
- if (preventDefault) {
- e.preventDefault();
- }
-
- this.setSelected(index, true);
- }
- },
-
setSelected(index, focus) {
// Don't do anything if nothing has changed
if (index === this.state.selectedIndex) return;
@@ -188,11 +147,11 @@ module.exports = React.createClass({
},
getTab(index) {
- return this.refs['tabs-' + index];
+ return this.refs[`tabs-${index}`];
},
getPanel(index) {
- return this.refs['panels-' + index];
+ return this.refs[`panels-${index}`];
},
getChildren() {
@@ -234,7 +193,7 @@ module.exports = React.createClass({
return null;
}
- const ref = 'tabs-' + index;
+ const ref = `tabs-${index}`;
const id = tabIds[index];
const panelId = panelIds[index];
const selected = state.selectedIndex === index;
@@ -247,9 +206,9 @@ module.exports = React.createClass({
id,
panelId,
selected,
- focus
+ focus,
});
- })
+ }),
});
// Reset index for panels
@@ -257,7 +216,7 @@ module.exports = React.createClass({
}
// Clone TabPanel components to have refs
else {
- const ref = 'panels-' + index;
+ const ref = `panels-${index}`;
const id = panelIds[index];
const tabId = tabIds[index];
const selected = state.selectedIndex === index;
@@ -268,7 +227,7 @@ module.exports = React.createClass({
ref,
id,
tabId,
- selected
+ selected,
});
}
@@ -276,6 +235,73 @@ module.exports = React.createClass({
});
},
+ handleKeyDown(e) {
+ if (isTabNode(e.target)) {
+ let index = this.state.selectedIndex;
+ let preventDefault = false;
+
+ // Select next tab to the left
+ if (e.keyCode === 37 || e.keyCode === 38) {
+ index = this.getPrevTab(index);
+ preventDefault = true;
+ }
+ // Select next tab to the right
+ /* eslint brace-style:0 */
+ else if (e.keyCode === 39 || e.keyCode === 40) {
+ index = this.getNextTab(index);
+ preventDefault = true;
+ }
+
+ // This prevents scrollbars from moving around
+ if (preventDefault) {
+ e.preventDefault();
+ }
+
+ this.setSelected(index, true);
+ }
+ },
+
+ handleClick(e) {
+ let node = e.target;
+ do { // eslint-disable-line no-cond-assign
+ if (isTabNode(node)) {
+ if (isTabDisabled(node)) {
+ return;
+ }
+
+ const index = [].slice.call(node.parentNode.children).indexOf(node);
+ this.setSelected(index);
+ return;
+ }
+ } while ((node = node.parentNode) !== null);
+ },
+
+ // This is an anti-pattern, so sue me
+ copyPropsToState(props) {
+ let selectedIndex = props.selectedIndex;
+
+ // If no selectedIndex prop was supplied, then try
+ // preserving the existing selectedIndex from state.
+ // If the state has not selectedIndex, default
+ // to the first tab in the TabList.
+ //
+ // TODO: Need automation testing around this
+ // Manual testing can be done using examples/focus
+ // See 'should preserve selectedIndex when typing' in specs/Tabs.spec.js
+ if (selectedIndex === -1) {
+ if (this.state && this.state.selectedIndex) {
+ selectedIndex = this.state.selectedIndex;
+ } else {
+ selectedIndex = 0;
+ }
+ }
+
+ return {
+ selectedIndex,
+ focus: props.focus,
+ };
+ },
+
render() {
// This fixes an issue with focus management.
//
@@ -310,30 +336,4 @@ module.exports = React.createClass({