Repository: vuejs/vue
Pull request: fix: transition group should work with dynamic name (#6006)

<!--
Please make sure to read the Pull Request Guidelines:
https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#pull-request-guidelines
-->

<!-- PULL REQUEST TEMPLATE -->
<!-- (Update "[ ]" to "[x]" to check a box) -->

**What kind of change does this PR introduce?** (check at least one)

- [x] Bugfix
- [ ] Feature
- [ ] Code style update
- [ ] Refactor
- [ ] Build-related changes
- [ ] Other, please describe:

**Does this PR introduce a breaking change?** (check one)

- [ ] Yes
- [x] No

If yes, please describe the impact and migration path for existing applications:

**The PR fulfills these requirements:**

- [x] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
- [x] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
- [x] All tests are passing: https://github.com/vuejs/vue/blob/dev/.github/CONTRIBUTING.md#development-setup
- [x] New/updated tests are included

If adding a **new feature**, the PR's description includes:
- [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)

**Other information:**

fix #6006 


Changed files:
filename: src/platforms/web/runtime/class-util.js
patch: @@ -42,12 +42,20 @@ export function removeClass (el: HTMLElement, cls: ?string) {
     } else {
       el.classList.remove(cls)
     }
+    if (!el.classList.length) {
+      el.removeAttribute('class')
+    }
   } else {
     let cur = ` ${el.getAttribute('class') || ''} `
     const tar = ' ' + cls + ' '
     while (cur.indexOf(tar) >= 0) {
       cur = cur.replace(tar, ' ')
     }
-    el.setAttribute('class', cur.trim())
+    cur = cur.trim()
+    if (cur) {
+      el.setAttribute('class', cur)
+    } else {
+      el.removeAttribute('class')
+    }
   }
 }

filename: src/platforms/web/runtime/components/transition-group.js
patch: @@ -127,7 +127,7 @@ export default {
       if (!hasTransition) {
         return false
       }
-      if (this._hasMove != null) {
+      if (this._hasMove) {
         return this._hasMove
       }
       // Detect whether an element with the move class applied has

filename: test/unit/features/transition/transition-group.spec.js
patch: @@ -293,5 +293,52 @@ if (!isIE9) {
       }).$mount()
       expect('<transition-group> children must be keyed: <div>').toHaveBeenWarned()
     })
+
+    // Github issue #6006
+    it('should work with dynamic name', done => {
+      const vm = new Vue({
+        template: `
+          <div>
+            <transition-group :name="name">
+              <div v-for="item in items" :key="item">{{ item }}</div>
+            </transition-group>
+          </div>
+        `,
+        data: {
+          items: ['a', 'b', 'c'],
+          name: 'group'
+        }
+      }).$mount(el)
+
+      vm.name = 'invalid-name'
+      vm.items = ['b', 'c', 'a']
+      waitForUpdate(() => {
+        expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
+          `<span>` +
+            `<div>b</div>` +
+            `<div>c</div>` +
+            `<div>a</div>` +
+          `</span>`
+        )
+        vm.name = 'group'
+        vm.items = ['a', 'b', 'c']
+      }).thenWaitFor(nextFrame).then(() => {
+        expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
+          `<span>` +
+            `<div class="group-move">a</div>` +
+            `<div class="group-move">b</div>` +
+            `<div class="group-move">c</div>` +
+          `</span>`
+        )
+      }).thenWaitFor(duration * 2 + buffer).then(() => {
+        expect(vm.$el.innerHTML.replace(/\s?style=""(\s?)/g, '$1')).toBe(
+          `<span>` +
+            `<div>a</div>` +
+            `<div>b</div>` +
+            `<div>c</div>` +
+          `</span>`
+        )
+      }).then(done)
+    })
   })
 }

filename: test/unit/features/transition/transition.spec.js
patch: @@ -437,7 +437,7 @@ if (!isIE9) {
         expect(enterSpy).toHaveBeenCalled()
         expect(vm.$el.innerHTML).toBe('<div class="nope-enter nope-enter-active">foo</div>')
       }).thenWaitFor(nextFrame).then(() => {
-        expect(vm.$el.innerHTML).toMatch(/<div( class="")?>foo<\/div>/)
+        expect(vm.$el.innerHTML).toBe('<div>foo</div>')
       }).then(done)
     })