Evan You пре 10 година
родитељ
комит
74538b4529

+ 8 - 41
src/platforms/web/runtime/class-util.js

@@ -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')
-  }
-}
-
-/**
- * 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 || ''
+    el.setAttribute('class', cur.trim())
   }
-  return classname
 }

+ 1 - 2
src/platforms/web/runtime/modules/class.js

@@ -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
   }
 }

+ 6 - 5
src/platforms/web/runtime/modules/transition.js

@@ -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)
   })

+ 7 - 2
test/helpers/wait-for-update.js

@@ -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
     }
   }
 

+ 79 - 0
test/unit/features/transition/transition-css.spec.js

@@ -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)
+}