Skip to content

Commit 42c1950

Browse files
committed
properly merge classes between multiple nested components sharing the same element (fix vuejs#3365)
1 parent d9d4e7f commit 42c1950

File tree

2 files changed

+56
-7
lines changed

2 files changed

+56
-7
lines changed

src/platforms/web/util/class.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,18 @@ import { isObject } from 'shared/util'
44

55
export function genClassForVnode (vnode: VNode): string {
66
let data = vnode.data
7-
// Important: check if this is a component container node
8-
// or a child component root node
9-
let i
10-
if ((i = vnode.child) && (i = i._vnode.data)) {
11-
data = mergeClassData(i, data)
7+
let parentNode = vnode
8+
let childNode = vnode
9+
while (childNode.child) {
10+
childNode = childNode.child._vnode
11+
if (childNode.data) {
12+
data = mergeClassData(childNode.data, data)
13+
}
1214
}
13-
if ((i = vnode.parent) && (i = i.data)) {
14-
data = mergeClassData(data, i)
15+
while ((parentNode = parentNode.parent)) {
16+
if (parentNode.data) {
17+
data = mergeClassData(data, parentNode.data)
18+
}
1519
}
1620
return genClassFromData(data)
1721
}

test/unit/features/directives/class.spec.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,51 @@ describe('Directive v-bind:class', () => {
9393
}).then(done)
9494
})
9595

96+
it('class merge between multiple nested components sharing same element', done => {
97+
const vm = new Vue({
98+
template: `
99+
<component1 :class="componentClass1">
100+
<component2 :class="componentClass2">
101+
<component3 :class="componentClass3">
102+
some text
103+
</component3>
104+
</component2>
105+
</component1>
106+
`,
107+
data: {
108+
componentClass1: 'componentClass1',
109+
componentClass2: 'componentClass2',
110+
componentClass3: 'componentClass3'
111+
},
112+
components: {
113+
component1: {
114+
render () {
115+
return this.$slots.default[0]
116+
}
117+
},
118+
component2: {
119+
render () {
120+
return this.$slots.default[0]
121+
}
122+
},
123+
component3: {
124+
template: '<div class="staticClass"><slot></slot></div>'
125+
}
126+
}
127+
}).$mount()
128+
expect(vm.$el.className).toBe('staticClass componentClass3 componentClass2 componentClass1')
129+
vm.componentClass1 = 'c1'
130+
waitForUpdate(() => {
131+
expect(vm.$el.className).toBe('staticClass componentClass3 componentClass2 c1')
132+
vm.componentClass2 = 'c2'
133+
}).then(() => {
134+
expect(vm.$el.className).toBe('staticClass componentClass3 c2 c1')
135+
vm.componentClass3 = 'c3'
136+
}).then(() => {
137+
expect(vm.$el.className).toBe('staticClass c3 c2 c1')
138+
}).then(done)
139+
})
140+
96141
it('deep update', done => {
97142
const vm = new Vue({
98143
template: '<div :class="test"></div>',

0 commit comments

Comments
 (0)