import Vue from 'vue' describe('Component scoped slot', () => { it('default slot', done => { const vm = new Vue({ template: ` `, components: { test: { data() { return { msg: 'hello' } }, template: `
` } } }).$mount() expect(vm.$el.innerHTML).toBe('hello') vm.$refs.test.msg = 'world' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('world') }).then(done) }) it('default slot (plain element)', done => { const vm = new Vue({ template: ` {{ props.msg }} `, components: { test: { data() { return { msg: 'hello' } }, template: `
` } } }).$mount() expect(vm.$el.innerHTML).toBe('hello') vm.$refs.test.msg = 'world' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('world') }).then(done) }) it('with v-bind', done => { const vm = new Vue({ template: ` `, components: { test: { data() { return { msg: 'hello', obj: { msg2: 'world', msg3: '.' } } }, template: `
` } } }).$mount() expect(vm.$el.innerHTML).toBe('hello world !') vm.$refs.test.msg = 'bye' vm.$refs.test.obj.msg2 = 'bye' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('bye bye !') }).then(done) }) it('should warn when using v-bind with no object', () => { new Vue({ template: ` `, components: { test: { data() { return { text: 'some text' } }, template: `
` } } }).$mount() expect('slot v-bind without argument expects an Object').toHaveBeenWarned() }) it('should not warn when using v-bind with object', () => { new Vue({ template: ` `, components: { test: { data() { return { foo: { text: 'some text' } } }, template: `
` } } }).$mount() expect( 'slot v-bind without argument expects an Object' ).not.toHaveBeenWarned() }) it('named scoped slot', done => { const vm = new Vue({ template: ` `, components: { test: { data() { return { foo: 'FOO', bar: 'BAR' } }, template: `
` } } }).$mount() expect(vm.$el.innerHTML).toBe('FOOBAR') vm.$refs.test.foo = 'BAZ' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('BAZBAR') }).then(done) }) it('named scoped slot (plain element)', done => { const vm = new Vue({ template: ` {{ props.foo }} {{ props.bar }} `, components: { test: { data() { return { foo: 'FOO', bar: 'BAR' } }, template: `
` } } }).$mount() expect(vm.$el.innerHTML).toBe('FOO BAR') vm.$refs.test.foo = 'BAZ' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('BAZ BAR') }).then(done) }) it('fallback content', () => { const vm = new Vue({ template: ``, components: { test: { data() { return { msg: 'hello' } }, template: `
{{ msg }} fallback
` } } }).$mount() expect(vm.$el.innerHTML).toBe('hello fallback') }) it('slot with v-for', done => { const vm = new Vue({ template: ` `, components: { test: { data() { return { items: ['foo', 'bar', 'baz'] } }, template: `
` } } }).$mount() function assertOutput() { expect(vm.$el.innerHTML).toBe( vm.$refs.test.items .map(item => { return `${item}` }) .join('') ) } assertOutput() vm.$refs.test.items.reverse() waitForUpdate(assertOutput) .then(() => { vm.$refs.test.items.push('qux') }) .then(assertOutput) .then(done) }) it('slot inside v-for', done => { const vm = new Vue({ template: ` `, components: { test: { data() { return { items: ['foo', 'bar', 'baz'] } }, template: ` ` } } }).$mount() function assertOutput() { expect(vm.$el.innerHTML).toBe( vm.$refs.test.items .map(item => { return `
  • ${item}
  • ` }) .join('') ) } assertOutput() vm.$refs.test.items.reverse() waitForUpdate(assertOutput) .then(() => { vm.$refs.test.items.push('qux') }) .then(assertOutput) .then(done) }) it('scoped slot without scope alias', () => { const vm = new Vue({ template: ` I am static `, components: { test: { data() { return { msg: 'hello' } }, template: `
    ` } } }).$mount() expect(vm.$el.innerHTML).toBe('I am static') }) it('non-scoped slot with scope alias', () => { const vm = new Vue({ template: ` `, components: { test: { data() { return { msg: 'hello' } }, template: `
    ` } } }).$mount() expect(vm.$el.innerHTML).toBe('meh') }) it('warn key on slot', () => { new Vue({ template: ` `, components: { test: { data() { return { items: ['foo', 'bar', 'baz'] } }, template: `
    ` } } }).$mount() expect(`\`key\` does not work on `).toHaveBeenWarned() }) it('render function usage (named, via data)', done => { const vm = new Vue({ render(h) { return h('test', { ref: 'test', scopedSlots: { item: props => h('span', props.text) } }) }, components: { test: { data() { return { msg: 'hello' } }, render(h) { return h( 'div', this.$scopedSlots.item({ text: this.msg }) ) } } } }).$mount() expect(vm.$el.innerHTML).toBe('hello') vm.$refs.test.msg = 'world' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('world') }).then(done) }) it('render function usage (default, as children)', () => { const vm = new Vue({ render(h) { return h('test', [props => h('span', [props.msg])]) }, components: { test: { data() { return { msg: 'hello' } }, render(h) { return h('div', this.$scopedSlots.default({ msg: this.msg })) } } } }).$mount() expect(vm.$el.innerHTML).toBe('hello') }) it('render function usage (default, as root)', () => { const vm = new Vue({ render(h) { return h('test', [props => h('span', [props.msg])]) }, components: { test: { data() { return { msg: 'hello' } }, render(h) { const res = this.$scopedSlots.default({ msg: this.msg }) // all scoped slots should be normalized into arrays expect(Array.isArray(res)).toBe(true) return res } } } }).$mount() expect(vm.$el.outerHTML).toBe('hello') }) // new in 2.6, unifying all slots as functions it('non-scoped slots should also be available on $scopedSlots', () => { const vm = new Vue({ template: `before
    {{ scope.msg }}
    after
    `, components: { foo: { render(h) { return h('div', [ this.$scopedSlots.default(), this.$scopedSlots.bar({ msg: 'hi' }) ]) } } } }).$mount() expect(vm.$el.innerHTML).toBe(`before after
    hi
    `) }) // #4779 it('should support dynamic slot target', done => { const Child = { template: `
    ` } const vm = new Vue({ data: { a: 'a', b: 'b' }, template: ` `, components: { Child } }).$mount() expect(vm.$el.textContent.trim()).toBe('A a B b') // switch slots vm.a = 'b' vm.b = 'a' waitForUpdate(() => { expect(vm.$el.textContent.trim()).toBe('B a A b') }).then(done) }) // it('render function usage (JSX)', () => { // const vm = new Vue({ // render (h) { // return ({ // props => {props.msg} // }) // }, // components: { // test: { // data () { // return { msg: 'hello' } // }, // render (h) { // return
    // {this.$scopedSlots.default({ msg: this.msg })} //
    // } // } // } // }).$mount() // expect(vm.$el.innerHTML).toBe('hello') // }) // #5615 it('scoped slot with v-for', done => { const vm = new Vue({ data: { names: ['foo', 'bar'] }, template: ` `, components: { test: { data: () => ({ msg: 'hello' }), template: `
    ` } } }).$mount() expect(vm.$el.innerHTML).toBe( 'hello foo hello bar hello abc' ) vm.$refs.test.msg = 'world' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( 'world foo world bar world abc' ) }).then(done) }) it('scoped slot with v-for (plain elements)', done => { const vm = new Vue({ data: { names: ['foo', 'bar'] }, template: ` {{ props.msg }} {{ props.msg }} `, components: { test: { data: () => ({ msg: 'hello' }), template: `
    ` } } }).$mount() expect(vm.$el.innerHTML).toBe( 'hello foo hello bar hello abc' ) vm.$refs.test.msg = 'world' waitForUpdate(() => { expect(vm.$el.innerHTML).toBe( 'world foo world bar world abc' ) }).then(done) }) // #6725 it('scoped slot with v-if', done => { const vm = new Vue({ data: { ok: false }, template: ` `, components: { test: { data() { return { msg: 'hello' } }, template: `
    {{ msg }} fallback
    ` } } }).$mount() expect(vm.$el.innerHTML).toBe('hello fallback') vm.ok = true waitForUpdate(() => { expect(vm.$el.innerHTML).toBe('

    hello

    ') }).then(done) }) // #9422 // the behavior of the new syntax is slightly different. it('scoped slot v-if using slot-scope value', () => { const Child = { template: '
    ' } const vm = new Vue({ components: { Child }, template: ` ` }).$mount() expect(vm.$el.textContent).toMatch(`foo foo`) }) // 2.6 new slot syntax describe('v-slot syntax', () => { const Foo = { render(h) { return h('div', [ this.$scopedSlots.default && this.$scopedSlots.default('from foo default'), this.$scopedSlots.one && this.$scopedSlots.one('from foo one'), this.$scopedSlots.two && this.$scopedSlots.two('from foo two') ]) } } const Bar = { render(h) { return ( this.$scopedSlots.default && this.$scopedSlots.default('from bar') ) } } const Baz = { render(h) { return ( this.$scopedSlots.default && this.$scopedSlots.default('from baz') ) } } const toNamed = (syntax, name) => syntax[0] === '#' ? `#${name}` // shorthand : `${syntax}:${name}` // full syntax function runSuite(syntax) { it('default slot', () => { const vm = new Vue({ template: `{{ foo }}
    {{ foo }}
    `, components: { Foo } }).$mount() expect(vm.$el.innerHTML).toBe( `from foo default
    from foo default
    ` ) }) it('nested default slots', () => { const vm = new Vue({ template: ` {{ foo }} | {{ bar }} | {{ baz }} `, components: { Foo, Bar, Baz } }).$mount() expect(vm.$el.innerHTML.trim()).toBe( `from foo default | from bar | from baz` ) }) it('named slots', () => { const vm = new Vue({ template: ` `, components: { Foo } }).$mount() expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch( `from foo default from foo one from foo two` ) }) it('nested + named + default slots', () => { const vm = new Vue({ template: ` `, components: { Foo, Bar, Baz } }).$mount() expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch( `from foo one from bar from foo two from baz` ) }) it('should warn v-slot usage on non-component elements', () => { new Vue({ template: `
    ` }).$mount() expect( `v-slot can only be used on components or