Merge pull request #706 from Reanmachine/reanmachine/tabs

Support for fixed & inverse color tabs.
old
Javi Velasco 2016-08-22 20:57:58 +02:00 committed by GitHub
commit 766eeac4e2
7 changed files with 200 additions and 21 deletions

View File

@ -11,9 +11,13 @@ const factory = (Tab, TabContent) => {
children: PropTypes.node,
className: PropTypes.string,
disableAnimatedBottomBorder: PropTypes.bool,
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
@ -21,7 +25,9 @@ const factory = (Tab, TabContent) => {
};
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 = () => {
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 (
<div ref='tabs' data-react-toolbox='tabs' className={classnames(theme.tabs, className)}>
<div ref='tabs' data-react-toolbox='tabs' className={classes}>
<nav className={theme.navigation} ref='navigation'>
{this.renderHeaders(headers)}
</nav>

View File

@ -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, TargetComponent) => {
const rendered = ReactTestUtils.findRenderedComponentWithType(tree, TargetComponent);
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(<Tabs theme={theme} />);
const className = getRenderedClassName(tree, RawTabs);
expect(className).toNotContain(theme.fixed);
});
it('uses fixed when set', function () {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} fixed />);
const className = getRenderedClassName(tree, RawTabs);
expect(className).toContain(theme.fixed);
});
it('does not use inverse by default', function () {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} />);
const className = getRenderedClassName(tree, RawTabs);
expect(className).toNotContain(theme.inverse);
});
it('uses inverse when set', function () {
const tree = ReactTestUtils.renderIntoDocument(<Tabs theme={theme} inverse />);
const className = getRenderedClassName(tree, RawTabs);
expect(className).toContain(theme.inverse);
});
});
});

View File

@ -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;

View File

@ -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 (
<Tabs index={this.state.index} onChange={this.handleTabChange}>
<Tab label='Primary'><small>Primary content</small></Tab>
<Tab label='Secondary' onActive={this.handleActive}><small>Secondary content</small></Tab>
<Tab label='Third' disabled><small>Disabled content</small></Tab>
<Tab label='Fourth' hidden><small>Fourth content hidden</small></Tab>
<Tab label='Fifth'><small>Fifth content</small></Tab>
</Tabs>
<section>
<Tabs index={this.state.index} onChange={this.handleTabChange}>
<Tab label='Primary'><small>Primary content</small></Tab>
<Tab label='Secondary' onActive={this.handleActive}><small>Secondary content</small></Tab>
<Tab label='Third' disabled><small>Disabled content</small></Tab>
<Tab label='Fourth' hidden><small>Fourth content hidden</small></Tab>
<Tab label='Fifth'><small>Fifth content</small></Tab>
</Tabs>
<h5>Fixed Tabs</h5>
<Tabs index={this.state.fixedIndex} onChange={this.handleFixedTabChange} fixed>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
</Tabs>
<h5>Inverse Tabs</h5>
<Tabs index={this.state.inverseIndex} onChange={this.handleInverseTabChange} inverse>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
<Tab label='Disabled' disabled><small>Disabled Content</small></Tab>
</Tabs>
</section>
);
}
}
@ -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 <Tab> |
| `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.|

View File

@ -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;
}
}

View File

@ -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 (
<Tabs index={this.state.index} onChange={this.handleTabChange}>
<Tab label='Primary'><small>Primary content</small></Tab>
<Tab label='Secondary'><small>Secondary content</small></Tab>
<Tab label='Third' disabled><small>Disabled content</small></Tab>
<Tab label='Fourth' hidden><small>Fourth content hidden</small></Tab>
<Tab label='Fifth'><small>Fifth content</small></Tab>
</Tabs>
<section>
<Tabs index={this.state.index} onChange={this.handleTabChange}>
<Tab label='Primary'><small>Primary content</small></Tab>
<Tab label='Secondary' onActive={this.handleActive}><small>Secondary content</small></Tab>
<Tab label='Third' disabled><small>Disabled content</small></Tab>
<Tab label='Fourth' hidden><small>Fourth content hidden</small></Tab>
<Tab label='Fifth'><small>Fifth content</small></Tab>
</Tabs>
<h5>Fixed Tabs</h5>
<Tabs index={this.state.fixedIndex} onChange={this.handleFixedTabChange} fixed>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
</Tabs>
<h5>Inverse Tabs</h5>
<Tabs index={this.state.inverseIndex} onChange={this.handleInverseTabChange} inverse>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
<Tab label='Disabled' disabled><small>Disabled Content</small></Tab>
</Tabs>
</section>
);
}
}

View File

@ -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 {
<Tab label='Fourth' hidden><small>Fourth content hidden</small></Tab>
<Tab label='Fifth'><small>Fifth content</small></Tab>
</Tabs>
<h5>Fixed Tabs</h5>
<p>These tabs fill the given space.</p>
<Tabs index={this.state.fixedIndex} onChange={this.handleFixedTabChange} fixed>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
</Tabs>
<h5>Inverse Tabs</h5>
<p>These tabs have an inverted theme.</p>
<Tabs index={this.state.inverseIndex} onChange={this.handleInverseTabChange} inverse>
<Tab label='First'><small>First Content</small></Tab>
<Tab label='Second'><small>Second Content</small></Tab>
<Tab label='Third'><small>Third Content</small></Tab>
<Tab label='Disabled' disabled><small>Disabled Content</small></Tab>
</Tabs>
</section>
);
}