Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Controllability to Tabs Component #1232

Merged
merged 2 commits into from
Jul 21, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
235 changes: 154 additions & 81 deletions docs/src/app/components/pages/components/tabs.jsx
Original file line number Diff line number Diff line change
@@ -1,76 +1,69 @@
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);
}

getStyles() {
return {
headline: {
fontSize: '24px',
lineHeight: '32px',
paddingTop: '16px',
marginBottom: '12px',
letterSpacing: '0',
fontWeight: Typography.fontWeightNormal,
color: Typography.textDarkBlack
}
}
this._handleTabActive = this._handleTabActive.bind(this);
this.state = {tabsValue: 'a'};
}

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 = `
//Uncontrolled Tabs
<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">
home
</IconButton>

//Controlled Tabs using valueLink (passing value and onChange props works too!)
<Tabs
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>

`;

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 @@ -96,27 +89,21 @@ class TabsPage extends React.Component {
desc: 'Override the inline-styles of the tab-labels container.'
},
{
name: 'contentContainerStyle',
type: 'object',
name: 'value',
type: 'string or number',
header: 'optional',
desc: 'Override the inline-styles of the content\'s container.'
desc: 'Makes Tabs controllable and selects the tab whose value prop matches this prop.'
},
{
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: 'Tabs Events',
infoArray: [
{
name: 'onChange',
type: 'function(tabIndex, tab)',
type: 'function(e, 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 @@ -130,10 +117,11 @@ class TabsPage extends React.Component {
desc: 'Sets the text value of the tab item to the string specified.'
},
{
name: 'route',
name: 'value',
type: 'string',
header: 'optional',
desc: 'Specifies a router RouteName if included.'
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,18 +138,57 @@ class TabsPage extends React.Component {
}
];

let padding = 400;

let styles = {
contentContainerStyle: {
marginLeft: -padding,
},
div: {
position: 'absolute',
left: 48,
backgroundColor: Colors.cyan500,
width: padding,
height: 48,
},
headline: {
fontSize: 24,
lineHeight: '32px',
paddingTop: 16,
marginBottom: 12,
letterSpacing: 0,
fontWeight: Typography.fontWeightNormal,
color: Typography.textDarkBlack,
},
iconButton: {
position: 'absolute',
left: 0,
backgroundColor: Colors.cyan500,
color: 'white',
marginRight: padding,
},
iconStyle: {
color: Colors.white,
},
tabs: {
position: 'relative',
},
tabsContainer: {
position: 'relative',
paddingLeft: padding,
},
};

return (
<ComponentDoc
name="Tabs"
code={code}
desc={desc}
componentInfo={componentInfo}>

<div>
<Tabs onChange={this._onChange}>
<Tab label='Item One' >
<Tabs>
<Tab label="Item One" >
<div>
<h2 style={this.getStyles().headline}>Tab One Template Example</h2>
<h2 style={styles.headline}>Tab One Template Example</h2>
<p>
This is an example of a tab template!
</p>
Expand All @@ -171,9 +198,9 @@ class TabsPage extends React.Component {
<Slider name="slider0" defaultValue={0.5} />
</div>
</Tab>
<Tab label='Item Two' >
<Tab label="Item Two" >
<div>
<h2 style={this.getStyles().headline}>Tab Two Template Example</h2>
<h2 style={styles.headline}>Tab Two Template Example</h2>
<p>
This is another example of a tab template!
</p>
Expand All @@ -183,19 +210,65 @@ class TabsPage extends React.Component {
</div>
</Tab>
<Tab
label='Item Three'
route='home'
onActive={this._onActive} />
label="Item Three"
route="home"
onActive={this._handleTabActive} />
</Tabs>
</div>

<div style={styles.tabsContainer}>
<IconButton
onClick={this._handleButtonClick.bind(this)}
iconClassName="material-icons"
style={styles.iconButton}
iconStyle={styles.iconStyle}>
home
</IconButton>
<div style={styles.div}/>
<Tabs
valueLink={{value: this.state.tabsValue, requestChange: this._handleTabsChange.bind(this)}}
style={styles.tabs}
contentContainerStyle={styles.contentContainerStyle}>
<Tab label="Tab A" value="a" >
<div>
<h2 style={styles.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={styles.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(e, tab){
this.setState({tabsValue: tab.props.value});
}
}

TabsPage.contextTypes = {
Expand Down
1 change: 0 additions & 1 deletion src/circular-progress.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,6 @@ let CircularProgress = React.createClass({
margin: "5px",
display: "inline-block",
transition: Transitions.create("transform", "20s", null, "linear"),
//webkitTransitionTimingFunction: "linear",
},
svg: {
height: size,
Expand Down
38 changes: 28 additions & 10 deletions src/tabs/tab.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,40 +13,58 @@ 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() {
let {
label,
selected,
style,
value,
width,
...other,
} = this.props;
let styles = this.mergeAndPrefix({
display: 'table-cell',
cursor: 'pointer',
textAlign: 'center',
verticalAlign: 'middle',
height: 48,
color: Colors.white,
opacity: 0.6,
opacity: selected ? 1 : 0.6,
outline: 'none',
fontSize: 14,
fontWeight: '500',
fontWeight: 500,
whiteSpace: 'initial',
fontFamily: this.context.muiTheme.contentFontFamily,
boxSizing: 'border-box',
width: this.props.width,
}, this.props.style);

if (this.props.selected) styles.opacity = '1';
width: width,
}, style);

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

_handleTouchTap(e) {
this.props.handleTouchTap(e, this);
},

});

module.exports = Tab;
Loading