Skip to content

Commit da3e193

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

File tree

10 files changed

+159
-9
lines changed

10 files changed

+159
-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

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

3-
import { createSlotVNodes } from './add-slots'
43
import addMocks from './add-mocks'
54
import { addEventLogger } from './log-events'
65
import { createComponentStubs } from 'shared/stub-components'
6+
import { createSlotVNodes } from './create-slot-vnodes'
77
import { throwError, warn, vueVersion } from 'shared/util'
88
import { compileTemplate } from 'shared/compile-template'
99
import extractInstanceOptions from './extract-instance-options'

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

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// @flow
2+
3+
import Vue from 'vue'
4+
import { compileToFunctions } from 'vue-template-compiler'
5+
6+
const _renderSlot = Vue.prototype._t
7+
8+
function createVNodes (
9+
vm: Component,
10+
slotValue: Component | string
11+
): ?Array<VNode> {
12+
if (typeof slotValue === 'string') {
13+
const compiledResult = compileToFunctions(`<div>${slotValue}{{ }}</div>`)
14+
const _staticRenderFns = vm._renderProxy.$options.staticRenderFns
15+
vm._renderProxy.$options.staticRenderFns = compiledResult.staticRenderFns
16+
const vnodes = compiledResult.render.call(
17+
vm._renderProxy, vm.$createElement
18+
).children
19+
vm._renderProxy.$options.staticRenderFns = _staticRenderFns
20+
return vnodes
21+
}
22+
return [vm.$createElement(slotValue)]
23+
}
24+
25+
export default function createRenderSlot (
26+
options: Object
27+
): (
28+
name: string,
29+
fallback: ?Array<VNode>,
30+
props: ?Object,
31+
bindObject: ?Object
32+
) => ?Array<VNode> {
33+
return function renderSlot (
34+
name: string,
35+
fallback: ?Array<VNode>,
36+
props: ?Object,
37+
bindObject: ?Object
38+
): ?Array<VNode> {
39+
if (options.slots && options.slots[name]) {
40+
this.$slots[name] = []
41+
const slotsValue = options.slots[name]
42+
if (Array.isArray(slotsValue)) {
43+
slotsValue.forEach((value) => {
44+
if (typeof value === 'string') {
45+
const vnodes = createVNodes(this, value)
46+
if (Array.isArray(vnodes)) {
47+
this.$slots[name].push(...vnodes)
48+
}
49+
} else {
50+
this.$slots[name].push(this.$createElement(value))
51+
}
52+
})
53+
} else {
54+
const vnodes = createVNodes(this, slotsValue)
55+
if (Array.isArray(vnodes)) {
56+
this.$slots[name] = vnodes
57+
}
58+
}
59+
}
60+
return _renderSlot.call(this, name, fallback, props, bindObject)
61+
}
62+
}

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

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
// @flow
2+
3+
import createInstance from './create-instance'
4+
import createRenderSlot from './create-render-slot'
5+
6+
export { createInstance, createRenderSlot }

Diff for: packages/create-instance/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "create-instance",
33
"version": "1.0.0-beta.20",
4-
"main": "create-instance.js",
4+
"main": "index.js",
55
"private": true
66
}

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

+66-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,18 @@ 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(
235+
'<div data-server-rendered="true" class="container"><header>hello,</header> <main>world</main> <footer></footer></div>'
236+
)
233237
} else {
234-
expect(wrapper.text()).to.contain('hello,world')
238+
expect(wrapper.html()).to.equal(
239+
'<div class="container"><header>hello,</header> <main>world</main> <footer></footer></div>'
240+
)
235241
}
236242
})
237243

@@ -546,4 +552,60 @@ describeWithMountingMethods('options.slots', mountingMethod => {
546552
wrapper.find('div').trigger('click')
547553
}
548554
)
555+
556+
itDoNotRunIf(
557+
mountingMethod.name === 'renderToString',
558+
'mounts component with default slot if passed string in slot object',
559+
() => {
560+
const wrapper1 = mount(ComponentWithSlots, { slots: { default: 'foo<span>123</span>{{ foo }}' }})
561+
expect(wrapper1.find('main').html()).to.equal('<main>foo<span>123</span>bar</main>')
562+
const wrapper2 = mount(ComponentWithSlots, { slots: { default: '<p>1</p>{{ foo }}2' }})
563+
expect(wrapper2.find('main').html()).to.equal('<main><p>1</p>bar2</main>')
564+
const wrapper3 = mount(ComponentWithSlots, { slots: { default: '<p>1</p>{{ foo }}<p>2</p>' }})
565+
expect(wrapper3.find('main').html()).to.equal('<main><p>1</p>bar<p>2</p></main>')
566+
const wrapper4 = mount(ComponentWithSlots, { slots: { default: '123' }})
567+
expect(wrapper4.find('main').html()).to.equal('<main>123</main>')
568+
const wrapper5 = mount(ComponentWithSlots, { slots: { default: '1{{ foo }}2' }})
569+
expect(wrapper5.find('main').html()).to.equal('<main>1bar2</main>')
570+
wrapper5.trigger('keydown')
571+
expect(wrapper5.find('main').html()).to.equal('<main>1BAR2</main>')
572+
const wrapper6 = mount(ComponentWithSlots, { slots: { default: '<p>1</p><p>2</p>' }})
573+
expect(wrapper6.find('main').html()).to.equal('<main><p>1</p><p>2</p></main>')
574+
const wrapper7 = mount(ComponentWithSlots, { slots: { default: '1<p>2</p>3' }})
575+
expect(wrapper7.find('main').html()).to.equal('<main>1<p>2</p>3</main>')
576+
const wrapper8 = mountingMethod(ComponentWithSlots, { slots: { default: ' space ' }})
577+
expect(wrapper8.find('main').html()).to.equal('<main> space </main>')
578+
}
579+
)
580+
581+
itDoNotRunIf(
582+
mountingMethod.name === 'renderToString',
583+
'sets a component which can access the parent component',
584+
() => {
585+
const localVue = createLocalVue()
586+
localVue.prototype.bar = 'FOO'
587+
const wrapperComponent = mount(
588+
{
589+
name: 'parentComponent',
590+
template: '<div><slot /></div>',
591+
data () {
592+
return {
593+
childName: ''
594+
}
595+
}
596+
},
597+
{
598+
components: {
599+
ComponentWithParentName
600+
},
601+
slots: {
602+
default: '<component-with-parent-name :foo="bar" />'
603+
},
604+
localVue
605+
}
606+
)
607+
expect(wrapperComponent.vm.childName).to.equal('component-with-parent-name')
608+
expect(wrapperComponent.html()).to.equal('<div><div foo="FOO"><span baz="qux">quux</span></div></div>')
609+
}
610+
)
549611
})

0 commit comments

Comments
 (0)