diff --git a/.travis.yml b/.travis.yml index 3115098..3f0af57 100644 --- a/.travis.yml +++ b/.travis.yml @@ -8,3 +8,5 @@ env: - - PREACT_VERSION=5.7.0 - PREACT_VERSION=6.0.2 + - PREACT_VERSION=7.2.1 + - PREACT_VERSION=8.1.0 diff --git a/src/CSSTransitionGroup.js b/src/CSSTransitionGroup.js index 6b4213a..d8fb4e1 100644 --- a/src/CSSTransitionGroup.js +++ b/src/CSSTransitionGroup.js @@ -12,7 +12,7 @@ import { h, cloneElement, Component } from 'preact'; -import { getKey } from './util'; +import { getKey, filterNullChildren } from './util'; import { mergeChildMappings, isShownInChildren, isShownInChildrenByKey, inChildren, inChildrenByKey } from './TransitionChildMapping'; import { CSSTransitionGroupChild } from './CSSTransitionGroupChild'; @@ -42,10 +42,10 @@ export class CSSTransitionGroup extends Component { } componentWillReceiveProps({ children, exclusive, showProp }) { - let nextChildMapping = (children || []).slice(); + let nextChildMapping = filterNullChildren(children || []).slice(); // last props children if exclusive - let prevChildMapping = exclusive ? this.props.children : this.state.children; + let prevChildMapping = filterNullChildren(exclusive ? this.props.children : this.state.children); let newChildren = mergeChildMappings( prevChildMapping, @@ -118,7 +118,7 @@ export class CSSTransitionGroup extends Component { _handleDoneEntering(key) { delete this.currentlyTransitioningKeys[key]; - let currentChildMapping = this.props.children, + let currentChildMapping = filterNullChildren(this.props.children), showProp = this.props.showProp; if (!currentChildMapping || ( !showProp && !inChildrenByKey(currentChildMapping, key) @@ -157,7 +157,7 @@ export class CSSTransitionGroup extends Component { _handleDoneLeaving(key) { delete this.currentlyTransitioningKeys[key]; let showProp = this.props.showProp, - currentChildMapping = this.props.children; + currentChildMapping = filterNullChildren(this.props.children); if (showProp && currentChildMapping && isShownInChildrenByKey(currentChildMapping, key, showProp)) { this.performEnter(key); @@ -200,7 +200,7 @@ export class CSSTransitionGroup extends Component { render({ component:Component, transitionName, transitionEnter, transitionLeave, children:c, ...props }, { children }) { return ( - { children.map(this.renderChild) } + { filterNullChildren(children).map(this.renderChild) } ); } diff --git a/src/util.js b/src/util.js index e581a66..df8943f 100644 --- a/src/util.js +++ b/src/util.js @@ -9,3 +9,7 @@ export function getComponentBase(component) { export function onlyChild(children) { return children && children[0]; } + +export function filterNullChildren(children) { + return children && children.filter(i => i !== null); +} diff --git a/tests/index.js b/tests/index.js index 349b051..2774923 100644 --- a/tests/index.js +++ b/tests/index.js @@ -83,6 +83,40 @@ class SVGList extends Component { } } +class NullChildren extends Component { + + state = { + items: [ + { displayed: true, item: 'hello'}, + { displayed: true, item: 'world'}, + { displayed: false, item: 'click'}, + { displayed: true, item: 'me'} + ] + }; + + toggleDisplay(i) { + let { items } = this.state; + const item = items[i]; + item.displayed = !item.displayed; + this.setState({ items }); + } + + render(_, { items }) { + return ( +
+ + {null} + + { items.map( ({displayed, item}, i) => ( + displayed ? + {item} + : null + )) } + +
+ ); + } +} const Nothing = () => null; @@ -204,3 +238,63 @@ describe('CSSTransitionGroup: SVG', () => { }, 1400); }); }); + +describe('CSSTransitionGroup: NullChildren', () => { + let container = document.createElement('div'), + list, root; + document.body.appendChild(container); + + let $ = s => [].slice.call(container.querySelectorAll(s)); + + beforeEach( () => { + root = render(
, container, root); + root = render(
list=c} />
, container, root); + }); + + afterEach( () => { + list = null; + }); + + it('create works', () => { + expect($('.item')).to.have.length(3); + }); + + it('transitionLeave works', done => { + // this.timeout(5999); + list.toggleDisplay(1); + + // make sure -leave class was added + setTimeout( () => { + expect($('.item')).to.have.length(3); + + expect($('.item')[1].className).to.contain('example-leave'); + expect($('.item')[1].className).to.contain('example-leave-active'); + }, 100); + + // then make sure it's gone + setTimeout( () => { + expect($('.item')).to.have.length(2); + done(); + }, 1400); + }); + + it('transitionEnter works', done => { + // this.timeout(5999); + list.toggleDisplay(2); + + setTimeout( () => { + expect($('.item')).to.have.length(4); + expect($('.item')[2].className).to.contain('example-enter'); + expect($('.item')[2].className).to.contain('example-enter-active'); + }, 500); + + setTimeout( () => { + expect($('.item')).to.have.length(4); + + expect($('.item')[3].className).not.to.contain('example-enter'); + expect($('.item')[3].className).not.to.contain('example-enter-active'); + + done(); + }, 1400); + }); +});