diff --git a/src/CSSTransition.js b/src/CSSTransition.js index 8a3fd10b..74ac8ef6 100644 --- a/src/CSSTransition.js +++ b/src/CSSTransition.js @@ -14,11 +14,11 @@ const propTypes = { ...Transition.propTypes, /** - * The animation classNames applied to the component as it enters or exits. + * The animation classNames applied to the component as it enters, exits or has finished the transition. * A single name can be provided and it will be suffixed for each stage: e.g. * - * `classNames="fade"` applies `fade-enter`, `fade-enter-active`, - * `fade-exit`, `fade-exit-active`, `fade-appear`, and `fade-appear-active`. + * `classNames="fade"` applies `fade-enter`, `fade-enter-active`, `fade-enter-done`, + * `fade-exit`, `fade-exit-active`, `fade-exit-done`, `fade-appear`, and `fade-appear-active`. * Each individual classNames can also be specified independently like: * * ```js @@ -27,8 +27,10 @@ const propTypes = { * appearActive: 'my-active-appear', * enter: 'my-enter', * enterActive: 'my-active-enter', + * enterDone: 'my-done-enter, * exit: 'my-exit', * exitActive: 'my-active-exit', + * exitDone: 'my-done-exit, * }} * ``` * @@ -37,8 +39,10 @@ const propTypes = { * appearActive?: string, * enter?: string, * enterActive?: string, + * enterDone?: string, * exit?: string, * exitActive?: string, + * exitDone?: string, * }} */ classNames: classNamesShape, @@ -61,7 +65,7 @@ const propTypes = { /** * A `` callback fired immediately after the 'enter' or - * 'appear' classes are **removed** from the DOM node. + * 'appear' classes are **removed** and the `done` class is added to the DOM node. * * @type Function(node: HtmlElement, isAppearing: bool) */ @@ -85,7 +89,7 @@ const propTypes = { /** * A `` callback fired immediately after the 'exit' classes - * are **removed** from the DOM node. + * are **removed** and the `exit-done` class is added to the DOM node. * * @type Function(node: HtmlElement) */ @@ -98,7 +102,8 @@ const propTypes = { * * `CSSTransition` applies a pair of class names during the `appear`, `enter`, * and `exit` stages of the transition. The first class is applied and then a - * second "active" class in order to activate the css animation. + * second "active" class in order to activate the css animation. After the animation, + * matching `done` class names are applied to persist the animation state. * * When the `in` prop is toggled to `true` the Component will get * the `example-enter` CSS class and the `example-enter-active` CSS class @@ -131,7 +136,10 @@ class CSSTransition extends React.Component { } onEntered = (node, appearing) => { + const { doneClassName } = this.getClassNames('enter'); + this.removeClasses(node, appearing ? 'appear' : 'enter'); + addClass(node, doneClassName); if (this.props.onEntered) { this.props.onEntered(node) @@ -161,7 +169,10 @@ class CSSTransition extends React.Component { } onExited = (node) => { + const { doneClassName } = this.getClassNames('exit'); + this.removeClasses(node, 'exit'); + addClass(node, doneClassName); if (this.props.onExited) { this.props.onExited(node) @@ -169,7 +180,7 @@ class CSSTransition extends React.Component { } getClassNames = (type) => { - const { classNames } = this.props + const { classNames } = this.props; let className = typeof classNames !== 'string' ? classNames[type] : classNames + '-' + type; @@ -177,13 +188,21 @@ class CSSTransition extends React.Component { let activeClassName = typeof classNames !== 'string' ? classNames[type + 'Active'] : className + '-active'; - return { className, activeClassName } + let doneClassName = typeof classNames !== 'string' ? + classNames[type + 'Done'] : className + '-done'; + + return { + className, + activeClassName, + doneClassName + }; } removeClasses(node, type) { - const { className, activeClassName } = this.getClassNames(type) + const { className, activeClassName, doneClassName } = this.getClassNames(type) className && removeClass(node, className); activeClassName && removeClass(node, activeClassName); + doneClassName && removeClass(node, doneClassName); } reflowAndAddClass(node, className) { diff --git a/src/utils/PropTypes.js b/src/utils/PropTypes.js index ce528d85..af818dd7 100644 --- a/src/utils/PropTypes.js +++ b/src/utils/PropTypes.js @@ -44,8 +44,10 @@ export const classNamesShape = PropTypes.oneOfType([ }), PropTypes.shape({ enter: PropTypes.string, + enterDone: PropTypes.string, enterActive: PropTypes.string, exit: PropTypes.string, + exitDone: PropTypes.string, exitActive: PropTypes.string, }), ]); diff --git a/test/CSSTransition-test.js b/test/CSSTransition-test.js index 28309848..6d4a1e40 100644 --- a/test/CSSTransition-test.js +++ b/test/CSSTransition-test.js @@ -69,7 +69,7 @@ describe('CSSTransition', () => { }, onEntered(node){ - expect(node.className).toEqual(''); + expect(node.className).toEqual('test-enter-done'); expect(count).toEqual(2); done(); } @@ -84,6 +84,7 @@ describe('CSSTransition', () => { classNames={{ enter: 'custom', enterActive: 'custom-super-active', + enterDone: 'custom-super-done', }} >
@@ -104,7 +105,7 @@ describe('CSSTransition', () => { }, onEntered(node){ - expect(node.className).toEqual(''); + expect(node.className).toEqual('custom-super-done'); expect(count).toEqual(2); done(); } @@ -144,7 +145,7 @@ describe('CSSTransition', () => { }, onExited(node){ - expect(node.className).toEqual(''); + expect(node.className).toEqual('test-exit-done'); expect(count).toEqual(2); done(); } @@ -160,6 +161,7 @@ describe('CSSTransition', () => { classNames={{ exit: 'custom', exitActive: 'custom-super-active', + exitDone: 'custom-super-done', }} >
@@ -180,7 +182,7 @@ describe('CSSTransition', () => { }, onExited(node){ - expect(node.className).toEqual(''); + expect(node.className).toEqual('custom-super-done'); expect(count).toEqual(2); done(); }