-
Notifications
You must be signed in to change notification settings - Fork 669
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: improve slots mounting option #821
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
// @flow | ||
|
||
import Vue from 'vue' | ||
import { compileToFunctions } from 'vue-template-compiler' | ||
|
||
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 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you save the renderFunctions and then reassign? |
||
const vnodes = compiledResult.render.call( | ||
vm._renderProxy, vm.$createElement | ||
).children | ||
vm._renderProxy.$options.staticRenderFns = _staticRenderFns | ||
return vnodes | ||
} | ||
return [vm.$createElement(slotValue)] | ||
} | ||
|
||
export default function createRenderSlot ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What is the purpose of replacing the renderSlot alias? Does this solve the issue of parent being undefined? |
||
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 (options.slots && options.slots[name]) { | ||
this.$slots[name] = [] | ||
const slotsValue = options.slots[name] | ||
if (Array.isArray(slotsValue)) { | ||
slotsValue.forEach((value) => { | ||
if (typeof value === 'string') { | ||
const vnodes = createVNodes(this, value) | ||
if (Array.isArray(vnodes)) { | ||
this.$slots[name].push(...vnodes) | ||
} | ||
} else { | ||
this.$slots[name].push(this.$createElement(value)) | ||
} | ||
}) | ||
} else { | ||
const vnodes = createVNodes(this, slotsValue) | ||
if (Array.isArray(vnodes)) { | ||
this.$slots[name] = vnodes | ||
} | ||
} | ||
} | ||
return _renderSlot.call(this, name, fallback, props, bindObject) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
// @flow | ||
|
||
import createInstance from './create-instance' | ||
import createRenderSlot from './create-render-slot' | ||
|
||
export { createInstance, createRenderSlot } |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,6 @@ | ||
{ | ||
"name": "create-instance", | ||
"version": "1.0.0-beta.20", | ||
"main": "create-instance.js", | ||
"main": "index.js", | ||
"private": true | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
<template> | ||
<div><span baz="qux">{{ time }},{{ fromLocalVue }},{{ bar }}</span></div> | ||
</template> | ||
|
||
<script> | ||
export default{ | ||
name: 'component-with-parent-name', | ||
props: ['fromLocalVue', 'time'], | ||
data () { | ||
return { | ||
bar: 'quux' | ||
} | ||
}, | ||
mounted () { | ||
this.$parent.childComponentName = this.$options.name | ||
} | ||
} | ||
</script> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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', () => { | ||
|
@@ -224,14 +226,18 @@ 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') | ||
expect(wrapper).contains( | ||
'<div data-server-rendered="true" class="container"><header>hello,</header> <main>world</main> <footer></footer></div>' | ||
) | ||
} else { | ||
expect(wrapper.text()).to.contain('hello,world') | ||
expect(wrapper.html()).to.equal( | ||
'<div class="container"><header>hello,</header> <main>world</main> <footer></footer></div>' | ||
) | ||
} | ||
}) | ||
|
||
|
@@ -568,4 +574,72 @@ describeWithMountingMethods('options.slots', mountingMethod => { | |
expect(wrapper.contains(ComponentAsAClass)).to.equal(true) | ||
} | ||
}) | ||
|
||
itDoNotRunIf( | ||
mountingMethod.name === 'renderToString', | ||
'mounts component with default slot if passed string in slot object', | ||
() => { | ||
const wrapper1 = mount(ComponentWithSlots, { slots: { default: 'foo<span>123</span>{{ foo }}' }}) | ||
expect(wrapper1.find('main').html()).to.equal('<main>foo<span>123</span>bar</main>') | ||
const wrapper2 = mount(ComponentWithSlots, { slots: { default: '<p>1</p>{{ foo }}2' }}) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is how slots should behave. foo should not be resolved using the child vm that it's rendered in. If it uses any instance to resolve foo on, it should be the root instance. But I don't think we should support any variables in the slots option as it adds too much complexity There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am sorry. |
||
expect(wrapper2.find('main').html()).to.equal('<main><p>1</p>bar2</main>') | ||
const wrapper3 = mount(ComponentWithSlots, { slots: { default: '<p>1</p>{{ foo }}<p>2</p>' }}) | ||
expect(wrapper3.find('main').html()).to.equal('<main><p>1</p>bar<p>2</p></main>') | ||
const wrapper4 = mount(ComponentWithSlots, { slots: { default: '123' }}) | ||
expect(wrapper4.find('main').html()).to.equal('<main>123</main>') | ||
const wrapper5 = mount(ComponentWithSlots, { slots: { default: '1{{ foo }}2' }}) | ||
expect(wrapper5.find('main').html()).to.equal('<main>1bar2</main>') | ||
wrapper5.trigger('keydown') | ||
expect(wrapper5.find('main').html()).to.equal('<main>1BAR2</main>') | ||
const wrapper6 = mount(ComponentWithSlots, { slots: { default: '<p>1</p><p>2</p>' }}) | ||
expect(wrapper6.find('main').html()).to.equal('<main><p>1</p><p>2</p></main>') | ||
const wrapper7 = mount(ComponentWithSlots, { slots: { default: '1<p>2</p>3' }}) | ||
expect(wrapper7.find('main').html()).to.equal('<main>1<p>2</p>3</main>') | ||
const wrapper8 = mountingMethod(ComponentWithSlots, { slots: { default: ' space ' }}) | ||
expect(wrapper8.find('main').html()).to.equal('<main> space </main>') | ||
} | ||
) | ||
|
||
itDoNotRunIf( | ||
mountingMethod.name === 'renderToString', | ||
'sets a component which can access the parent component and the child component', | ||
() => { | ||
const localVue = createLocalVue() | ||
localVue.prototype.bar = 'FOO' | ||
const ParentComponent = mount( | ||
{ | ||
name: 'parentComponent', | ||
template: '<div><slot /></div>', | ||
data () { | ||
return { | ||
time: 1, | ||
childComponentName: '' | ||
} | ||
} | ||
}, | ||
{ | ||
components: { | ||
ComponentWithParentName | ||
}, | ||
slots: { | ||
default: [ | ||
'<component-with-parent-name :fromLocalVue="bar" :time="time" />', | ||
'<component-with-parent-name :fromLocalVue="bar" :time="time" />' | ||
] | ||
}, | ||
localVue | ||
} | ||
) | ||
const childComponentName = 'component-with-parent-name' | ||
expect(ParentComponent.vm.childComponentName).to.equal(childComponentName) | ||
expect(ParentComponent.vm.$children.length).to.equal(2) | ||
expect(ParentComponent.vm.$children.every(c => c.$options.name === childComponentName)).to.equal(true) | ||
expect(ParentComponent.html()).to.equal('<div><div><span baz="qux">1,FOO,quux</span></div><div><span baz="qux">1,FOO,quux</span></div></div>') | ||
ParentComponent.vm.time = 2 | ||
expect(ParentComponent.vm.childComponentName).to.equal(childComponentName) | ||
expect(ParentComponent.vm.$children.length).to.equal(2) | ||
expect(ParentComponent.vm.$children.every(c => c.$options.name === childComponentName)).to.equal(true) | ||
expect(ParentComponent.html()).to.equal('<div><div><span baz="qux">2,FOO,quux</span></div><div><span baz="qux">2,FOO,quux</span></div></div>') | ||
} | ||
) | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why the empty interpolation
{{ }}
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not understand.
It is not necessary.
I thought the compileToFunctions raises an error if it dose not exist at #274.