Skip to content

Commit

Permalink
Added Controlled Tabs component
Browse files Browse the repository at this point in the history
  • Loading branch information
Zadielerick committed Jul 20, 2015
1 parent b21bbff commit 7a85b67
Show file tree
Hide file tree
Showing 3 changed files with 220 additions and 87 deletions.
194 changes: 134 additions & 60 deletions docs/src/app/components/pages/components/tabs.jsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,100 @@
let React = require('react');
let CodeExample = require('../../code-example/code-example');
let { Slider, Styles, Tab, Tabs } = require('material-ui');
let {IconButton, Slider, Styles, Tab, Tabs } = require('material-ui');
let ComponentDoc = require('../../component-doc');

let { Typography } = Styles;
let { Colors, Typography } = Styles;


class TabsPage extends React.Component {

constructor() {
super();
this._onActive = this._onActive.bind(this);
this._handleTabActive = this._handleTabActive.bind(this);
this.state = {tabsValue: 'a'};
}

getStyles() {
return {
headline: {
fontSize: '24px',
fontSize: 24,
lineHeight: '32px',
paddingTop: '16px',
marginBottom: '12px',
letterSpacing: '0',
paddingTop: 16,
marginBottom: 12,
letterSpacing: 0,
fontWeight: Typography.fontWeightNormal,
color: Typography.textDarkBlack
}
}
}

render(){
let code = '<Tabs> \n' +
' <Tab label="Item One" > \n' +
' <div> \n' +
' <h2 style={this.getStyles().headline}>Tab One Template Example</h2> \n' +
' <p> \n' +
' This is an example of a tab template! \n' +
' </p> \n' +
' <p> \n' +
' You can put any sort of HTML or react component in here. \n' +
' </p> \n' +
' </div> \n' +
' </Tab> \n' +
' <Tab label="Item Two" > \n' +
' <div> \n' +
' <h2 style={this.getStyles().headline}>Tab Two Template Example</h2> \n' +
' <p> \n' +
' This is another example of a tab template! \n' +
' </p> \n' +
' <p> \n' +
' Fair warning - the next tab routes to home! \n' +
' </p> \n' +
' </div> \n' +
' </Tab> \n' +
' <Tab \n' +
' label="Item Three" \n' +
' route="home" \n' +
' onActive={this._onActive} /> \n' +
'</Tabs> \n' +
'\n' +
'_onActive(tab){ \n' +
' this.context.router.transitionTo(tab.props.route); \n' +
'}';

let desc = 'Refs cannot be set on individual Tab components as cloneWithProps is being ' +
'used to extend the individual tab components under the hood. However, ' +
'refs can be passed to the Tabs container and to any element or component within the template. ' +
'If you need to access a tab directly - you can do so with the first argument of onActive or ' +
'by accessing the props.children array by passing refs to the Tabs container.';
let code = `
constructor() {
this._handleTabActive = this._handleTabActive.bind(this);
this.state = {tabsValue: 'a'};
}
<Tabs> //Uncontrolled Tabs
<Tab label="Item One" >
(Tab content...)
</Tab>
<Tab label="Item Two" >
(Tab content...)
</Tab>
<Tab
label="Item Three"
route="home"
onActive={this._handleTabActive} />
</Tabs>
<IconButton
onClick={this._handleButtonClick.bind(this)}
iconClassName="material-icons"
style={{position: 'absolute', left: '90%'}}>home
</IconButton>
<Tabs //Controlled Tabs using valueLink (passing value and onChange props works too!)
inkBarStyle={{marginLeft: marginLeft}}
tabItemContainerStyle={{width: '75%', marginLeft: marginLeft}}
valueLink={{value: this.state.tabsValue,
requestChange: this._handleTabsChange.bind(this)}}>
<Tab label='Tab A' value='a' >
(Tab content...)
</Tab>
<Tab label='Tab B' value='b'>
(Tab content...)
</Tab>
</Tabs>
_handleTabActive(tab){
this.context.router.transitionTo(tab.props.route);
}
_handleButtonClick() {
this.setState({tabsValue: 'c'});
}
_handleTabsChange(tabIndex, tab, value){
this.setState({tabsValue: value});
}
`;

let desc = 'Tabs can now operate in two different modes: controlled and uncontrolled. ' +
'The uncontrolled mode takes over automatically if no value prop is passed to your' +
'Tabs and Tab components. If you want controllable tabs, passing a value to both the' +
' Tabs and Tab elements will let you programmatically adjust which one is selected. ' +
'ValueLink is now supported by Tabs.';

let componentInfo = [
{
name: 'Tabs Props',
infoArray: [
{
name: 'contentContainerStyle',
type: 'object',
header: 'optional',
desc: 'Override the inline-styles of the content\'s container.'
},
{
name: 'initialSelectedIndex',
type: 'number',
Expand All @@ -95,18 +119,18 @@ class TabsPage extends React.Component {
header: 'optional',
desc: 'Override the inline-styles of the tab-labels container.'
},
{
name: 'contentContainerStyle',
type: 'object',
header: 'optional',
desc: 'Override the inline-styles of the content\'s container.'
},
{
name: 'tabWidth',
type: 'number',
header: 'optional',
desc: 'Specifiy tabWidth to set each tab to a set number of pixels. Tab Width is set by default to an even distribution of the parent Tabs container. If tabWidth is set but the total width of all tabs is greater than the container, tabWidth will revert back to default'
}
},
{
name: 'value',
type: 'string or number',
header: 'optional',
desc: 'Makes Tabs controllable and selects the tab whose value prop matches this prop.'
},
]
},
{
Expand All @@ -116,7 +140,7 @@ class TabsPage extends React.Component {
name: 'onChange',
type: 'function(tabIndex, tab)',
header: 'optional',
desc: 'Fired on touch or tap of a tab.'
desc: 'Fired on touch or tap of a tab. Passes the index and the tab element.'
}
]
},
Expand All @@ -134,6 +158,13 @@ class TabsPage extends React.Component {
type: 'string',
header: 'optional',
desc: 'Specifies a router RouteName if included.'
},
{
name: 'value',
type: 'string',
header: 'optional',
desc: 'If value prop passed to Tabs component, this value prop is also required. It assigns a value ' +
'to the tab so that it can be selected by the Tabs.'
}
]
},
Expand All @@ -150,15 +181,14 @@ class TabsPage extends React.Component {
}
];

let marginLeft = 0;
return (
<ComponentDoc
name="Tabs"
code={code}
desc={desc}
componentInfo={componentInfo}>

<div>
<Tabs onChange={this._onChange}>
<Tabs>
<Tab label='Item One' >
<div>
<h2 style={this.getStyles().headline}>Tab One Template Example</h2>
Expand All @@ -185,17 +215,61 @@ class TabsPage extends React.Component {
<Tab
label='Item Three'
route='home'
onActive={this._onActive} />
onActive={this._handleTabActive} />
</Tabs>
</div>

<div style={{width: '100%', position: 'relative'}}>
<IconButton
onClick={this._handleButtonClick.bind(this)}
iconClassName="material-icons"
style={{position: 'absolute', left: '90%'}}>home
</IconButton>

<Tabs
inkBarStyle={{marginLeft: marginLeft}}
tabItemContainerStyle={{width: '75%', marginLeft: marginLeft}}
valueLink={{value: this.state.tabsValue, requestChange: this._handleTabsChange.bind(this)}}>
<Tab label='Tab A' value='a' >
<div>
<h2 style={this.getStyles().headline}>Controllable Tab Examples</h2>
<p>
Tabs are also controllable if you want to programmatically pass them their values.
This allows for more functionality in Tabs such as not
having any Tab selected or assigning them different values.
</p>
<p>(The home Icon Button will unselect all the tabs and hide their content.)</p>
</div>
</Tab>
<Tab label='Tab B' value='b'>
<div>
<h2 style={this.getStyles().headline}>Controllable Tab B</h2>
<p>
This is another example of a controllable tab. Remember, if you
use controllable Tabs, you need to give all of your tabs values or else
you wont be able to select them.
</p>
<p>
To see one use for controlled Tabs, press the home button on the right.
</p>
</div>
</Tab>
</Tabs>
</div>
</ComponentDoc>
);
}

_onActive(tab){
_handleButtonClick() {
this.setState({tabsValue: 'c'});
}

_handleTabActive(tab){
this.context.router.transitionTo(tab.props.route);
}

_handleTabsChange(tabIndex, tab, value){
this.setState({tabsValue: value});
}
}

TabsPage.contextTypes = {
Expand Down
16 changes: 12 additions & 4 deletions src/tabs/tab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,16 @@ let Tab = React.createClass({

propTypes: {
handleTouchTap: React.PropTypes.func,
onActive: React.PropTypes.func,
selected: React.PropTypes.bool,
width: React.PropTypes.string,
value: React.PropTypes.string,
},

handleTouchTap() {
this.props.handleTouchTap(this.props.tabIndex, this);
getDefaultProps(){
return {
onActive: () => {},
};
},

render() {
Expand All @@ -31,7 +35,7 @@ let Tab = React.createClass({
color: Colors.white,
opacity: 0.6,
fontSize: 14,
fontWeight: '500',
fontWeight: 500,
whiteSpace: 'initial',
fontFamily: this.context.muiTheme.contentFontFamily,
boxSizing: 'border-box',
Expand All @@ -41,12 +45,16 @@ let Tab = React.createClass({
if (this.props.selected) styles.opacity = '1';

return (
<div style={styles} onTouchTap={this.handleTouchTap} routeName={this.props.route}>
<div style={styles} onTouchTap={this._handleTouchTap} routeName={this.props.route}>
{this.props.label}
</div>
);
},

_handleTouchTap() {
this.props.handleTouchTap(this.props.tabIndex, this, this.props.value);
},

});

module.exports = Tab;
Loading

0 comments on commit 7a85b67

Please sign in to comment.