Skip to content

Commit bb1a378

Browse files
authored
regression: #1476 reactive prop object compare (#1479)
* chore: Add regression tests for #1476 * fix: reactive props and keep refs * chore: fix type compile error on test component
1 parent 86baba7 commit bb1a378

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

src/mount.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import {
22
h,
33
createApp,
44
defineComponent,
5+
reactive,
56
shallowReactive,
7+
isRef,
68
FunctionalComponent,
79
ComponentPublicInstance,
810
ComponentOptionsWithObjectProps,
@@ -379,12 +381,22 @@ export function mount(
379381
const MOUNT_COMPONENT_REF = 'VTU_COMPONENT'
380382
// we define props as reactive so that way when we update them with `setProps`
381383
// Vue's reactivity system will cause a rerender.
382-
const props = shallowReactive({
384+
const refs = shallowReactive<Record<string, unknown>>({})
385+
const props = reactive<Record<string, unknown>>({})
386+
387+
Object.entries({
383388
...options?.attrs,
384389
...options?.propsData,
385390
...options?.props,
386391
ref: MOUNT_COMPONENT_REF
392+
}).forEach(([k, v]) => {
393+
if (isRef(v)) {
394+
refs[k] = v
395+
} else {
396+
props[k] = v
397+
}
387398
})
399+
388400
const global = mergeGlobalProperties(options?.global)
389401
if (isObjectComponent(component)) {
390402
component.components = { ...component.components, ...global.components }
@@ -394,7 +406,7 @@ export function mount(
394406
const Parent = defineComponent({
395407
name: 'VTU_ROOT',
396408
render() {
397-
return h(component, props, slots)
409+
return h(component as ComponentOptions, { ...props, ...refs }, slots)
398410
}
399411
})
400412

tests/components/Issue1476.vue

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<template>
2+
<div>
3+
<template v-for="field of availableFields" :key="field.name">
4+
<button class="field" @click="selectedField = field">
5+
{{ field.name }}
6+
</button>
7+
<div
8+
v-if="selectedField === field"
9+
class="selectedField"
10+
>
11+
{{ field.name }}
12+
</div>
13+
</template>
14+
</div>
15+
</template>
16+
17+
<script lang="ts">
18+
export default {
19+
props: {
20+
availableFields: { type: Array, required: true }
21+
},
22+
data: () => ({
23+
selectedField: ''
24+
})
25+
}
26+
</script>

tests/props.spec.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import PropWithSymbol from './components/PropWithSymbol.vue'
44
import Hello from './components/Hello.vue'
55
import { defineComponent, h, isRef, ref } from 'vue'
66
import Title from './components/FunctionComponent'
7+
import Issue1476 from './components/Issue1476.vue'
78

89
describe('props', () => {
910
it('returns a single prop applied to a component', () => {
@@ -182,6 +183,27 @@ describe('props', () => {
182183
expect(wrapper.find('span').text()).toBe('Some value')
183184
})
184185

186+
it('should keep props as same object', async () => {
187+
// https://github.com/vuejs/test-utils/issues/1476
188+
const wrapper = mount(Issue1476, {
189+
props: {
190+
availableFields: [{ name: 'Animals' }, { name: 'Cities' }]
191+
}
192+
})
193+
194+
expect(wrapper.find('.subField').exists()).toBe(false)
195+
196+
await wrapper.findAll('.field')[0].trigger('click')
197+
198+
expect(wrapper.find('.selectedField').exists()).toBe(true)
199+
expect(wrapper.find('.selectedField').text()).toBe('Animals')
200+
201+
await wrapper.findAll('.field')[1].trigger('click')
202+
203+
expect(wrapper.find('.selectedField').exists()).toBe(true)
204+
expect(wrapper.find('.selectedField').text()).toBe('Cities')
205+
})
206+
185207
it('returns reactive props on a stubbed component shallow case', async () => {
186208
const Foo = {
187209
name: 'Foo',

0 commit comments

Comments
 (0)