Skip to content

Commit 303378f

Browse files
committed
support default scoped slot + function children as scoped slot
1 parent 745f8a9 commit 303378f

File tree

7 files changed

+79
-24
lines changed

7 files changed

+79
-24
lines changed

.babelrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
{
22
"presets": ["es2015", "flow-vue"],
3+
"plugins": ["transform-vue-jsx"],
34
"ignore": [
45
"dist/*.js",
56
"packages/**/*.js"

flow/vnode.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ declare interface VNodeData {
5353
};
5454
directives?: Array<VNodeDirective>;
5555
keepAlive?: boolean;
56+
scopedSlots?: { [key: string]: Function }
5657
}
5758

5859
declare type VNodeDirective = {

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,11 @@
6060
"devDependencies": {
6161
"babel-core": "^6.9.0",
6262
"babel-eslint": "^7.1.0",
63+
"babel-helper-vue-jsx-merge-props": "^2.0.2",
6364
"babel-loader": "^6.2.4",
6465
"babel-plugin-coverage": "^1.0.0",
66+
"babel-plugin-syntax-jsx": "^6.18.0",
67+
"babel-plugin-transform-vue-jsx": "^3.2.0",
6568
"babel-preset-es2015": "^6.9.0",
6669
"babel-preset-flow-vue": "^1.0.0",
6770
"buble": "^0.14.0",

src/compiler/parser/index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,7 +355,7 @@ function processSlot (el) {
355355
} else {
356356
const slotTarget = getBindingAttr(el, 'slot')
357357
if (slotTarget) {
358-
el.slotTarget = slotTarget
358+
el.slotTarget = slotTarget === '""' ? '"default"' : slotTarget
359359
el.slotScope = getAndRemoveAttr(el, 'scope')
360360
}
361361
}

src/core/instance/render.js

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,6 @@ export function renderMixin (Vue: Class<Component>) {
191191
fallback: ?Array<VNode>,
192192
props: ?Object
193193
): ?Array<VNode> {
194-
if (process.env.NODE_ENV !== 'production' && name === 'default' && props) {
195-
warn(`Scoped slots must be named`, this)
196-
}
197194
const scopedSlotFn = this.$scopedSlots && this.$scopedSlots[name]
198195
if (scopedSlotFn) { // scoped slot
199196
return scopedSlotFn(props || {}) || fallback

src/core/vdom/create-element.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,13 @@ export function _createElement (
3939
// in case of component :is set to falsy value
4040
return emptyVNode()
4141
}
42+
// support single function children as default scoped slot
43+
if (Array.isArray(children) &&
44+
typeof children[0] === 'function') {
45+
data = data || {}
46+
data.scopedSlots = { default: children[0] }
47+
children.length = 0
48+
}
4249
if (typeof tag === 'string') {
4350
let Ctor
4451
const ns = config.getTagNamespace(tag)

test/unit/features/component/component-scoped-slot.spec.js

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
import Vue from 'vue'
22

33
describe('Component scoped slot', () => {
4+
it('default slot', () => {
5+
const vm = new Vue({
6+
template: `<test><span slot scope="props">{{ props.msg }}</span></test>`,
7+
components: {
8+
test: {
9+
data () {
10+
return { msg: 'hello' }
11+
},
12+
template: `
13+
<div>
14+
<slot :msg="msg"></slot>
15+
</div>
16+
`
17+
}
18+
}
19+
}).$mount()
20+
expect(vm.$el.innerHTML).toBe('<span>hello</span>')
21+
})
22+
423
it('normal element slot', done => {
524
const vm = new Vue({
625
template: `
@@ -204,25 +223,6 @@ describe('Component scoped slot', () => {
204223
expect(vm.$el.innerHTML).toBe('<span>meh</span>')
205224
})
206225

207-
it('warn un-named scoped slot', () => {
208-
new Vue({
209-
template: `<test><span scope="lol"></span></test>`,
210-
components: {
211-
test: {
212-
data () {
213-
return { msg: 'hello' }
214-
},
215-
template: `
216-
<div>
217-
<slot :msg="msg"></slot>
218-
</div>
219-
`
220-
}
221-
}
222-
}).$mount()
223-
expect('Scoped slots must be named').toHaveBeenWarned()
224-
})
225-
226226
it('warn key on slot', () => {
227227
new Vue({
228228
template: `
@@ -250,7 +250,7 @@ describe('Component scoped slot', () => {
250250
expect(`\`key\` does not work on <slot>`).toHaveBeenWarned()
251251
})
252252

253-
it('render function usage', done => {
253+
it('render function usage (named, via data)', done => {
254254
const vm = new Vue({
255255
render (h) {
256256
return h('test', {
@@ -282,4 +282,50 @@ describe('Component scoped slot', () => {
282282
expect(vm.$el.innerHTML).toBe('<span>world</span>')
283283
}).then(done)
284284
})
285+
286+
it('render function usage (default, as children)', () => {
287+
const vm = new Vue({
288+
render (h) {
289+
return h('test', [
290+
props => h('span', [props.msg])
291+
])
292+
},
293+
components: {
294+
test: {
295+
data () {
296+
return { msg: 'hello' }
297+
},
298+
render (h) {
299+
return h('div', [
300+
this.$scopedSlots.default({ msg: this.msg })
301+
])
302+
}
303+
}
304+
}
305+
}).$mount()
306+
expect(vm.$el.innerHTML).toBe('<span>hello</span>')
307+
})
308+
309+
it('render function usage (JSX)', () => {
310+
const vm = new Vue({
311+
render (h) {
312+
return <test>{
313+
props => <span>{props.msg}</span>
314+
}</test>
315+
},
316+
components: {
317+
test: {
318+
data () {
319+
return { msg: 'hello' }
320+
},
321+
render (h) {
322+
return <div>
323+
{this.$scopedSlots.default({ msg: this.msg })}
324+
</div>
325+
}
326+
}
327+
}
328+
}).$mount()
329+
expect(vm.$el.innerHTML).toBe('<span>hello</span>')
330+
})
285331
})

0 commit comments

Comments
 (0)