From c837d8db301ffbe669166c7bd07ec0556ec3dfe5 Mon Sep 17 00:00:00 2001 From: Jimmy Jia Date: Mon, 20 Jul 2015 03:12:37 -0400 Subject: [PATCH] [fixed] Only calculate overlay position on display Fixes #1018 --- src/Collapse.js | 72 +++++---- src/Fade.js | 68 ++++---- src/Overlay.js | 96 +++++------ src/Position.js | 93 ++++++----- src/Transition.js | 350 ++++++++++++++++++++--------------------- test/TransitionSpec.js | 52 ++---- 6 files changed, 349 insertions(+), 382 deletions(-) diff --git a/src/Collapse.js b/src/Collapse.js index 505c116085..299ec8a625 100644 --- a/src/Collapse.js +++ b/src/Collapse.js @@ -115,84 +115,82 @@ class Collapse extends React.Component { Collapse.propTypes = { /** - * Collapse the Component in or out. + * Whether the component is entered; triggers the enter or exit animation */ - in: React.PropTypes.bool, + in: React.PropTypes.bool, /** - * Provide the duration of the animation in milliseconds, used to ensure that finishing callbacks are fired even if the - * original browser transition end events are canceled. + * Whether the component should be unmounted (removed from DOM) when exited */ - duration: React.PropTypes.number, + unmountOnExit: React.PropTypes.bool, /** - * Specifies the dimension used when collapsing. - * - * _Note: Bootstrap only partially supports 'width'! - * You will need to supply your own css animation for the `.width` css class._ + * Whether transition in should run when the Transition component mounts, if + * the component is initially entered */ - dimension: React.PropTypes.oneOfType([ - React.PropTypes.oneOf(['height', 'width']), - React.PropTypes.func - ]), + transitionAppear: React.PropTypes.bool, /** - * A function that returns the height or width of the animating DOM node. Allows for providing some custom logic how much - * Collapse component should animate in its specified dimension. - * - * `getDimensionValue` is called with the current dimension prop value and the DOM node. + * Duration of the animation in milliseconds, to ensure that finishing + * callbacks are fired even if the original browser transition end events are + * canceled */ - getDimensionValue: React.PropTypes.func, + duration: React.PropTypes.number, /** - * A Callback fired before the component starts to expand. + * Callback fired before the "entering" classes are applied */ onEnter: React.PropTypes.func, - /** - * A Callback fired immediately after the component starts to expand. + * Callback fired after the "entering" classes are applied */ onEntering: React.PropTypes.func, - /** - * A Callback fired after the component has expanded. + * Callback fired after the "enter" classes are applied */ onEntered: React.PropTypes.func, - /** - * A Callback fired before the component starts to collapse. + * Callback fired before the "exiting" classes are applied */ onExit: React.PropTypes.func, - /** - * A Callback fired immediately after the component starts to collapse. + * Callback fired after the "exiting" classes are applied */ onExiting: React.PropTypes.func, - /** - * A Callback fired after the component has collapsed. + * Callback fired after the "exited" classes are applied */ onExited: React.PropTypes.func, /** - * Specify whether the transitioning component should be unmounted (removed from the DOM) once the exit animation finishes. + * The dimension used when collapsing + * + * _Note: Bootstrap only partially supports 'width'! + * You will need to supply your own CSS animation for the `.width` CSS class._ */ - unmountOnExit: React.PropTypes.bool, + dimension: React.PropTypes.oneOfType([ + React.PropTypes.oneOf(['height', 'width']), + React.PropTypes.func + ]), /** - * Specify whether the component should collapse or expand when it mounts. + * Function that returns the height or width of the animating DOM node + * + * Allows for providing some custom logic for how much the Collapse component + * should animate in its specified dimension. Called with the current + * dimension prop value and the DOM node. */ - transitionAppear: React.PropTypes.bool + getDimensionValue: React.PropTypes.func }; Collapse.defaultProps = { - in: false, + in: false, duration: 300, - dimension: 'height', - transitionAppear: false, unmountOnExit: false, + transitionAppear: false, + + dimension: 'height', getDimensionValue }; export default Collapse; - diff --git a/src/Fade.js b/src/Fade.js index 523508fff5..528e839d85 100644 --- a/src/Fade.js +++ b/src/Fade.js @@ -2,87 +2,77 @@ import React from 'react'; import Transition from './Transition'; class Fade extends React.Component { - - constructor(props, context){ - super(props, context); - } - render() { return ( - { this.props.children } + {this.props.children} ); } } +// Explicitly copied from Transition for doc generation. + Fade.propTypes = { /** - * Fade the Component in or out. + * Whether the component is entered; triggers the enter or exit animation */ - in: React.PropTypes.bool, + in: React.PropTypes.bool, /** - * Provide the duration of the animation in milliseconds, used to ensure that finishing callbacks are fired even if the - * original browser transition end events are canceled. + * Whether the component should be unmounted (removed from DOM) when exited */ - duration: React.PropTypes.number, + unmountOnExit: React.PropTypes.bool, /** - * A Callback fired before the component starts to fade in. + * Whether transition in should run when the Transition component mounts, if + * the component is initially entered */ - onEnter: React.PropTypes.func, + transitionAppear: React.PropTypes.bool, /** - * A Callback fired immediately after the component has started to faded in. + * Duration of the animation in milliseconds, to ensure that finishing + * callbacks are fired even if the original browser transition end events are + * canceled */ - onEntering: React.PropTypes.func, + duration: React.PropTypes.number, /** - * A Callback fired after the component has faded in. + * Callback fired before the "entering" classes are applied */ - onEntered: React.PropTypes.func, - + onEnter: React.PropTypes.func, /** - * A Callback fired before the component starts to fade out. + * Callback fired after the "entering" classes are applied */ - onExit: React.PropTypes.func, - + onEntering: React.PropTypes.func, /** - * A Callback fired immediately after the component has started to faded out. + * Callback fired after the "enter" classes are applied */ - onExiting: React.PropTypes.func, - + onEntered: React.PropTypes.func, /** - * A Callback fired after the component has faded out. + * Callback fired before the "exiting" classes are applied */ - onExited: React.PropTypes.func, - - + onExit: React.PropTypes.func, /** - * Specify whether the transitioning component should be unmounted (removed from the DOM) once the exit animation finishes. + * Callback fired after the "exiting" classes are applied */ - unmountOnExit: React.PropTypes.bool, - + onExiting: React.PropTypes.func, /** - * Specify whether the component should fade in or out when it mounts. + * Callback fired after the "exited" classes are applied */ - transitionAppear: React.PropTypes.bool - + onExited: React.PropTypes.func }; Fade.defaultProps = { - in: false, + in: false, duration: 300, - dimension: 'height', - transitionAppear: false, - unmountOnExit: false + unmountOnExit: false, + transitionAppear: false }; export default Fade; diff --git a/src/Overlay.js b/src/Overlay.js index 8e3e996e5b..0a4932903e 100644 --- a/src/Overlay.js +++ b/src/Overlay.js @@ -7,31 +7,24 @@ import CustomPropTypes from './utils/CustomPropTypes'; import Fade from './Fade'; import classNames from 'classnames'; - class Overlay extends React.Component { - - constructor(props, context){ + constructor(props, context) { super(props, context); - this.state = { exited: false }; + this.state = {exited: !props.show}; this.onHiddenListener = this.handleHidden.bind(this); } componentWillReceiveProps(nextProps) { - let state = {}; - - if ( !nextProps.show && this.props.show ){ - state.exiting = true; - } - if (nextProps.show) { - state = { exited: false, exiting: false }; + this.setState({exited: false}); + } else if (!nextProps.animation) { + // Otherwise let handleHidden take care of marking exited. + this.setState({exited: true}); } - - this.setState(state); } - render(){ + render() { let { container , containerPadding @@ -42,56 +35,63 @@ class Overlay extends React.Component { , animation: Transition , ...props } = this.props; - let child = null; - - if ( Transition === true ){ + if (Transition === true) { Transition = Fade; } - if (props.show || (Transition && this.state.exiting && !this.state.exited)) { + // Don't un-render the overlay while it's transitioning out. + const mountOverlay = props.show || (Transition && !this.state.exited); + + if (!mountOverlay) { + // Don't bother showing anything if we don't have to. + return null; + } - child = children; + let child = children; - // Position the child before the animation to avoid `null` DOM nodes + if (Transition) { + // This animates the child by injecting props, so it must be inner-most. child = ( - - { child } - + + {child} + + ); + } else { + child = cloneElement( + child, + {className: classNames('in', child.className)} ); - - child = Transition - ? ( - - { child } - - ) - : cloneElement(child, { className: classNames('in', child.className) }); - - //Adds a wrapping div so it cannot be before Transition - if (rootClose) { - child = ( - - { child } - - ); - } } + // This must wrap the transition to avoid position recalculations. + child = ( + + {child} + + ); + + // This goes after everything else because it adds a wrapping div. + if (rootClose) { + child = ( + + {child} + + ); + } return ( - { child } + {child} ); } - handleHidden(){ - this.setState({ exited: true, exiting: false }); + handleHidden() { + this.setState({exited: true}); } } diff --git a/src/Position.js b/src/Position.js index 6b3b8ff9a0..cb4408e041 100644 --- a/src/Position.js +++ b/src/Position.js @@ -4,9 +4,9 @@ import { calcOverlayPosition } from './utils/overlayPositionUtils'; import CustomPropTypes from './utils/CustomPropTypes'; class Position extends React.Component { - constructor(props, context){ super(props, context); + this.state = { positionLeft: null, positionTop: null, @@ -15,33 +15,35 @@ class Position extends React.Component { }; } - componentWillMount(){ - this._needsFlush = true; + componentDidMount() { + this.updatePosition(this.props, this.getTargetSafe(this.props)); } - componentWillReceiveProps(){ - this._needsFlush = true; - } + componentWillReceiveProps(nextProps) { + if (nextProps.target !== this.props.target) { + const target = this.getTargetSafe(this.props); + const nextTarget = this.getTargetSafe(nextProps); - componentDidMount(){ - this._maybeUpdatePosition(); - } - componentDidUpdate(){ - this._maybeUpdatePosition(); + if (nextTarget !== target) { + this.updatePosition(nextProps, nextTarget); + } + } } render() { - let { children, ...props } = this.props; - let { positionLeft, positionTop, ...arrows } = this.props.target ? this.state : {}; + const {children, ...props} = this.props; + const {positionLeft, positionTop, ...arrowPosition } = this.state; + const child = React.Children.only(children); return cloneElement( - React.Children.only(children), { + child, + { ...props, - ...arrows, + ...arrowPosition, positionTop, positionLeft, style: { - ...children.props.style, + ...child.props.style, left: positionLeft, top: positionTop } @@ -49,55 +51,62 @@ class Position extends React.Component { ); } - _maybeUpdatePosition(){ - if ( this._needsFlush ) { - this._needsFlush = false; - this._updatePosition(); + getTargetSafe(props) { + if (!props.target) { + return null; } + + return props.target(props); } - _updatePosition() { - if ( this.props.target == null ){ + updatePosition(props, target) { + if (!target) { + this.setState({ + positionLeft: null, + positionTop: null, + arrowOffsetLeft: null, + arrowOffsetTop: null + }); + return; } - let overlay = React.findDOMNode(this); - let target = React.findDOMNode(this.props.target(this.props)); - let container = React.findDOMNode(this.props.container) || domUtils.ownerDocument(this).body; - - this.setState( - calcOverlayPosition( - this.props.placement - , overlay - , target - , container - , this.props.containerPadding)); + const overlay = React.findDOMNode(this); + const container = + React.findDOMNode(props.container) || domUtils.ownerDocument(this).body; + + this.setState(calcOverlayPosition( + props.placement, + overlay, + target, + container, + props.containerPadding + )); } } Position.propTypes = { /** - * The target DOM node the Component is positioned next too. + * Function mapping props to DOM node the component is positioned next to */ - target: React.PropTypes.func, + target: React.PropTypes.func, /** - * The "offsetParent" of the Component + * "offsetParent" of the component */ - container: CustomPropTypes.mountable, + container: CustomPropTypes.mountable, /** - * Distance in pixels the Component should be positioned to the edge of the Container. + * Minimum spacing in pixels between container border and component border */ containerPadding: React.PropTypes.number, /** - * The location that the overlay should be positioned to its target. + * How to position the component relative to the target */ - placement: React.PropTypes.oneOf(['top', 'right', 'bottom', 'left']) + placement: React.PropTypes.oneOf(['top', 'right', 'bottom', 'left']) }; Position.defaultProps = { containerPadding: 0, - placement: 'right' + placement: 'right' }; - export default Position; diff --git a/src/Transition.js b/src/Transition.js index a49124aac7..85eb05c5f6 100644 --- a/src/Transition.js +++ b/src/Transition.js @@ -2,284 +2,274 @@ import React from 'react'; import TransitionEvents from './utils/TransitionEvents'; import classnames from 'classnames'; -function omit(obj, keys) { - let included = Object.keys(obj).filter( k => keys.indexOf(k) === -1); - let newObj = {}; - - included.forEach( key => newObj[key] = obj[key] ); - return newObj; -} - -function ensureTransitionEnd(node, handler, duration){ - let fired = false; - let done = e => { - if (!fired) { - fired = true; - handler(e); - } - }; - - if ( node ) { - TransitionEvents.addEndEventListener(node, done); - setTimeout(done, duration); - } else { - setTimeout(done, 0); - } -} - -// reading a dimension prop will cause the browser to recalculate, -// which will let our animations work -let triggerBrowserReflow = node => node.offsetHeight; //eslint-disable-line no-unused-expressions +const UNMOUNTED = 0; +export const EXITED = 1; +export const ENTERING = 2; +export const ENTERED = 3; +export const EXITING = 4; class Transition extends React.Component { - - constructor(props, context){ + constructor(props, context) { super(props, context); - this.state = { - in: !props.in, - transitioning: false - }; + let initialStatus; + if (props.in) { + // Perform enter in performNextTransition from componentDidMount. + initialStatus = props.transitionAppear ? EXITED : ENTERED; + } else { + initialStatus = props.unmountOnExit ? UNMOUNTED : EXITED; + } + this.state = {status: initialStatus}; - this.needsTransition = true; + this.nextCallback = null; } - componentWillReceiveProps(nextProps) { - if (nextProps.in !== this.props.in) { - this.needsTransition = true; - } + componentDidMount() { + this.performNextTransition(); } - componentDidUpdate() { - this.processChild(); - } + componentWillReceiveProps(nextProps) { + const status = this.state.status; + if (nextProps.in) { + if (status === EXITING) { + this.performEnter(nextProps); + } else if (this.props.unmountOnExit) { + // Perform enter in performNextTransition. + if (status === UNMOUNTED) { + this.setState({status: EXITED}); + } + } else if (status === EXITED) { + this.performEnter(nextProps); + } - componentWillMount() { - this._mounted = true; + // Otherwise we're already entering or entered. + } else { + if (status === ENTERING || status === ENTERED) { + this.performExit(nextProps); + } - if (!this.props.transitionAppear) { - this.needsTransition = false; - this.setState({ in: this.props.in }); + // Otherwise we're already exited or exiting. } } - componentWillUnmount(){ - this._mounted = false; + componentDidUpdate() { + this.performNextTransition(); } - componentDidMount() { - if (this.props.transitionAppear) { - this.processChild(); - } + componentWillUnmount() { + this.cancelNextCallback(); } - processChild(){ - let needsTransition = this.needsTransition; - let enter = this.props.in; - - if (needsTransition) { - this.needsTransition = false; - this[enter ? 'performEnter' : 'performLeave'](); + performNextTransition() { + if (this.state.status === EXITED) { + if (this.props.in) { + // Either because of transitionAppear or unmountOnExit. + this.performEnter(this.props); + } else if (this.props.unmountOnExit) { + this.setState({status: UNMOUNTED}); + } } } - performEnter() { - let maybeNode = React.findDOMNode(this); - - let enter = node => { - node = this.props.transitioningNode(node) || node; - - this.props.onEnter(node); + performEnter(props) { + this.cancelNextCallback(); + const node = React.findDOMNode(this); - this.safeSetState({ in: true, transitioning: true, needInitialRender: false }, ()=> { + // Not this.props, because we might be about to receive new props. + props.onEnter(node); - this.props.onEntering(node); + this.safeSetState({status: ENTERING}, () => { + this.props.onEntering(node); - ensureTransitionEnd(node, () => { - if ( this.state.in ){ - this.safeSetState({ - transitioning: false - }, () => this.props.onEntered(node)); - } - - }, this.props.duration); + this.onTransitionEnd(node, () => { + this.safeSetState({status: ENTERED}, () => { + this.props.onEntered(node); + }); }); - }; - - if (maybeNode) { - enter(maybeNode); - } - else if (this.props.unmountOnExit) { - this._ensureNode(enter); - } + }); } - performLeave() { - let node = React.findDOMNode(this); - - node = this.props.transitioningNode(node) || node; + performExit(props) { + this.cancelNextCallback(); + const node = React.findDOMNode(this); - this.props.onExit(node); + // Not this.props, because we might be about to receive new props. + props.onExit(node); - this.setState({ in: false, transitioning: true }, () => { + this.safeSetState({status: EXITING}, () => { this.props.onExiting(node); - ensureTransitionEnd(node, () => { - if ( !this.state.in ){ - this.safeSetState({ transitioning: false }, ()=> this.props.onExited(node)); - } - }, this.props.duration); + this.onTransitionEnd(node, () => { + this.safeSetState({status: EXITED}, () => { + this.props.onExited(node); + }); + }); }); } - _ensureNode(callback) { - - this.setState({ needInitialRender: true }, ()=> { - let node = React.findDOMNode(this); - - triggerBrowserReflow(node); - - callback(node); - }); + cancelNextCallback() { + if (this.nextCallback !== null) { + this.nextCallback.cancel(); + this.nextCallback = null; + } } - safeSetState(newState, cb){ - if (this._mounted) { - this.setState(newState, cb); - } + safeSetState(nextState, callback) { + // This shouldn't be necessary, but there are weird race conditions with + // setState callbacks and unmounting in testing, so always make sure that + // we can cancel any pending setState callbacks after we unmount. + this.setState(nextState, this.setNextCallback(callback)); } - render() { - let childProps = omit(this.props, Object.keys(Transition.propTypes).concat('children')); + setNextCallback(callback) { + let active = true; - let child = this.props.children; - let starting = this.state.needInitialRender; - let out = !this.state.in && !this.state.transitioning; + this.nextCallback = (event) => { + if (active) { + active = false; + this.nextCallback = null; - if ( !child || (this.props.unmountOnExit && out && !starting) ){ - return null; - } + callback(event); + } + }; - let classes = ''; + this.nextCallback.cancel = () => { + active = false; + }; - // using `classnames()` here causes a subtle bug, - // hence the verbose if/else if sequence. - if (this.state.in && !this.state.transitioning) { - classes = this.props.enteredClassName; - } + return this.nextCallback; + } + + onTransitionEnd(node, handler) { + this.setNextCallback(handler); - else if (this.state.in && this.state.transitioning) { - classes = this.props.enteringClassName; + if (node) { + TransitionEvents.addEndEventListener(node, this.nextCallback); + setTimeout(this.nextCallback, this.props.duration); + } else { + setTimeout(this.nextCallback, 0); } + } - else if (!this.state.in && !this.state.transitioning) { - classes = this.props.exitedClassName; + render() { + const status = this.state.status; + if (status === UNMOUNTED) { + return null; } - else if (!this.state.in && this.state.transitioning) { - classes = this.props.exitingClassName; + const {children, className, style, ...childProps} = this.props; + Object.keys(Transition.propTypes).forEach(key => delete childProps[key]); + + let transitionClassName; + if (status === EXITED) { + transitionClassName = this.props.exitedClassName; + } else if (status === ENTERING) { + transitionClassName = this.props.enteringClassName; + } else if (status === ENTERED) { + transitionClassName = this.props.enteredClassName; + } else if (status === EXITING) { + transitionClassName = this.props.exitingClassName; } - return React.cloneElement(child, { - ...childProps, - className: classnames( - child.props.className, - this.props.className, - classes) - }); + const child = React.Children.only(children); + return React.cloneElement( + child, + { + ...childProps, + className: classnames( + child.props.className, + className, + transitionClassName + ), + style: {...child.props.style, ...style} + } + ); } } Transition.propTypes = { /** - * Triggers the Enter or Exit animation + * Whether the component is entered; triggers the enter or exit animation */ - in: React.PropTypes.bool, + in: React.PropTypes.bool, /** - * Specify whether the transitioning component should be unmounted (removed from the DOM) once the exit animation finishes. + * Whether the component should be unmounted (removed from DOM) when exited */ - unmountOnExit: React.PropTypes.bool, + unmountOnExit: React.PropTypes.bool, /** - * Specify whether transitions should run when the Transition component mounts. + * Whether transition in should run when the Transition component mounts, if + * the component is initially entered */ transitionAppear: React.PropTypes.bool, /** - * Provide the duration of the animation in milliseconds, used to ensure that finishing callbacks are fired even if the - * original browser transition end events are canceled. + * Duration of the animation in milliseconds, to ensure that finishing + * callbacks are fired even if the original browser transition end events are + * canceled */ - duration: React.PropTypes.number, + duration: React.PropTypes.number, /** - * A css class or classes applied once the Component has exited. + * CSS class or classes applied when the component is exited */ - exitedClassName: React.PropTypes.string, + exitedClassName: React.PropTypes.string, /** - * A css class or classes applied while the Component is exiting. + * CSS class or classes applied while the component is exiting */ - exitingClassName: React.PropTypes.string, + exitingClassName: React.PropTypes.string, /** - * A css class or classes applied once the Component has entered. + * CSS class or classes applied when the component is entered */ - enteredClassName: React.PropTypes.string, + enteredClassName: React.PropTypes.string, /** - * A css class or classes applied while the Component is entering. + * CSS class or classes applied while the component is entering */ enteringClassName: React.PropTypes.string, /** - * A function that returns the DOM node to animate. This Node will have the transition classes applied to it. - * When left out, the Component will use its immediate child. - * - * @private - */ - transitioningNode: React.PropTypes.func, - - /** - * A callback fired just before the "entering" classes are applied + * Callback fired before the "entering" classes are applied */ - onEnter: React.PropTypes.func, + onEnter: React.PropTypes.func, /** - * A callback fired just after the "entering" classes are applied + * Callback fired after the "entering" classes are applied */ - onEntering: React.PropTypes.func, + onEntering: React.PropTypes.func, /** - * A callback fired after "enter" classes are applied + * Callback fired after the "enter" classes are applied */ - onEntered: React.PropTypes.func, + onEntered: React.PropTypes.func, /** - * A callback fired after "exiting" classes are applied + * Callback fired before the "exiting" classes are applied */ - onExit: React.PropTypes.func, + onExit: React.PropTypes.func, /** - * A callback fired after "exiting" classes are applied + * Callback fired after the "exiting" classes are applied */ - onExiting: React.PropTypes.func, + onExiting: React.PropTypes.func, /** - * A callback fired after "exit" classes are applied + * Callback fired after the "exited" classes are applied */ - onExited: React.PropTypes.func + onExited: React.PropTypes.func }; -// name the function so it is clearer in the documentation -const noop = ()=>{}; +// Name the function so it is clearer in the documentation +function noop() {} Transition.defaultProps = { - in: false, + in: false, duration: 300, unmountOnExit: false, transitionAppear: false, - transitioningNode: noop, - onEnter: noop, + onEnter: noop, onEntering: noop, - onEntered: noop, + onEntered: noop, - onExit: noop, - onExiting: noop, - onExited: noop + onExit: noop, + onExiting: noop, + onExited: noop }; export default Transition; diff --git a/test/TransitionSpec.js b/test/TransitionSpec.js index 7945ba8ceb..ced505b537 100644 --- a/test/TransitionSpec.js +++ b/test/TransitionSpec.js @@ -1,12 +1,10 @@ import React from 'react'; import ReactTestUtils from 'react/lib/ReactTestUtils'; import { render } from './helpers'; -import Transition from '../src/Transition'; -//import classNames from 'classnames'; +import Transition, {EXITED, ENTERING, ENTERED, EXITING} from + '../src/Transition'; describe('Transition', function () { - - it('should not transition on mount', function(){ let instance = render( { throw new Error('should not Enter'); }}> @@ -14,8 +12,7 @@ describe('Transition', function () { ); - instance.state.in.should.equal(true); - assert.ok(!instance.state.transitioning); + instance.state.status.should.equal(ENTERED); }); it('should transition on mount with transitionAppear', done =>{ @@ -28,8 +25,7 @@ describe('Transition', function () { ); - instance.state.in.should.equal(true); - instance.state.transitioning.should.equal(true); + instance.state.status.should.equal(EXITED); }); describe('entering', ()=> { @@ -51,10 +47,9 @@ describe('Transition', function () { let onEnter = sinon.spy(); let onEntering = sinon.spy(); - instance.state.in.should.equal(false); + instance.state.status.should.equal(EXITED); instance = instance.renderWithProps({ - in: true, onEnter, @@ -73,27 +68,23 @@ describe('Transition', function () { it('should move to each transition state', done => { let count = 0; - instance.state.in.should.equal(false); + instance.state.status.should.equal(EXITED); instance = instance.renderWithProps({ - in: true, onEnter(){ count++; - instance.state.in.should.equal(false); - instance.state.transitioning.should.equal(false); + instance.state.status.should.equal(EXITED); }, onEntering(){ count++; - instance.state.in.should.equal(true); - instance.state.transitioning.should.equal(true); + instance.state.status.should.equal(ENTERING); }, onEntered(){ - instance.state.in.should.equal(true); - instance.state.transitioning.should.equal(false); + instance.state.status.should.equal(ENTERED); assert.ok(count === 2); done(); } @@ -103,10 +94,9 @@ describe('Transition', function () { it('should apply classes at each transition state', done => { let count = 0; - instance.state.in.should.equal(false); + instance.state.status.should.equal(EXITED); instance = instance.renderWithProps({ - in: true, onEnter(node){ @@ -126,10 +116,8 @@ describe('Transition', function () { } }); }); - }); - describe('exiting', ()=> { let instance; @@ -150,10 +138,9 @@ describe('Transition', function () { let onExit = sinon.spy(); let onExiting = sinon.spy(); - instance.state.in.should.equal(true); + instance.state.status.should.equal(ENTERED); instance = instance.renderWithProps({ - in: false, onExit, @@ -172,27 +159,23 @@ describe('Transition', function () { it('should move to each transition state', done => { let count = 0; - instance.state.in.should.equal(true); + instance.state.status.should.equal(ENTERED); instance = instance.renderWithProps({ - in: false, onExit(){ count++; - instance.state.in.should.equal(true); - instance.state.transitioning.should.equal(false); + instance.state.status.should.equal(ENTERED); }, onExiting(){ count++; - instance.state.in.should.equal(false); - instance.state.transitioning.should.equal(true); + instance.state.status.should.equal(EXITING); }, onExited(){ - instance.state.in.should.equal(false); - instance.state.transitioning.should.equal(false); + instance.state.status.should.equal(EXITED); //assert.ok(count === 2); done(); } @@ -202,10 +185,9 @@ describe('Transition', function () { it('should apply classes at each transition state', done => { let count = 0; - instance.state.in.should.equal(true); + instance.state.status.should.equal(ENTERED); instance = instance.renderWithProps({ - in: false, onExit(node){ @@ -225,7 +207,5 @@ describe('Transition', function () { } }); }); - }); - });