Skip to content

Commit

Permalink
fix: improve slots mounting option
Browse files Browse the repository at this point in the history
  • Loading branch information
38elements committed Jul 12, 2018
1 parent d0a9c16 commit c24e8c5
Show file tree
Hide file tree
Showing 7 changed files with 122 additions and 8 deletions.
2 changes: 1 addition & 1 deletion packages/create-instance/create-functional-component.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { throwError } from 'shared/util'
import { validateSlots } from './validate-slots'
import { createSlotVNodes } from './add-slots'
import { createSlotVNodes } from './create-slot-vnodes'

export default function createFunctionalComponent (
component: Component,
Expand Down
64 changes: 62 additions & 2 deletions packages/create-instance/create-instance.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// @flow

import { createSlotVNodes } from './add-slots'
import Vue from 'vue'
import { compileToFunctions } from 'vue-template-compiler'
import { createSlotVNodes } from './create-slot-vnodes'
import addMocks from './add-mocks'
import { addEventLogger } from './log-events'
import { createComponentStubs } from 'shared/stub-components'
Expand All @@ -12,7 +14,65 @@ import { componentNeedsCompiling } from 'shared/validators'
import { validateSlots } from './validate-slots'
import createScopedSlots from './create-scoped-slots'

export default function createInstance (
const _renderSlot = Vue.prototype._t

function createVNodes (
vm: Component,
slotValue: Component | string
): Array<VNode> {
if (typeof slotValue === 'string') {
const compiledResult = compileToFunctions(`<div>${slotValue}{{ }}</div>`)
const _staticRenderFns = vm._renderProxy.$options.staticRenderFns
vm._renderProxy.$options.staticRenderFns = compiledResult.staticRenderFns
const elem = compiledResult.render.call(
vm._renderProxy, vm.$createElement
).children
vm._renderProxy.$options.staticRenderFns = _staticRenderFns
return elem
}
return [vm.$createElement(slotValue)]
}

export function createRenderSlot (
options: Object
): (
name: string,
fallback: ?Array<VNode>,
props: ?Object,
bindObject: ?Object
) => ?Array<VNode> {
return function renderSlot (
name: string,
fallback: ?Array<VNode>,
props: ?Object,
bindObject: ?Object
): ?Array<VNode> {
if (createVNodes && options.slots && options.slots[name]) {
const slotsValue = options.slots[name]
if (Array.isArray(slotsValue)) {
this.$slots[name] = []
for (let i = 0; i < slotsValue.length; i++) {
const _slotValue = slotsValue[i]
if (typeof _slotValue === 'string') {
const slots = createVNodes(this, _slotValue)
if (Array.isArray(slots)) {
this.$slots[name].push(...slots)
} else {
this.$slots[name].push(slots)
}
} else {
this.$slots[name].push(this.$createElement(_slotValue))
}
}
} else {
this.$slots[name] = createVNodes(this, slotsValue)
}
}
return _renderSlot.call(this, name, fallback, props, bindObject)
}
}

export function createInstance (
component: Component,
options: Options,
_Vue: Component,
Expand Down
File renamed without changes.
4 changes: 3 additions & 1 deletion packages/server-test-utils/src/renderToString.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// @flow

import Vue from 'vue'
import createInstance from 'create-instance'
import { createInstance, createRenderSlot } from 'create-instance'
import { throwError } from 'shared/util'
import { createRenderer } from 'vue-server-renderer'
import testUtils from '@vue/test-utils'
Expand Down Expand Up @@ -29,6 +29,8 @@ export default function renderToString (
throwError(`you cannot use attachToDocument with ` + `renderToString`)
}
const vueConstructor = testUtils.createLocalVue(options.localVue)
vueConstructor.prototype._t = createRenderSlot(options)

const vm = createInstance(
component,
mergeOptions(options, config),
Expand Down
3 changes: 2 additions & 1 deletion packages/test-utils/src/mount.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,14 @@ import './matches-polyfill'
import './object-assign-polyfill'
import Vue from 'vue'
import VueWrapper from './vue-wrapper'
import createInstance from 'create-instance'
import createElement from './create-element'
import createLocalVue from './create-local-vue'
import errorHandler from './error-handler'
import { findAllVueComponentsFromVm } from './find-vue-components'
import { mergeOptions } from 'shared/merge-options'
import config from './config'
import warnIfNoWindow from './warn-if-no-window'
import { createInstance, createRenderSlot } from 'create-instance'

Vue.config.productionTip = false
Vue.config.devtools = false
Expand All @@ -28,6 +28,7 @@ export default function mount (
// Remove cached constructor
delete component._Ctor
const vueConstructor = createLocalVue(options.localVue)
vueConstructor.prototype._t = createRenderSlot(options)

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

Expand Down
17 changes: 17 additions & 0 deletions test/resources/components/component-with-parent-name.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
i<template>
<div foo="bar"><span baz="qux">{{ quux }}</span></div>
</template>

<script>
export default{
name: 'component-with-parent-name',
data () {
return {
quux: 'quux'
}
},
mounted () {
this.$parent.childName = this.$options.name
}
}
</script>
40 changes: 37 additions & 3 deletions test/specs/mounting-options/slots.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import { compileToFunctions } from 'vue-template-compiler'
import Component from '~resources/components/component.vue'
import ComponentWithSlots from '~resources/components/component-with-slots.vue'
import ComponentAsAClass from '~resources/components/component-as-a-class.vue'
import ComponentWithParentName from '~resources/components/component-with-parent-name.vue'
import { describeWithMountingMethods, vueVersion } from '~resources/utils'
import { itSkipIf, itDoNotRunIf } from 'conditional-specs'
import { mount, createLocalVue } from '~vue/test-utils'

describeWithMountingMethods('options.slots', mountingMethod => {
it('mounts component with default slot if passed component in slot object', () => {
Expand Down Expand Up @@ -224,14 +226,15 @@ describeWithMountingMethods('options.slots', mountingMethod => {
it('mounts component with text slot', () => {
const wrapper = mountingMethod(ComponentWithSlots, {
slots: {
default: 'hello,',
header: 'world'
header: 'hello,',
default: 'world'
}
})
if (mountingMethod.name === 'renderToString') {
expect(wrapper).contains('hello,world')
} else {
expect(wrapper.text()).to.contain('hello,world')
expect(wrapper.find('header').text()).to.equal('hello,')
expect(wrapper.find('main').text()).to.equal('world')
}
})

Expand Down Expand Up @@ -546,4 +549,35 @@ describeWithMountingMethods('options.slots', mountingMethod => {
wrapper.find('div').trigger('click')
}
)

itDoNotRunIf(
mountingMethod.name === 'renderToString',
'sets a component which can access the parent component',
() => {
const localVue = createLocalVue()
localVue.prototype.bar = 'FOO'
const wrapperComponent = mount(
{
name: 'parentComponent',
template: '<div><slot /></div>',
data () {
return {
childName: ''
}
}
},
{
components: {
ComponentWithParentName
},
slots: {
default: '<component-with-parent-name :foo="bar" />'
},
localVue
}
)
expect(wrapperComponent.vm.childName).to.equal('component-with-parent-name')
expect(wrapperComponent.html()).to.equal('<div><div foo="FOO"><span baz="qux">quux</span></div></div>')
}
)
})

0 comments on commit c24e8c5

Please sign in to comment.