Skip to content

Commit

Permalink
fix: improve scopedSlots option (#808)
Browse files Browse the repository at this point in the history
  • Loading branch information
38elements authored and eddyerburgh committed Jul 6, 2018
1 parent dd30546 commit b946997
Show file tree
Hide file tree
Showing 5 changed files with 134 additions and 104 deletions.
6 changes: 5 additions & 1 deletion packages/create-instance/create-instance.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import extractInstanceOptions from './extract-instance-options'
import createFunctionalComponent from './create-functional-component'
import { componentNeedsCompiling } from 'shared/validators'
import { validateSlots } from './validate-slots'
import createScopedSlots from './create-scoped-slots'

export default function createInstance (
component: Component,
Expand Down Expand Up @@ -122,6 +123,8 @@ export default function createInstance (
options.provide = () => obj
}

const scopedSlots = createScopedSlots(options.scopedSlots)

const Parent = _Vue.extend({
provide: options.provide,
render (h) {
Expand All @@ -134,7 +137,8 @@ export default function createInstance (
ref: 'vm',
props: options.propsData,
on: options.listeners,
attrs: options.attrs
attrs: options.attrs,
scopedSlots
},
slots
)
Expand Down
87 changes: 87 additions & 0 deletions packages/create-instance/create-scoped-slots.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// @flow

import Vue from 'vue'
import { compileToFunctions } from 'vue-template-compiler'
import { throwError, vueVersion } from 'shared/util'

function isDestructuringSlotScope (slotScope: string): boolean {
return slotScope[0] === '{' && slotScope[slotScope.length - 1] === '}'
}

function getVueTemplateCompilerHelpers (): { [name: string]: Function } {
const vue = new Vue()
const helpers = {}
const names = [
'_c',
'_o',
'_n',
'_s',
'_l',
'_t',
'_q',
'_i',
'_m',
'_f',
'_k',
'_b',
'_v',
'_e',
'_u',
'_g'
]
names.forEach(name => {
helpers[name] = vue._renderProxy[name]
})
return helpers
}

function validateEnvironment (): void {
if (window.navigator.userAgent.match(/PhantomJS/i)) {
throwError(
`the scopedSlots option does not support PhantomJS. ` +
`Please use Puppeteer, or pass a component.`
)
}
if (vueVersion < 2.5) {
throwError(`the scopedSlots option is only supported in ` + `[email protected]+.`)
}
}

function validateTempldate (template: string): void {
if (template.trim().substr(0, 9) === '<template') {
throwError(
`the scopedSlots option does not support a template ` +
`tag as the root element.`
)
}
}

export default function createScopedSlots (
scopedSlotsOption: ?{ [slotName: string]: string }
): { [slotName: string]: (props: Object) => VNode | Array<VNode>} {
const scopedSlots = {}
if (!scopedSlotsOption) {
return scopedSlots
}
validateEnvironment()
const helpers = getVueTemplateCompilerHelpers()
for (const name in scopedSlotsOption) {
const template = scopedSlotsOption[name]
validateTempldate(template)
const render = compileToFunctions(template).render
const domParser = new window.DOMParser()
const _document = domParser.parseFromString(template, 'text/html')
const slotScope = _document.body.firstChild.getAttribute(
'slot-scope'
)
const isDestructuring = isDestructuringSlotScope(slotScope)
scopedSlots[name] = function (props) {
if (isDestructuring) {
return render.call({ ...helpers, ...props })
} else {
return render.call({ ...helpers, [slotScope]: props })
}
}
}
return scopedSlots
}
92 changes: 0 additions & 92 deletions packages/test-utils/src/add-scoped-slots.js

This file was deleted.

11 changes: 0 additions & 11 deletions packages/test-utils/src/mount.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ import { findAllVueComponentsFromVm } from './find-vue-components'
import { mergeOptions } from 'shared/merge-options'
import config from './config'
import warnIfNoWindow from './warn-if-no-window'
import { addScopedSlots } from './add-scoped-slots'

Vue.config.productionTip = false
Vue.config.devtools = false
Expand Down Expand Up @@ -46,16 +45,6 @@ export default function mount (
// Workaround for Vue < 2.5
vm._staticTrees = []

if (options.scopedSlots) {
addScopedSlots(vm, options.scopedSlots)

if (mergedOptions.sync) {
vm._watcher.sync = true
}

vm.$forceUpdate()
}

const componentsWithError = findAllVueComponentsFromVm(vm).filter(
c => c._error
)
Expand Down
42 changes: 42 additions & 0 deletions test/specs/mounting-options/scopedSlots.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,48 @@ describeWithShallowAndMount('scopedSlots', mountingMethod => {
window = windowSave // eslint-disable-line no-native-reassign
})

itDoNotRunIf(
vueVersion < 2.5 || isRunningPhantomJS,
'mounts component scoped slots in render function',
() => {
const destructuringWrapper = mountingMethod(
{
render: function () {
return this.$scopedSlots.default({
index: 1,
item: 'foo'
})
}
},
{
scopedSlots: {
default:
'<p slot-scope="{ index, item }">{{index}},{{item}}</p>'
}
}
)
expect(destructuringWrapper.html()).to.equal('<p>1,foo</p>')

const notDestructuringWrapper = mountingMethod(
{
render: function () {
return this.$scopedSlots.default({
index: 1,
item: 'foo'
})
}
},
{
scopedSlots: {
default:
'<p slot-scope="props">{{props.index}},{{props.item}}</p>'
}
}
)
expect(notDestructuringWrapper.html()).to.equal('<p>1,foo</p>')
}
)

itDoNotRunIf(
vueVersion < 2.5 || isRunningPhantomJS,
'mounts component scoped slots',
Expand Down

0 comments on commit b946997

Please sign in to comment.