Skip to content

Commit 1969e65

Browse files
karevndanez
authored andcommitted
feat(tabs): Allow for higher order components (#196)
* Allow for higher order components * Added tests with higher order components. * Test if solution works with hoist-non-react-statics
1 parent 722d52f commit 1969e65

24 files changed

+246
-27
lines changed

.eslintrc

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@
1717
"react/forbid-prop-types": "off",
1818
"react/sort-comp": "off",
1919
"jsx-a11y/no-static-element-interactions": "off",
20-
"import/no-extraneous-dependencies": ["error", {
21-
"devDependencies": ["**/__tests__/*-test.js"],
22-
"optionalDependencies": false
23-
}]
20+
"import/no-extraneous-dependencies": [
21+
"error",
22+
{
23+
"devDependencies": ["**/__tests__/**/*"],
24+
"optionalDependencies": false
25+
}
26+
]
2427
}
2528
}

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"eslint-plugin-jsx-a11y": "^5.0.3",
6363
"eslint-plugin-prettier": "^2.2.0",
6464
"eslint-plugin-react": "^7.0.1",
65+
"hoist-non-react-statics": "^2.3.1",
6566
"husky": "^0.14.3",
6667
"jest-cli": "^20.0.0",
6768
"lint-staged": "^4.0.4",
@@ -84,7 +85,8 @@
8485
"jest": {
8586
"roots": [
8687
"src"
87-
]
88+
],
89+
"testRegex": "/__tests__/.+-test\\.js$"
8890
},
8991
"lint-staged": {
9092
"src/**/*.js": [

src/components/Tab.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,3 +80,5 @@ export default class Tab extends Component {
8080
);
8181
}
8282
}
83+
84+
Tab.tabsRole = 'Tab';

src/components/TabList.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,5 @@ export default class TabList extends Component {
2222
);
2323
}
2424
}
25+
26+
TabList.tabsRole = 'TabList';

src/components/TabPanel.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,5 @@ export default class TabPanel extends Component {
4949
);
5050
}
5151
}
52+
53+
TabPanel.tabsRole = 'TabPanel';

src/components/Tabs.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,3 +106,5 @@ For more information about controlled and uncontrolled mode of react-tabs see th
106106
return <UncontrolledTabs {...props}>{children}</UncontrolledTabs>;
107107
}
108108
}
109+
110+
Tabs.tabsRole = 'Tabs';

src/components/UncontrolledTabs.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ import React, { cloneElement, Component } from 'react';
33
import cx from 'classnames';
44
import uuid from '../helpers/uuid';
55
import { childrenPropType } from '../helpers/propTypes';
6-
import Tab from './Tab';
7-
import TabList from './TabList';
8-
import TabPanel from './TabPanel';
96
import { getPanelsCount, getTabsCount } from '../helpers/count';
107
import { deepMap } from '../helpers/childrenDeepMap';
8+
import { isTabList, isTabPanel, isTab } from '../helpers/elementTypes';
119

1210
// Determine if a node from event.target is a Tab element
1311
function isTabNode(node) {
@@ -145,7 +143,7 @@ export default class UncontrolledTabs extends Component {
145143
let result = child;
146144

147145
// Clone TabList and Tab components to have refs
148-
if (child.type === TabList) {
146+
if (isTabList(child)) {
149147
let listIndex = 0;
150148

151149
// Figure out if the current focus in the DOM is set on a Tab
@@ -155,7 +153,7 @@ export default class UncontrolledTabs extends Component {
155153
if (canUseActiveElement) {
156154
wasTabFocused = React.Children
157155
.toArray(child.props.children)
158-
.filter(tab => tab.type === Tab)
156+
.filter(isTab)
159157
.some((tab, i) => document.activeElement === this.getTab(i));
160158
}
161159

@@ -182,7 +180,7 @@ export default class UncontrolledTabs extends Component {
182180
return cloneElement(tab, props);
183181
}),
184182
});
185-
} else if (child.type === TabPanel) {
183+
} else if (isTabPanel(child)) {
186184
const props = {
187185
id: this.panelIds[index],
188186
tabId: this.tabIds[index],

src/components/__tests__/Tab-test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import React from 'react';
33
import renderer from 'react-test-renderer';
44
import Tab from '../Tab';
5+
import { TabWrapper } from './helpers/higherOrder';
56

67
function expectToMatchSnapshot(component) {
78
expect(renderer.create(component).toJSON()).toMatchSnapshot();
@@ -51,4 +52,8 @@ describe('<Tab />', () => {
5152
// eslint-disable-next-line jsx-a11y/aria-role
5253
expectToMatchSnapshot(<Tab role="micro-tab" />);
5354
});
55+
56+
it('should allow to be wrapped in higher-order-component', () => {
57+
expectToMatchSnapshot(<TabWrapper />);
58+
});
5459
});

src/components/__tests__/TabList-test.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import Tab from '../Tab';
55
import TabList from '../TabList';
66
import TabPanel from '../TabPanel';
77
import Tabs from '../Tabs';
8+
import { TabListWrapper, TabWrapper } from './helpers/higherOrder';
89

910
function expectToMatchSnapshot(component) {
1011
expect(renderer.create(component).toJSON()).toMatchSnapshot();
@@ -77,4 +78,17 @@ describe('<TabList />', () => {
7778
</Tabs>,
7879
);
7980
});
81+
82+
it('should allow for higher order components', () => {
83+
expectToMatchSnapshot(
84+
<Tabs>
85+
<TabListWrapper>
86+
<TabWrapper>Foo</TabWrapper>
87+
<TabWrapper>Bar</TabWrapper>
88+
</TabListWrapper>
89+
<TabPanel>Foo</TabPanel>
90+
<TabPanel>Bar</TabPanel>
91+
</Tabs>,
92+
);
93+
});
8094
});

src/components/__tests__/TabPanel-test.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import React from 'react';
33
import renderer from 'react-test-renderer';
44
import TabPanel from '../TabPanel';
5+
import { TabPanelWrapper } from './helpers/higherOrder';
56

67
function expectToMatchSnapshot(component) {
78
expect(renderer.create(component).toJSON()).toMatchSnapshot();
@@ -55,4 +56,8 @@ describe('<TabPanel />', () => {
5556
// eslint-disable-next-line jsx-a11y/aria-role
5657
expectToMatchSnapshot(<TabPanel role="micro-tab" />);
5758
});
59+
60+
it('should allow for higher-order components', () => {
61+
expectToMatchSnapshot(<TabPanelWrapper />);
62+
});
5863
});

0 commit comments

Comments
 (0)