Skip to content

Commit

Permalink
Merge pull request #4203 from nathanmarks/stepper-height-fix
Browse files Browse the repository at this point in the history
[Stepper][StepButton] Add more tests and fix an issue with StepButton event handlers
  • Loading branch information
oliviertassinari committed May 22, 2016
2 parents 0870f57 + 873cb4e commit be15233
Show file tree
Hide file tree
Showing 11 changed files with 329 additions and 75 deletions.
11 changes: 8 additions & 3 deletions src/Stepper/StepButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,13 @@ class StepButton extends Component {

state = {
hovered: false,
touch: false,
touched: false,
};

handleMouseEnter = (event) => {
const {onMouseEnter} = this.props;
// Cancel hover styles for touch devices
if (!this.state.touch) {
if (!this.state.touched) {
this.setState({hovered: true});
}
if (typeof onMouseEnter === 'function') {
Expand All @@ -108,7 +108,9 @@ class StepButton extends Component {

handleTouchStart = (event) => {
const {onTouchStart} = this.props;
this.setState({touch: true});
if (!this.state.touched) {
this.setState({touched: true});
}
if (typeof onTouchStart === 'function') {
onTouchStart(event);
}
Expand All @@ -121,6 +123,9 @@ class StepButton extends Component {
completed,
disabled,
icon,
onMouseEnter, // eslint-disable-line no-unused-vars
onMouseLeave, // eslint-disable-line no-unused-vars
onTouchStart, // eslint-disable-line no-unused-vars
style,
...other,
} = this.props;
Expand Down
139 changes: 94 additions & 45 deletions src/Stepper/StepButton.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
import React from 'react';
import {shallow} from 'enzyme';
import {assert} from 'chai';
import sinon from 'sinon';
import {spy} from 'sinon';
import StepButton from './StepButton';
import getMuiTheme from '../styles/getMuiTheme';

Expand All @@ -13,25 +13,25 @@ describe('<StepButton />', () => {
return shallow(node, {context});
};

it('merges user styles in', () => {
it('should merge user styles in', () => {
const wrapper = themedShallow(
<StepButton style={{backgroundColor: 'purple'}}>Step One</StepButton>
);

assert.strictEqual(wrapper.props().style.backgroundColor, 'purple');
});

it('renders an EnhancedButton with a StepLabel', () => {
it('should render an EnhancedButton with a StepLabel', () => {
const wrapper = themedShallow(
<StepButton>Step One</StepButton>
);
assert.ok(wrapper.is('EnhancedButton'));
assert.ok(wrapper.is('EnhancedButton'), 'should be an EnhancedButton');
const stepLabel = wrapper.find('StepLabel');
assert.strictEqual(stepLabel.length, 1);
assert.strictEqual(stepLabel.length, 1, 'should have a stepLabel');
assert.strictEqual(stepLabel.props().children, 'Step One');
});

it('passes props to StepLabel', () => {
it('should pass props to StepLabel', () => {
const wrapper = themedShallow(
<StepButton
active={true}
Expand All @@ -43,54 +43,103 @@ describe('<StepButton />', () => {
</StepButton>
);
const stepLabel = wrapper.find('StepLabel');
assert.strictEqual(stepLabel.prop('active'), true);
assert.strictEqual(stepLabel.prop('completed'), true);
assert.strictEqual(stepLabel.prop('disabled'), true);
assert.strictEqual(stepLabel.prop('active'), true, 'should be active');
assert.strictEqual(stepLabel.prop('completed'), true, 'should be completed');
assert.strictEqual(stepLabel.prop('disabled'), true, 'should be disabled');
});

it('passes props to EnhancedButton', () => {
it('should pass props to EnhancedButton', () => {
const wrapper = themedShallow(
<StepButton disabled={true}>Step One</StepButton>
);
const stepLabel = wrapper.find('EnhancedButton');
assert.strictEqual(stepLabel.prop('disabled'), true);
});

it('bubbles callbacks used internally', () => {
const handleMouseEnter = sinon.spy();
const handleMouseLeave = sinon.spy();
const handleTouchStart = sinon.spy();
const wrapper = themedShallow(
<StepButton
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onTouchStart={handleTouchStart}
>
Step One
</StepButton>
);
wrapper.simulate('mouseEnter');
assert.ok(handleMouseEnter.calledOnce);
wrapper.simulate('mouseLeave');
assert.ok(handleMouseEnter.calledOnce);
assert.ok(handleMouseLeave.calledOnce);
wrapper.simulate('touchStart');
assert.ok(handleMouseEnter.calledOnce);
assert.ok(handleMouseLeave.calledOnce);
assert.ok(handleTouchStart.calledOnce);
wrapper.simulate('mouseEnter');
wrapper.simulate('touchStart');
assert.ok(handleMouseEnter.calledTwice);
assert.ok(handleMouseLeave.calledOnce);
assert.ok(handleTouchStart.calledTwice);
});
describe('event handlers', () => {
describe('handleMouseEnter/Leave', () => {
const handleMouseEnter = spy();
const handleMouseLeave = spy();
const wrapper = themedShallow(
<StepButton
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
Step One
</StepButton>
);

it('sets the EnhancedButton backgroundColor on hover', () => {
const wrapper = themedShallow(
<StepButton>Step One</StepButton>
);
assert.strictEqual(wrapper.prop('style').backgroundColor, muiTheme.stepper.backgroundColor);
wrapper.setState({hovered: true});
assert.strictEqual(wrapper.prop('style').backgroundColor, muiTheme.stepper.hoverBackgroundColor);
it('should set the hovered state', () => {
wrapper.simulate('mouseEnter');
assert.strictEqual(wrapper.state('hovered'), true, 'should be hovered');
assert.strictEqual(handleMouseEnter.callCount, 1, 'should call handleMouseEnter once');
wrapper.simulate('mouseLeave');
assert.strictEqual(wrapper.state('hovered'), false, 'should not be hovered');
assert.strictEqual(handleMouseEnter.callCount, 1, 'should call handleMouseEnter once');
assert.strictEqual(handleMouseLeave.callCount, 1, 'should call handleMouseLeave once');
});

it('should set the EnhancedButton backgroundColor on hover', () => {
wrapper.setState({hovered: false});
assert.strictEqual(wrapper.prop('style').backgroundColor, muiTheme.stepper.backgroundColor);
wrapper.setState({hovered: true});
assert.strictEqual(wrapper.prop('style').backgroundColor, muiTheme.stepper.hoverBackgroundColor);
});
});

describe('handleTouchStart', () => {
const handleTouchStart = spy();
const handleMouseEnter = spy();
const wrapper = themedShallow(
<StepButton
onTouchStart={handleTouchStart}
onMouseEnter={handleMouseEnter}
>
Step One
</StepButton>
);

it('should set the touched state', () => {
assert.strictEqual(wrapper.state('touched'), false, 'should not be touched');
wrapper.simulate('touchStart');
assert.strictEqual(wrapper.state('touched'), true, 'should be touched');
assert.strictEqual(handleTouchStart.callCount, 1, 'should call handleTouchStart once');
});

it('should not set the hovered state with touch set', () => {
wrapper.simulate('mouseEnter');
assert.strictEqual(wrapper.state('hovered'), false, 'should not be hovered');
assert.strictEqual(handleMouseEnter.callCount, 1, 'should call handleMouseEnter once');
});
});

it('should bubble callbacks used internally', () => {
const handleMouseEnter = spy();
const handleMouseLeave = spy();
const handleTouchStart = spy();
const wrapper = themedShallow(
<StepButton
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
onTouchStart={handleTouchStart}
>
Step One
</StepButton>
);
wrapper.simulate('mouseEnter');
assert.strictEqual(handleMouseEnter.callCount, 1, 'should call handleMouseEnter once');
wrapper.simulate('mouseLeave');
assert.strictEqual(handleMouseEnter.callCount, 1, 'should call handleMouseEnter once');
assert.strictEqual(handleMouseLeave.callCount, 1, 'should call handleMouseLeave once');
wrapper.simulate('touchStart');
assert.strictEqual(handleMouseEnter.callCount, 1, 'should call handleMouseEnter once');
assert.strictEqual(handleMouseLeave.callCount, 1, 'should call handleMouseLeave once');
assert.strictEqual(handleTouchStart.callCount, 1, 'should call handleTouchStart once');
wrapper.simulate('mouseEnter');
wrapper.simulate('touchStart');
assert.strictEqual(handleMouseEnter.callCount, 2, 'should call handleMouseEnter twice');
assert.strictEqual(handleMouseLeave.callCount, 1, 'should call handleMouseLeave once');
assert.strictEqual(handleTouchStart.callCount, 2, 'should call handleTouchStart twice');
});
});
});
30 changes: 26 additions & 4 deletions src/Stepper/StepContent.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import React, {Component, PropTypes} from 'react';
import ExpandTransition from '../internal/ExpandTransition';
import TransitionComponent from '../internal/ExpandTransition';
import warning from 'warning';

function ExpandTransition(props) {
return <TransitionComponent {...props} />;
}

const getStyles = (props, context) => {
const styles = {
root: {
Expand Down Expand Up @@ -38,6 +42,19 @@ class StepContent extends Component {
* Override the inline-style of the root element.
*/
style: PropTypes.object,
/**
* ReactTransitionGroup component.
*/
transition: PropTypes.func,
/**
* Adjust the duration of the content expand transition. Passed as a prop to the transition component.
*/
transitionDuration: PropTypes.number,
};

static defaultProps = {
transition: ExpandTransition,
transitionDuration: 450,
};

static contextTypes = {
Expand All @@ -51,6 +68,8 @@ class StepContent extends Component {
children,
last, // eslint-disable-line no-unused-vars
style,
transition,
transitionDuration,
...other,
} = this.props;
const {stepper, muiTheme: {prepareStyles}} = this.context;
Expand All @@ -61,12 +80,15 @@ class StepContent extends Component {
}

const styles = getStyles(this.props, this.context);
const transitionProps = {
enterDelay: transitionDuration,
transitionDuration: transitionDuration,
open: active,
};

return (
<div style={prepareStyles(Object.assign(styles.root, style))} {...other}>
<ExpandTransition enterDelay={450} open={active}>
<div style={{overflow: 'hidden'}}>{children}</div>
</ExpandTransition>
{React.createElement(transition, transitionProps, <div style={{overflow: 'hidden'}}>{children}</div>)}
</div>
);
}
Expand Down
1 change: 0 additions & 1 deletion src/Stepper/Stepper.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ class Stepper extends Component {

static defaultProps = {
orientation: 'horizontal',
activeStep: 0,
linear: true,
};

Expand Down
20 changes: 13 additions & 7 deletions src/internal/ExpandTransition.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@ class ExpandTransition extends Component {
loading: PropTypes.bool,
open: PropTypes.bool,
style: PropTypes.object,
transitionDelay: PropTypes.number,
transitionDuration: PropTypes.number,
};

static defaultProps = {
enterDelay: 0,
transitionDelay: 0,
transitionDuration: 450,
loading: false,
open: false,
};
Expand All @@ -21,15 +25,14 @@ class ExpandTransition extends Component {
muiTheme: PropTypes.object.isRequired,
};

renderChildren(children, loading) {
if (loading) {
return ([]);
}

renderChildren(children) {
const {enterDelay, transitionDelay, transitionDuration} = this.props;
return React.Children.map(children, (child) => {
return (
<ExpandTransitionChild
enterDelay={this.props.enterDelay}
enterDelay={enterDelay}
transitionDelay={transitionDelay}
transitionDuration={transitionDuration}
key={child.key}
>
{child}
Expand All @@ -41,9 +44,12 @@ class ExpandTransition extends Component {
render() {
const {
children,
enterDelay, // eslint-disable-line no-unused-vars
loading,
open,
style,
transitionDelay, // eslint-disable-line no-unused-vars
transitionDuration, // eslint-disable-line no-unused-vars
...other,
} = this.props;

Expand All @@ -55,7 +61,7 @@ class ExpandTransition extends Component {
height: '100%',
}, style);

const newChildren = this.renderChildren(children, loading);
const newChildren = loading ? [] : this.renderChildren(children);

return (
<ReactTransitionGroup
Expand Down
Loading

0 comments on commit be15233

Please sign in to comment.