-
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
feat: add scoped slots option #507
Conversation
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.
Looks good! I don't like monkey patching the _t helper but we can look at changing that in the future. For now we can go ahead with this. I've added some comments
it('mounts component scoped slots', () => { | ||
const wrapper = mountingMethod(ComponentWithScopedSlots, { | ||
scopedSlots: { | ||
'item': '<p slot="item" slot-scope="props">{{props.index}},{{props.text}}</p>' |
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.
Do we need to include slot=item in the markup? We already identify it as the item slot in the scopedSlots object
packages/test-utils/types/index.d.ts
Outdated
@@ -113,6 +113,7 @@ interface MountOptions<V extends Vue> extends ComponentOptions<V> { | |||
localVue?: typeof Vue | |||
mocks?: object | |||
slots?: Slots | |||
scopedSlots?: string | |||
stubs?: Stubs, |
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.
This should be an object. Can you add a test for this in types/test?
|
||
describeWithShallowAndMount('scopedSlots', (mountingMethod) => { | ||
if (vueVersion >= 2.5) { | ||
it('mounts component scoped slots', () => { |
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.
We have an itIf helper function in resouces/utils. Could you use that instead of if else statement
Thank you for reviewing. |
packages/test-utils/types/index.d.ts
Outdated
@@ -113,6 +113,7 @@ interface MountOptions<V extends Vue> extends ComponentOptions<V> { | |||
localVue?: typeof Vue | |||
mocks?: object | |||
slots?: Slots | |||
scopedSlots?: object |
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.
We can specify more detailed type Record<string, string>
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.
Thank you for pointing out.
I changed it.
docs/en/api/options.md
Outdated
|
||
Provide an object of scoped slots contents to the component. The key corresponds to the slot name. The value can be a template string. | ||
|
||
There is two limitations. |
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.
There is two limitations > There are two limitations.
docs/en/api/options.md
Outdated
|
||
There is two limitations. | ||
|
||
* This supports [email protected]+. |
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.
This option is only supported in [email protected]+
docs/en/api/options.md
Outdated
|
||
* This supports [email protected]+. | ||
|
||
* You can not set a `template` tag to top of `scopedSlots` option. |
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.
You cannot use
<template>
tag as the root element in thescopedSlots
option.
Object.keys(scopedSlots).forEach((key) => { | ||
const template = scopedSlots[key].trim() | ||
if (template.substr(0, 9) === '<template') { | ||
throwError('scopedSlots option does not support template tag.') |
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.
Can the error message read:
the scopedSlots option does not support a tag as the root element.
vm._renderProxy._t = function (name, feedback, props, bindObject) { | ||
const scopedSlotFn = vm.$_vueTestUtils_scopedSlots[name] | ||
if (scopedSlotFn) { | ||
props = { ...bindObject, ...props } |
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 do we need to set vm._renderProxy.props?
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.
scopedSlotFn
needs vm._renderProxy.props
.
If vm._renderProxy.prop
is not setted, the render function can not use props
.
with(this){return _c('p',{},[_v(_s(props.index)+","+_s(props.text))])}
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.
There is a bug.
When slot-scope
is other than props
, this is not work.
Thank you for pointing out. |
There is a bug. |
proxy[key] = vm._renderProxy[key] | ||
} | ||
}) | ||
proxy[slotScope] = props |
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.
Since when slot name
and slot scope
is same, vm._renderProxy.[<slot name>]
is changed in _t
function, proxy
is created.
<ul>
<slot name="foo"
v-for="(item, index) in foo"
:text="item.text"
:index="index">
</slot>
</ul>
function render() {
with(this) {
return _c('ul', [_l((foo), function (item, index) {
return _t("foo", null, {
text: item.text,
index: index
})
})], 2)
}
}
scopedSlotFn
function anonymous() {
with(this){return _c('p',{},[_v(_s(foo.index)+","+_s(foo.text))])}
}
Looking forward to this being merged. Thanks for this. |
if (key[0] === '_') { | ||
proxy[key] = vm._renderProxy[key] | ||
} | ||
const helpers = ['_c', '_o', '_n', '_s', '_l', '_t', '_q', '_i', '_m', '_f', '_k', '_b', '_v', '_e', '_u', '_g'] |
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.
Object.keys(vm._renderProxy).concat(Object.keys(Vue.prototype)).forEach((key) => {
may be better,
since it is abstruct.
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.
Looking good, this adds some much needed support 👍 . I've added a couple of comments
throwError('the scopedSlots option does not support a template tag as the root element.') | ||
} | ||
const domParser = new window.DOMParser() | ||
const document = domParser.parseFromString(template, 'text/html') |
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.
Can you rename this variable to avoid collisions with window.document
?
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 changed it.
helpers.forEach((key) => { | ||
proxy[key] = vm._renderProxy[key] | ||
}) | ||
proxy[slotScope] = props |
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.
Does Vue throw an error if there are two conflicting slot scopes? If not, we should throw an error here
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.
It is impossible to set a word that starts with "_" in Vue.
[Vue warn]: Property or method "_foo" is not defined on the instance but referenced during render.
Make sure that this property is reactive, either in the data option, or for class-based components, by initializing the property.
See: https://vuejs.org/v2/guide/reactivity.html#Declaring-Reactive-Properties.
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 meant if you mounted with this option:
{
scopedSlots: {
foo: '<div />',
foo: '<input />'
}
}
The second scopedSlot would overwrite the first scopedSlots in _vueTestUtils_slotScopes
. Although thinking about it, this is probably fine.
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.
It is not possible to register the same key.
{ foo: 1, foo: 2 }
// => { foo: 2 }
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.
🤦♂️ Of course ;p
docs/en/api/options.md
Outdated
* You can not use `<template>` tag as the root element in the `scopedSlots` option. | ||
|
||
* This does not support PhantomJS. | ||
Please use [Puppeteer](https://github.com/karma-runner/karma-chrome-launcher#headless-chromium-with-puppeteer). |
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.
Can you change to:
You can use Puppeteer as an alternative.
Thank you for pointing out. |
@@ -61,7 +61,7 @@ export default function createInstance ( | |||
|
|||
if (options.scopedSlots) { | |||
if (window.navigator.userAgent.match(/PhantomJS/i)) { | |||
throwError('the scopedSlots option does not support strings in PhantomJS. Please use Puppeteer, or pass a component.') | |||
throwError('the scopedSlots option does not support in PhantomJS. Please use Puppeteer, or pass a component.') |
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.
the scopedSlots option does not support in PhantomJS. Please use Puppeteer, or pass a component.
to
the scopedSlots option does not support PhantomJS. Please use Puppeteer, or pass a component.
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 changed it.
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.
Great job 👍
@eddyerburgh |
@38elements, what was the reason |
This is related to #156 .
This adds
scopedSlots
option by replacing the renderSlot method.It is supported at [email protected]+.
It does not support
template
tag.