From 9bf1c8e4feb04383aa0f8332321f723112734dde Mon Sep 17 00:00:00 2001 From: Aren Blondahl Date: Thu, 11 Aug 2016 20:55:45 -0600 Subject: [PATCH 1/2] Support for fixed & inverse color tabs. --- components/tabs/Tabs.js | 41 +++++++++++++++-- components/tabs/__tests__/index.spec.js | 34 +++++++++++++- components/tabs/_config.scss | 7 +++ components/tabs/readme.md | 45 +++++++++++++++---- components/tabs/theme.scss | 24 ++++++++++ .../main/modules/examples/tabs_example_1.txt | 45 +++++++++++++++---- spec/components/tabs.js | 27 ++++++++++- 7 files changed, 201 insertions(+), 22 deletions(-) diff --git a/components/tabs/Tabs.js b/components/tabs/Tabs.js index 0db6d121..1cab99f6 100644 --- a/components/tabs/Tabs.js +++ b/components/tabs/Tabs.js @@ -13,15 +13,21 @@ const factory = (Tab, TabContent) => { disableAnimatedBottomBorder: PropTypes.bool, index: PropTypes.number, onChange: PropTypes.func, + fixed: PropTypes.bool, + inverse: PropTypes.bool, theme: PropTypes.shape({ navigation: PropTypes.string, pointer: PropTypes.string, - tabs: PropTypes.string + tabs: PropTypes.string, + fixed: PropTypes.string, + inverse: PropTypes.string }) }; static defaultProps = { - index: 0 + index: 0, + fixed: false, + inverse: false }; state = { @@ -30,6 +36,8 @@ const factory = (Tab, TabContent) => { componentDidMount () { !this.props.disableAnimatedBottomBorder && this.updatePointer(this.props.index); + window.addEventListener('resize', this.handleResize); + this.handleResize(); } componentWillReceiveProps (nextProps) { @@ -37,6 +45,8 @@ const factory = (Tab, TabContent) => { } componentWillUnmount () { + window.removeEventListener('resize', this.handleResize); + clearTimeout(this.resizeTimeout); clearTimeout(this.pointerTimeout); } @@ -45,6 +55,21 @@ const factory = (Tab, TabContent) => { if (this.props.onChange) this.props.onChange(idx); }; + handleResize = (event) => { + if (!this.props.fixed) { + return; + } + + if (this.resizeTimeout) { + clearTimeout(this.resizeTimeout); + } + this.resizeTimeout = setTimeout(this.handleResizeEnd, 50); + }; + + handleResizeEnd = () => { + this.updatePointer(this.props.index); + }; + parseChildren () { const headers = []; const contents = []; @@ -104,10 +129,18 @@ const factory = (Tab, TabContent) => { } render () { - const { className, theme } = this.props; + const { className, theme, fixed, inverse } = this.props; const { headers, contents } = this.parseChildren(); + const classes = classnames( + theme.tabs, + className, + { + [theme.fixed]: fixed, + [theme.inverse]: inverse + } + ); return ( -
+
diff --git a/components/tabs/__tests__/index.spec.js b/components/tabs/__tests__/index.spec.js index 311c6331..f20d9596 100644 --- a/components/tabs/__tests__/index.spec.js +++ b/components/tabs/__tests__/index.spec.js @@ -3,10 +3,17 @@ import utils from '../../utils/testing'; import ReactTestUtils from 'react-addons-test-utils'; import React, { Component } from 'react'; +import ReactDOM from 'react-dom'; -import Tabs from '../Tabs'; +import Tabs, { Tabs as RawTabs } from '../Tabs'; import Tab from '../Tab'; import TabContent from '../TabContent'; +import theme from '../theme.scss'; + +const getRenderedClassName = (tree, Component) => { + const rendered = ReactTestUtils.findRenderedComponentWithType(tree, Component); + return ReactDOM.findDOMNode(rendered).getAttribute('class'); +}; describe('Tabs', function () { let tabContents, composition; @@ -48,4 +55,29 @@ describe('Tabs', function () { expect(tabContents[0].props.tabIndex).toEqual(1); }); + describe('#render', function() { + it('does not use fixed by default', function() { + const tree = ReactTestUtils.renderIntoDocument(); + const className = getRenderedClassName(tree, RawTabs); + expect(className).toNotContain(theme.fixed); + }); + + it('uses fixed when set', function() { + const tree = ReactTestUtils.renderIntoDocument(); + const className = getRenderedClassName(tree, RawTabs); + expect(className).toContain(theme.fixed); + }); + + it('does not use inverse by default', function() { + const tree = ReactTestUtils.renderIntoDocument(); + const className = getRenderedClassName(tree, RawTabs); + expect(className).toNotContain(theme.inverse); + }); + + it('uses inverse when set', function() { + const tree = ReactTestUtils.renderIntoDocument(); + const className = getRenderedClassName(tree, RawTabs); + expect(className).toContain(theme.inverse); + }); + }); }); diff --git a/components/tabs/_config.scss b/components/tabs/_config.scss index f67e5587..4a03d641 100644 --- a/components/tabs/_config.scss +++ b/components/tabs/_config.scss @@ -9,3 +9,10 @@ $tab-pointer-height: .2 * $unit !default; $tab-text: $color-black !default; $tab-text-color: $tab-text !default; $tab-text-inactive-color: rgba($tab-text, .7) !default; + +// Inverse +$tab-inverse-bar-color: $color-primary !default; +$tab-inverse-pointer-color: $color-accent !default; +$tab-inverse-text: $color-primary-contrast !default; +$tab-inverse-text-color: $tab-inverse-text !default; +$tab-inverse-text-inactive-color: rgba($tab-inverse-text, .7) !default; diff --git a/components/tabs/readme.md b/components/tabs/readme.md index b2c8d3a1..2c473267 100644 --- a/components/tabs/readme.md +++ b/components/tabs/readme.md @@ -8,26 +8,51 @@ import {Tab, Tabs} from 'react-toolbox'; class TabsTest extends React.Component { state = { - index: 1 + index: 1, + fixedIndex: 1, + inverseIndex: 1 }; handleTabChange = (index) => { this.setState({index}); }; + handleFixedTabChange = (index) => { + this.setState({fixedIndex: index}); + }; + + handleInverseTabChange = (index) => { + this.setState({inverseIndex: index}); + }; + handleActive = () => { console.log('Special one activated'); }; render () { return ( - - Primary content - Secondary content - Disabled content - - Fifth content - +
+ + Primary content + Secondary content + Disabled content + + Fifth content + +
Fixed Tabs
+ + First Content + Second Content + Third Content + +
Inverse Tabs
+ + First Content + Second Content + Third Content + Disabled Content + +
); } } @@ -45,7 +70,9 @@ This component acts as the wrapper and the main controller of the content that i |:-----|:-----|:-----|:-----| | `className` | `String` | `''` | Additional class name to provide custom styling.| | `disableAnimatedBottomBorder` | `Boolean` | `false` | Disable the animation below the active tab.| +| `fixed` | `Boolean` | `false` | If True, the tabs will be 'fixed tabs'.| | `index` | `Number` | `0` | Current | +| `inverse` | `Boolean` | `false` | If True, the tabs will have an inverse style.| | `onChange` | `Function` | | Callback function that is fired when the tab changes.| ### Theming @@ -53,6 +80,8 @@ This component acts as the wrapper and the main controller of the content that i | Name | Description| |:---------|:-----------| | `active` | Added to the active tab content and header.| +| `fixed` | Used to make the tabs 'fixed tabs'.| +| `inverse` | Used to invert the colors.| | `navigation` | Used for the navigation element.| | `pointer` | Used for the moving underline element.| | `tabs` |Used as a root classname for the component.| diff --git a/components/tabs/theme.scss b/components/tabs/theme.scss index f2e77760..173f8c14 100644 --- a/components/tabs/theme.scss +++ b/components/tabs/theme.scss @@ -61,3 +61,27 @@ display: block; } } + +.fixed { + .label { + flex: 1; + text-align: center; + } +} + +.inverse { + .navigation { + background-color: $tab-inverse-bar-color; + } + + .label { + color: $tab-inverse-text-inactive-color; + &.active { + color: $tab-inverse-text-color; + } + } + + .pointer { + background-color: $tab-inverse-pointer-color; + } +} \ No newline at end of file diff --git a/docs/app/components/layout/main/modules/examples/tabs_example_1.txt b/docs/app/components/layout/main/modules/examples/tabs_example_1.txt index 617d2c66..b01e15c5 100644 --- a/docs/app/components/layout/main/modules/examples/tabs_example_1.txt +++ b/docs/app/components/layout/main/modules/examples/tabs_example_1.txt @@ -1,21 +1,50 @@ class TabsExample extends React.Component { state = { - index: 1 + index: 1, + fixedIndex: 1, + inverseIndex: 1 }; handleTabChange = (index) => { this.setState({index}); }; + handleFixedTabChange = (index) => { + this.setState({fixedIndex: index}); + }; + + handleInverseTabChange = (index) => { + this.setState({inverseIndex: index}); + }; + + handleActive = () => { + console.log('Special one activated'); + }; + render () { return ( - - Primary content - Secondary content - Disabled content - - Fifth content - +
+ + Primary content + Secondary content + Disabled content + + Fifth content + +
Fixed Tabs
+ + First Content + Second Content + Third Content + +
Inverse Tabs
+ + First Content + Second Content + Third Content + Disabled Content + +
); } } diff --git a/spec/components/tabs.js b/spec/components/tabs.js index 73d037f2..e1be5879 100644 --- a/spec/components/tabs.js +++ b/spec/components/tabs.js @@ -3,13 +3,23 @@ import { Tabs, Tab } from '../../components/tabs'; class TabsTest extends React.Component { state = { - index: 1 + index: 1, + fixedIndex: 1, + inverseIndex: 1 }; handleTabChange = (index) => { this.setState({index}); }; + handleFixedTabChange = (index) => { + this.setState({fixedIndex: index}); + }; + + handleInverseTabChange = (index) => { + this.setState({inverseIndex: index}); + }; + handleActive = () => { console.log('Special one activated'); }; @@ -26,6 +36,21 @@ class TabsTest extends React.Component { Fifth content
+
Fixed Tabs
+

These tabs fill the given space.

+ + First Content + Second Content + Third Content + +
Inverse Tabs
+

These tabs have an inverted theme.

+ + First Content + Second Content + Third Content + Disabled Content + ); } From 8dedcfce776b1f372402c7aebbc91fcd9202aebf Mon Sep 17 00:00:00 2001 From: Aren Blondahl Date: Thu, 11 Aug 2016 21:31:08 -0600 Subject: [PATCH 2/2] Fixed linting errors. --- components/tabs/Tabs.js | 14 +++++++------- components/tabs/__tests__/index.spec.js | 14 +++++++------- components/tabs/theme.scss | 2 +- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/components/tabs/Tabs.js b/components/tabs/Tabs.js index 1cab99f6..cdedb23b 100644 --- a/components/tabs/Tabs.js +++ b/components/tabs/Tabs.js @@ -11,23 +11,23 @@ const factory = (Tab, TabContent) => { children: PropTypes.node, className: PropTypes.string, disableAnimatedBottomBorder: PropTypes.bool, - index: PropTypes.number, - onChange: PropTypes.func, fixed: PropTypes.bool, + index: PropTypes.number, inverse: PropTypes.bool, + onChange: PropTypes.func, theme: PropTypes.shape({ + fixed: PropTypes.string, + inverse: PropTypes.string, navigation: PropTypes.string, pointer: PropTypes.string, - tabs: PropTypes.string, - fixed: PropTypes.string, - inverse: PropTypes.string + tabs: PropTypes.string }) }; static defaultProps = { index: 0, fixed: false, - inverse: false + inverse: false }; state = { @@ -55,7 +55,7 @@ const factory = (Tab, TabContent) => { if (this.props.onChange) this.props.onChange(idx); }; - handleResize = (event) => { + handleResize = () => { if (!this.props.fixed) { return; } diff --git a/components/tabs/__tests__/index.spec.js b/components/tabs/__tests__/index.spec.js index f20d9596..ad53cefc 100644 --- a/components/tabs/__tests__/index.spec.js +++ b/components/tabs/__tests__/index.spec.js @@ -10,8 +10,8 @@ import Tab from '../Tab'; import TabContent from '../TabContent'; import theme from '../theme.scss'; -const getRenderedClassName = (tree, Component) => { - const rendered = ReactTestUtils.findRenderedComponentWithType(tree, Component); +const getRenderedClassName = (tree, TargetComponent) => { + const rendered = ReactTestUtils.findRenderedComponentWithType(tree, TargetComponent); return ReactDOM.findDOMNode(rendered).getAttribute('class'); }; @@ -55,26 +55,26 @@ describe('Tabs', function () { expect(tabContents[0].props.tabIndex).toEqual(1); }); - describe('#render', function() { - it('does not use fixed by default', function() { + describe('#render', function () { + it('does not use fixed by default', function () { const tree = ReactTestUtils.renderIntoDocument(); const className = getRenderedClassName(tree, RawTabs); expect(className).toNotContain(theme.fixed); }); - it('uses fixed when set', function() { + it('uses fixed when set', function () { const tree = ReactTestUtils.renderIntoDocument(); const className = getRenderedClassName(tree, RawTabs); expect(className).toContain(theme.fixed); }); - it('does not use inverse by default', function() { + it('does not use inverse by default', function () { const tree = ReactTestUtils.renderIntoDocument(); const className = getRenderedClassName(tree, RawTabs); expect(className).toNotContain(theme.inverse); }); - it('uses inverse when set', function() { + it('uses inverse when set', function () { const tree = ReactTestUtils.renderIntoDocument(); const className = getRenderedClassName(tree, RawTabs); expect(className).toContain(theme.inverse); diff --git a/components/tabs/theme.scss b/components/tabs/theme.scss index 173f8c14..9d9fda29 100644 --- a/components/tabs/theme.scss +++ b/components/tabs/theme.scss @@ -84,4 +84,4 @@ .pointer { background-color: $tab-inverse-pointer-color; } -} \ No newline at end of file +}