Skip to content

Commit 9ba301b

Browse files
committed
fix: improve slots mounting option
1 parent d0a9c16 commit 9ba301b

File tree

7 files changed

+124
-9
lines changed

7 files changed

+124
-9
lines changed

Diff for: packages/create-instance/create-functional-component.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { throwError } from 'shared/util'
44
import { validateSlots } from './validate-slots'
5-
import { createSlotVNodes } from './add-slots'
5+
import { createSlotVNodes } from './create-slot-vnodes'
66

77
export default function createFunctionalComponent (
88
component: Component,

Diff for: packages/create-instance/create-instance.js

+62-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// @flow
22

3-
import { createSlotVNodes } from './add-slots'
3+
import Vue from 'vue'
4+
import { compileToFunctions } from 'vue-template-compiler'
5+
import { createSlotVNodes } from './create-slot-vnodes'
46
import addMocks from './add-mocks'
57
import { addEventLogger } from './log-events'
68
import { createComponentStubs } from 'shared/stub-components'
@@ -12,7 +14,65 @@ import { componentNeedsCompiling } from 'shared/validators'
1214
import { validateSlots } from './validate-slots'
1315
import createScopedSlots from './create-scoped-slots'
1416

15-
export default function createInstance (
17+
const _renderSlot = Vue.prototype._t
18+
19+
function createVNodes (
20+
vm: Component,
21+
slotValue: Component | string
22+
): Array<VNode> {
23+
if (typeof slotValue === 'string') {
24+
const compiledResult = compileToFunctions(`<div>${slotValue}{{ }}</div>`)
25+
const _staticRenderFns = vm._renderProxy.$options.staticRenderFns
26+
vm._renderProxy.$options.staticRenderFns = compiledResult.staticRenderFns
27+
const elem = compiledResult.render.call(
28+
vm._renderProxy, vm.$createElement
29+
).children
30+
vm._renderProxy.$options.staticRenderFns = _staticRenderFns
31+
return elem
32+
}
33+
return [vm.$createElement(slotValue)]
34+
}
35+
36+
export function createRenderSlot (
37+
options: Object
38+
): (
39+
name: string,
40+
fallback: ?Array<VNode>,
41+
props: ?Object,
42+
bindObject: ?Object
43+
) => ?Array<VNode> {
44+
return function renderSlot (
45+
name: string,
46+
fallback: ?Array<VNode>,
47+
props: ?Object,
48+
bindObject: ?Object
49+
): ?Array<VNode> {
50+
if (createVNodes && options.slots && options.slots[name]) {
51+
const slotsValue = options.slots[name]
52+
if (Array.isArray(slotsValue)) {
53+
this.$slots[name] = []
54+
for (let i = 0; i < slotsValue.length; i++) {
55+
const _slotValue = slotsValue[i]
56+
if (typeof _slotValue === 'string') {
57+
const slots = createVNodes(this, _slotValue)
58+
if (Array.isArray(slots)) {
59+
this.$slots[name].push(...slots)
60+
} else {
61+
this.$slots[name].push(slots)
62+
}
63+
} else {
64+
this.$slots[name].push(this.$createElement(_slotValue))
65+
}
66+
}
67+
} else {
68+
this.$slots[name] = createVNodes(this, slotsValue)
69+
}
70+
}
71+
return _renderSlot.call(this, name, fallback, props, bindObject)
72+
}
73+
}
74+
75+
export function createInstance (
1676
component: Component,
1777
options: Options,
1878
_Vue: Component,

Diff for: packages/server-test-utils/src/renderToString.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// @flow
22

33
import Vue from 'vue'
4-
import createInstance from 'create-instance'
4+
import { createInstance, createRenderSlot } from 'create-instance'
55
import { throwError } from 'shared/util'
66
import { createRenderer } from 'vue-server-renderer'
77
import testUtils from '@vue/test-utils'
@@ -29,6 +29,8 @@ export default function renderToString (
2929
throwError(`you cannot use attachToDocument with ` + `renderToString`)
3030
}
3131
const vueConstructor = testUtils.createLocalVue(options.localVue)
32+
vueConstructor.prototype._t = createRenderSlot(options)
33+
3234
const vm = createInstance(
3335
component,
3436
mergeOptions(options, config),

Diff for: packages/test-utils/src/mount.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,14 @@ import './matches-polyfill'
44
import './object-assign-polyfill'
55
import Vue from 'vue'
66
import VueWrapper from './vue-wrapper'
7-
import createInstance from 'create-instance'
87
import createElement from './create-element'
98
import createLocalVue from './create-local-vue'
109
import errorHandler from './error-handler'
1110
import { findAllVueComponentsFromVm } from './find-vue-components'
1211
import { mergeOptions } from 'shared/merge-options'
1312
import config from './config'
1413
import warnIfNoWindow from './warn-if-no-window'
14+
import { createInstance, createRenderSlot } from 'create-instance'
1515

1616
Vue.config.productionTip = false
1717
Vue.config.devtools = false
@@ -28,6 +28,7 @@ export default function mount (
2828
// Remove cached constructor
2929
delete component._Ctor
3030
const vueConstructor = createLocalVue(options.localVue)
31+
vueConstructor.prototype._t = createRenderSlot(options)
3132

3233
const elm = options.attachToDocument ? createElement() : undefined
3334

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
i<template>
2+
<div foo="bar"><span baz="qux">{{ quux }}</span></div>
3+
</template>
4+
5+
<script>
6+
export default{
7+
name: 'component-with-parent-name',
8+
data () {
9+
return {
10+
quux: 'quux'
11+
}
12+
},
13+
mounted () {
14+
this.$parent.childName = this.$options.name
15+
}
16+
}
17+
</script>

Diff for: test/specs/mounting-options/slots.spec.js

+39-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { compileToFunctions } from 'vue-template-compiler'
22
import Component from '~resources/components/component.vue'
33
import ComponentWithSlots from '~resources/components/component-with-slots.vue'
44
import ComponentAsAClass from '~resources/components/component-as-a-class.vue'
5+
import ComponentWithParentName from '~resources/components/component-with-parent-name.vue'
56
import { describeWithMountingMethods, vueVersion } from '~resources/utils'
67
import { itSkipIf, itDoNotRunIf } from 'conditional-specs'
8+
import { mount, createLocalVue } from '~vue/test-utils'
79

810
describeWithMountingMethods('options.slots', mountingMethod => {
911
it('mounts component with default slot if passed component in slot object', () => {
@@ -224,14 +226,16 @@ describeWithMountingMethods('options.slots', mountingMethod => {
224226
it('mounts component with text slot', () => {
225227
const wrapper = mountingMethod(ComponentWithSlots, {
226228
slots: {
227-
default: 'hello,',
228-
header: 'world'
229+
header: 'hello,',
230+
default: 'world'
229231
}
230232
})
231233
if (mountingMethod.name === 'renderToString') {
232-
expect(wrapper).contains('hello,world')
234+
expect(wrapper).contains('hello,')
235+
expect(wrapper).contains('world')
233236
} else {
234-
expect(wrapper.text()).to.contain('hello,world')
237+
expect(wrapper.find('header').text()).to.equal('hello,')
238+
expect(wrapper.find('main').text()).to.equal('world')
235239
}
236240
})
237241

@@ -546,4 +550,35 @@ describeWithMountingMethods('options.slots', mountingMethod => {
546550
wrapper.find('div').trigger('click')
547551
}
548552
)
553+
554+
itDoNotRunIf(
555+
mountingMethod.name === 'renderToString',
556+
'sets a component which can access the parent component',
557+
() => {
558+
const localVue = createLocalVue()
559+
localVue.prototype.bar = 'FOO'
560+
const wrapperComponent = mount(
561+
{
562+
name: 'parentComponent',
563+
template: '<div><slot /></div>',
564+
data () {
565+
return {
566+
childName: ''
567+
}
568+
}
569+
},
570+
{
571+
components: {
572+
ComponentWithParentName
573+
},
574+
slots: {
575+
default: '<component-with-parent-name :foo="bar" />'
576+
},
577+
localVue
578+
}
579+
)
580+
expect(wrapperComponent.vm.childName).to.equal('component-with-parent-name')
581+
expect(wrapperComponent.html()).to.equal('<div><div foo="FOO"><span baz="qux">quux</span></div></div>')
582+
}
583+
)
549584
})

0 commit comments

Comments
 (0)