basic transition tests

This commit is contained in:
Evan You 2016-05-31 19:09:57 -04:00
parent 9c2ca07cc6
commit 74538b4529
5 changed files with 101 additions and 50 deletions

View File

@ -1,26 +1,8 @@
/* @flow */
import { isIE9, namespaceMap } from 'web/util/index'
const svgNS = namespaceMap.svg
/**
* In IE9, setAttribute('class') will result in empty class
* if the element also has the :class attribute; However in
* PhantomJS, setting `className` does not work on SVG elements...
* So we have to do a conditional check here.
*/
export function setClass (el: Element, cls: string) {
/* istanbul ignore else */
if (!isIE9 || el.namespaceURI === svgNS) {
el.setAttribute('class', cls)
} else {
el.className = cls
}
}
/**
* Add class with compatibility for IE & SVG
* Add class with compatibility for SVG since classList is not supported on
* SVG elements in IE
*/
export function addClass (el: Element, cls: string) {
if (el.classList) {
@ -30,15 +12,16 @@ export function addClass (el: Element, cls: string) {
el.classList.add(cls)
}
} else {
const cur = ' ' + getClass(el) + ' '
const cur = ' ' + el.getAttribute('class') + ' '
if (cur.indexOf(' ' + cls + ' ') < 0) {
setClass(el, (cur + cls).trim())
el.setAttribute('class', (cur + cls).trim())
}
}
}
/**
* Remove class with compatibility for IE & SVG
* Remove class with compatibility for SVG since classList is not supported on
* SVG elements in IE
*/
export function removeClass (el: Element, cls: string) {
if (el.classList) {
@ -48,27 +31,11 @@ export function removeClass (el: Element, cls: string) {
el.classList.remove(cls)
}
} else {
let cur = ' ' + getClass(el) + ' '
let cur = ' ' + el.getAttribute('class') + ' '
const tar = ' ' + cls + ' '
while (cur.indexOf(tar) >= 0) {
cur = cur.replace(tar, ' ')
}
setClass(el, cur.trim())
}
if (!el.className) {
el.removeAttribute('class')
el.setAttribute('class', cur.trim())
}
}
/**
* For IE9 compat: when both class and :class are present
* getAttribute('class') returns wrong value... but className
* on SVG elements returns an object.
*/
function getClass (el: Element): string {
let classname = el.className
if (typeof classname === 'object') {
classname = classname.baseVal || ''
}
return classname
}

View File

@ -1,6 +1,5 @@
/* @flow */
import { setClass } from '../class-util'
import { genClassForVnode, concat, stringifyClass } from 'web/util/index'
function updateClass (oldVnode: any, vnode: any) {
@ -20,7 +19,7 @@ function updateClass (oldVnode: any, vnode: any) {
// set the class
if (cls !== el._prevClass) {
setClass(el, cls)
el.setAttribute('class', cls)
el._prevClass = cls
}
}

View File

@ -10,11 +10,12 @@ const TRANSITION = 'transition'
const ANIMATION = 'animation'
// Transition property/event sniffing
let transitionProp = 'transition'
let transitionEndEvent = 'transitionend'
let animationProp = 'animation'
let animationEndEvent = 'animationend'
export let transitionProp = 'transition'
export let transitionEndEvent = 'transitionend'
export let animationProp = 'animation'
export let animationEndEvent = 'animationend'
if (hasTransition) {
/* istanbul ignore if */
if (window.ontransitionend === undefined &&
window.onwebkittransitionend !== undefined) {
transitionProp = 'WebkitTransition'
@ -28,7 +29,7 @@ if (hasTransition) {
}
const raf = (inBrowser && window.requestAnimationFrame) || setTimeout
function nextFrame (fn) {
export function nextFrame (fn: Function) {
raf(() => {
raf(fn)
})

View File

@ -20,7 +20,7 @@ window.waitForUpdate = initialCb => {
if (queue.length) {
let hasError = false
try {
job()
job.wait ? job(shift) : job()
} catch (e) {
hasError = true
const done = queue[queue.length - 1]
@ -28,7 +28,7 @@ window.waitForUpdate = initialCb => {
done.fail(e)
}
}
if (!hasError) {
if (!hasError && !job.wait) {
if (queue.length) {
Vue.nextTick(shift)
}
@ -49,6 +49,11 @@ window.waitForUpdate = initialCb => {
then: nextCb => {
queue.push(nextCb)
return chainer
},
thenWaitFor: (wait) => {
wait.wait = true
queue.push(wait)
return chainer
}
}

View File

@ -0,0 +1,79 @@
import Vue from 'vue'
import { isIE9 } from 'web/util/index'
import { nextFrame } from 'web/runtime/modules/transition'
if (!isIE9) {
describe('Transition CSS', () => {
const duration = 50
insertCSS(`
.test {
-webkit-transition: opacity ${duration}ms ease;
transition: opacity ${duration}ms ease;
}
.test-enter, .test-leave-active {
opacity: 0;
}
.test-anim-enter {
animation: test-enter ${duration}ms;
-webkit-animation: test-enter ${duration}ms;
}
.test-anim-leave {
animation: test-leave ${duration}ms;
-webkit-animation: test-leave ${duration}ms;
}
@keyframes test-enter {
from { opacity: 0 }
to { opacity: 1 }
}
@-webkit-keyframes test-enter {
from { opacity: 0 }
to { opacity: 1 }
}
@keyframes test-leave {
from { opacity: 1 }
to { opacity: 0 }
}
@-webkit-keyframes test-leave {
from { opacity: 1 }
to { opacity: 0 }
}
`)
it('basic transitions', done => {
const el = document.createElement('div')
document.body.appendChild(el)
const vm = new Vue({
template: '<div><div v-if="ok" class="test" transition="test">foo</div></div>',
data: { ok: true }
}).$mount(el)
// should not apply transition on initial render by default
expect(vm.$el.innerHTML).toBe('<div class="test">foo</div>')
vm.ok = false
waitForUpdate(() => {
expect(vm.$el.children[0].className).toBe('test test-leave')
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.children[0].className).toBe('test test-leave-active')
}).thenWaitFor(timeout(duration + 1)).then(() => {
expect(vm.$el.children.length).toBe(0)
vm.ok = true
}).then(() => {
expect(vm.$el.children[0].className).toBe('test test-enter')
}).thenWaitFor(nextFrame).then(() => {
expect(vm.$el.children[0].className).toBe('test test-enter-active')
}).thenWaitFor(timeout(duration + 1)).then(() => {
expect(vm.$el.children[0].className).toBe('test')
}).then(done)
})
})
}
function insertCSS (text) {
var cssEl = document.createElement('style')
cssEl.textContent = text.trim()
document.head.appendChild(cssEl)
}
function timeout (n) {
return next => setTimeout(next, n)
}