From 3850ebef08c1ea9345b0623fee7dcfe3baa5ec63 Mon Sep 17 00:00:00 2001 From: dobromir-hristov Date: Mon, 4 May 2020 12:06:49 +0300 Subject: [PATCH 01/10] fix: initial scoped slot work --- src/mount.ts | 8 +- tests/components/ComponentWithSlots.vue | 19 ++++ tests/mountingOptions/slots.spec.ts | 137 +++++++++++++++--------- 3 files changed, 111 insertions(+), 53 deletions(-) diff --git a/src/mount.ts b/src/mount.ts index ba0467f41..523fa17d6 100644 --- a/src/mount.ts +++ b/src/mount.ts @@ -26,6 +26,7 @@ import { MOUNT_PARENT_NAME } from './constants' import { stubComponents } from './stubs' +import { compile } from '@vue/compiler-dom' type Slot = VNode | string | { render: Function } @@ -103,7 +104,12 @@ export function mount( return acc } - acc[name] = () => slot + if (typeof slot === 'function') { + acc[name] = slot + return acc + } + + acc[name] = () => compile(slot) return acc }, {}) diff --git a/tests/components/ComponentWithSlots.vue b/tests/components/ComponentWithSlots.vue index d2c01a434..129964584 100644 --- a/tests/components/ComponentWithSlots.vue +++ b/tests/components/ComponentWithSlots.vue @@ -1,5 +1,24 @@ ` + } + const { code } = compile( `${template}`, { diff --git a/tests/mountingOptions/slots.spec.ts b/tests/mountingOptions/slots.spec.ts index d38b577b2..f96f06c58 100644 --- a/tests/mountingOptions/slots.spec.ts +++ b/tests/mountingOptions/slots.spec.ts @@ -1,81 +1,83 @@ -import { defineComponent, h } from 'vue' +import { h } from 'vue' import { mount } from '../../src' -import WithProps from '../components/WithProps.vue' +import Hello from '../components/Hello.vue' import ComponentWithSlots from '../components/ComponentWithSlots.vue' describe('slots', () => { describe('normal slots', () => { - it('supports default slot', () => { - const ItemWithSlots = defineComponent({ - name: 'ItemWithSlots', - render() { - return h('div', {}, this.$slots.default()) - } - }) - - const wrapper = mount(ItemWithSlots, { + it('supports providing a plain string text in slot', () => { + const defaultString = 'Rendered in Default' + let namedString = 'Rendered in Named' + const wrapper = mount(ComponentWithSlots, { slots: { - default: h('span', {}, 'Default Slot') + default: defaultString, + named: namedString } }) - - expect(wrapper.html()).toBe('
Default Slot
') + expect(wrapper.find('.default').text()).toBe(defaultString) + expect(wrapper.find('.named').text()).toBe(namedString) }) - it('supports named slots', () => { - const ItemWithNamedSlot = defineComponent({ - render() { - return h('div', {}, this.$slots.foo()) - } - }) + it('supports providing an html string into a slot', () => { + const defaultSlot = '

Content

' + const namedSlot = '

Content

' - const wrapper = mount(ItemWithNamedSlot, { + const wrapper = mount(ComponentWithSlots, { slots: { - foo: h('span', {}, 'Foo') + default: defaultSlot, + named: namedSlot } }) - expect(wrapper.html()).toBe('
Foo
') + expect(wrapper.find('.defaultNested').exists()).toBe(true) + expect(wrapper.find('.namedNested').exists()).toBe(true) }) - it('supports default and named slots together', () => { - const Component = defineComponent({ - render() { - return h('div', {}, [ - // h('div', {}, this.$slots.foo()), - h('div', {}, this.$slots.default()) - ]) + it('supports providing a render function to slot', () => { + const wrapper = mount(ComponentWithSlots, { + slots: { + default: h('span', {}, 'Default'), + named: h('span', {}, 'Named') } }) - const wrapper = mount(Component, { + expect(wrapper.find('.default').html()).toEqual( + '
Default
' + ) + expect(wrapper.find('.named').html()).toEqual( + '
Named
' + ) + }) + + it('does not render slots that do not exist', () => { + const wrapper = mount(ComponentWithSlots, { slots: { - default: 'Default' - // foo: h('h1', {}, 'Named Slot') + notExisting: h('span', {}, 'NotExistingText') } }) - expect(wrapper.html()).toBe( - '

Named Slot

Default
' - ) + expect(wrapper.text()).not.toContain('NotExistingText') }) it('supports passing a SFC', () => { - const wrapper = mount( - { - template: `
` - }, - { - slots: { - foo: WithProps - } + const wrapper = mount(ComponentWithSlots, { + slots: { + named: Hello } - ) + }) - expect(wrapper.html()).toBe('

Hello

') + expect(wrapper.find('.named').html()).toBe( + '' + + '
' + + '
' + + '
' + + '
' + + '
' + ) }) }) + describe('scoped slots', () => { it('allows providing a plain text string', () => { const wrapper = mount(ComponentWithSlots, { @@ -89,21 +91,53 @@ describe('slots', () => { it('allows passing a function that returns a render function', () => { const wrapper = mount(ComponentWithSlots, { slots: { - scoped: (params) => h('div', {}, 'foo') + scoped: (params) => h('div', {}, JSON.stringify(params)) } }) - expect(wrapper.find('.scoped').text()).toEqual('Just a plain string') + expect(wrapper.find('.scoped').text()).toEqual( + '{"boolean":true,"string":"string","object":{"foo":"foo"}}' + ) }) - it('allows passing a scoped slot with params', () => { + it('allows passing a scoped slot via string with no destructuring using the # syntax', () => { const wrapper = mount(ComponentWithSlots, { slots: { - scoped: `` + scoped: `` } }) - expect(wrapper.find('.scoped').text()).toEqual('Just a plain string') + expect(wrapper.find('.scoped').text()).toEqual('Just a plain true string') + }) + + it('allows passing a scoped slot via a string with destructuring using the # syntax', () => { + const wrapper = mount(ComponentWithSlots, { + slots: { + scoped: `` + } + }) + + expect(wrapper.find('.scoped').text()).toEqual('Just a plain true string') + }) + + it('allows passing a scoped slot via string with no destructuring using the v-slot syntax ', () => { + const wrapper = mount(ComponentWithSlots, { + slots: { + scoped: `` + } + }) + + expect(wrapper.find('.scoped').text()).toEqual('Just a plain true string') + }) + + it('allows passing a scoped slot via string with no destructuring without template tag', () => { + const wrapper = mount(ComponentWithSlots, { + slots: { + scoped: `
Just a plain {{ params.boolean }} {{ params.string }}
` + } + }) + + expect(wrapper.find('.scoped').text()).toEqual('Just a plain true string') }) }) }) From 08c9d70ad0e7d505dcfed57d116807e1c5cf18f9 Mon Sep 17 00:00:00 2001 From: dobromir-hristov Date: Sat, 9 May 2020 09:49:30 +0300 Subject: [PATCH 05/10] chore: add extra test --- tests/mountingOptions/slots.spec.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/tests/mountingOptions/slots.spec.ts b/tests/mountingOptions/slots.spec.ts index f96f06c58..ba8019881 100644 --- a/tests/mountingOptions/slots.spec.ts +++ b/tests/mountingOptions/slots.spec.ts @@ -100,6 +100,26 @@ describe('slots', () => { ) }) + it('allows passing a function to store variables for assertion', () => { + let assertParams + + const wrapper = mount(ComponentWithSlots, { + slots: { + scoped: (params) => { + assertParams = params + // always return something + return 'foo' + } + } + }) + + expect(assertParams).toEqual({ + boolean: true, + string: 'string', + object: { foo: 'foo' } + }) + }) + it('allows passing a scoped slot via string with no destructuring using the # syntax', () => { const wrapper = mount(ComponentWithSlots, { slots: { From 5ef57f9301a52bf267efd6336d1aa7423d4ddc97 Mon Sep 17 00:00:00 2001 From: dobromir-hristov Date: Sat, 9 May 2020 09:52:27 +0300 Subject: [PATCH 06/10] chore: remove unnecessary param type --- src/utils/compileSlots.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/compileSlots.ts b/src/utils/compileSlots.ts index dda164409..142b41dd0 100644 --- a/src/utils/compileSlots.ts +++ b/src/utils/compileSlots.ts @@ -1,6 +1,6 @@ import { compile } from '@vue/compiler-dom' -export function processSlot(template: string, Vue = require('vue')) { +export function processSlot(template, Vue = require('vue')) { const hasWrappingTemplate = template.startsWith(' Date: Sat, 9 May 2020 09:56:55 +0300 Subject: [PATCH 07/10] chore: cleanup --- src/mount.ts | 2 +- src/utils/compileSlots.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mount.ts b/src/mount.ts index 34eb29feb..a9ac7fd60 100644 --- a/src/mount.ts +++ b/src/mount.ts @@ -116,7 +116,7 @@ export function mount( } // slot is most probably a scoped slot string or a plain string - acc[name] = (props) => h(processSlot(slot), props) + acc[name] = (props) => h(processSlot(slot as string), props) return acc }, {}) diff --git a/src/utils/compileSlots.ts b/src/utils/compileSlots.ts index 142b41dd0..c4bc97bc9 100644 --- a/src/utils/compileSlots.ts +++ b/src/utils/compileSlots.ts @@ -1,7 +1,7 @@ import { compile } from '@vue/compiler-dom' -export function processSlot(template, Vue = require('vue')) { - const hasWrappingTemplate = template.startsWith(' Date: Sat, 9 May 2020 10:02:02 +0300 Subject: [PATCH 08/10] chore: ensure slot is a string before compiling --- src/mount.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/mount.ts b/src/mount.ts index a9ac7fd60..3f0f8e6f6 100644 --- a/src/mount.ts +++ b/src/mount.ts @@ -115,9 +115,11 @@ export function mount( return acc } - // slot is most probably a scoped slot string or a plain string - acc[name] = (props) => h(processSlot(slot as string), props) - return acc + if (typeof slot === 'string') { + // slot is most probably a scoped slot string or a plain string + acc[name] = (props) => h(processSlot(slot), props) + return acc + } }, {}) // override component data with mounting options data From 97623ef7d25a19420f3e919fa7584f4bc1d0dd2f Mon Sep 17 00:00:00 2001 From: dobromir-hristov Date: Sat, 9 May 2020 10:08:23 +0300 Subject: [PATCH 09/10] chore: fix render func test --- src/mount.ts | 2 +- tests/mountingOptions/slots.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mount.ts b/src/mount.ts index 3f0f8e6f6..3d6d0aca6 100644 --- a/src/mount.ts +++ b/src/mount.ts @@ -110,7 +110,7 @@ export function mount( return acc } - if (typeof slot === 'object' && '_isVNode' in slot) { + if (typeof slot === 'object') { acc[name] = () => slot return acc } diff --git a/tests/mountingOptions/slots.spec.ts b/tests/mountingOptions/slots.spec.ts index ba8019881..b052383f6 100644 --- a/tests/mountingOptions/slots.spec.ts +++ b/tests/mountingOptions/slots.spec.ts @@ -53,7 +53,7 @@ describe('slots', () => { it('does not render slots that do not exist', () => { const wrapper = mount(ComponentWithSlots, { slots: { - notExisting: h('span', {}, 'NotExistingText') + notExisting: () => h('span', {}, 'NotExistingText') } }) From 3e195a38361d1443889bd293d4854b4b35808186 Mon Sep 17 00:00:00 2001 From: Lachlan Miller Date: Sun, 10 May 2020 11:46:21 +1000 Subject: [PATCH 10/10] chore: specify compiler-dom as external --- rollup.config.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rollup.config.js b/rollup.config.js index a330c22a6..7e4cd409b 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -25,6 +25,7 @@ function createEntry(options) { input, external: [ 'vue', + '@vue/compiler-dom', 'lodash/mergeWith', 'lodash/isString' ], @@ -41,6 +42,7 @@ function createEntry(options) { format, globals: { vue: 'Vue', + '@vue/compiler-dom': 'VueCompilerDOM', 'lodash/mergeWith': '_.mergeWith', 'lodash/isString': '_.isString', }