| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076 |
- import Vue from 'vue'
- describe('Component slot', () => {
- let vm, child
- function mount(options) {
- vm = new Vue({
- data: {
- msg: 'parent message'
- },
- template: `<div><test>${options.parentContent || ''}</test></div>`,
- components: {
- test: {
- template: options.childTemplate,
- data() {
- return {
- msg: 'child message'
- }
- }
- }
- }
- }).$mount()
- child = vm.$children[0]
- }
- it('no content', () => {
- mount({
- childTemplate: '<div><slot></slot></div>'
- })
- expect(child.$el.childNodes.length).toBe(0)
- })
- it('default slot', done => {
- mount({
- childTemplate: '<div><slot></slot></div>',
- parentContent: '<p>{{ msg }}</p>'
- })
- expect(child.$el.tagName).toBe('DIV')
- expect(child.$el.children[0].tagName).toBe('P')
- expect(child.$el.children[0].textContent).toBe('parent message')
- vm.msg = 'changed'
- waitForUpdate(() => {
- expect(child.$el.children[0].textContent).toBe('changed')
- }).then(done)
- })
- it('named slot', done => {
- mount({
- childTemplate: '<div><slot name="test"></slot></div>',
- parentContent: '<p slot="test">{{ msg }}</p>'
- })
- expect(child.$el.tagName).toBe('DIV')
- expect(child.$el.children[0].tagName).toBe('P')
- expect(child.$el.children[0].textContent).toBe('parent message')
- vm.msg = 'changed'
- waitForUpdate(() => {
- expect(child.$el.children[0].textContent).toBe('changed')
- }).then(done)
- })
- it('named slot with 0 as a number', done => {
- mount({
- childTemplate: '<div><slot :name="0"></slot></div>',
- parentContent: '<p :slot="0">{{ msg }}</p>'
- })
- expect(child.$el.tagName).toBe('DIV')
- expect(child.$el.children[0].tagName).toBe('P')
- expect(child.$el.children[0].textContent).toBe('parent message')
- vm.msg = 'changed'
- waitForUpdate(() => {
- expect(child.$el.children[0].textContent).toBe('changed')
- }).then(done)
- })
- it('fallback content', () => {
- mount({
- childTemplate: '<div><slot><p>{{msg}}</p></slot></div>'
- })
- expect(child.$el.children[0].tagName).toBe('P')
- expect(child.$el.textContent).toBe('child message')
- })
- it('fallback content with multiple named slots', () => {
- mount({
- childTemplate: `
- <div>
- <slot name="a"><p>fallback a</p></slot>
- <slot name="b">fallback b</slot>
- </div>
- `,
- parentContent: '<p slot="b">slot b</p>'
- })
- expect(child.$el.children.length).toBe(2)
- expect(child.$el.children[0].textContent).toBe('fallback a')
- expect(child.$el.children[1].textContent).toBe('slot b')
- })
- it('fallback content with mixed named/unnamed slots', () => {
- mount({
- childTemplate: `
- <div>
- <slot><p>fallback a</p></slot>
- <slot name="b">fallback b</slot>
- </div>
- `,
- parentContent: '<p slot="b">slot b</p>'
- })
- expect(child.$el.children.length).toBe(2)
- expect(child.$el.children[0].textContent).toBe('fallback a')
- expect(child.$el.children[1].textContent).toBe('slot b')
- })
- it('it should work with previous versions of the templates', () => {
- const Test = {
- render() {
- const _vm = this
- // const _h = _vm.$createElement;
- const _c = _vm._self._c || vm._h
- return _c(
- 'div',
- [_vm._t('default', [_c('p', [_vm._v('slot default')])])],
- 2
- )
- }
- }
- let vm = new Vue({
- template: `<test/>`,
- components: { Test }
- }).$mount()
- expect(vm.$el.textContent).toBe('slot default')
- vm = new Vue({
- template: `<test>custom content</test>`,
- components: { Test }
- }).$mount()
- expect(vm.$el.textContent).toBe('custom content')
- })
- it('fallback content should not be evaluated when the parent is providing it', () => {
- const test = vi.fn()
- const vm = new Vue({
- template: '<test>slot default</test>',
- components: {
- test: {
- template: '<div><slot>{{test()}}</slot></div>',
- methods: {
- test() {
- test()
- return 'test'
- }
- }
- }
- }
- }).$mount()
- expect(vm.$el.textContent).toBe('slot default')
- expect(test).not.toHaveBeenCalled()
- })
- it('selector matching multiple elements', () => {
- mount({
- childTemplate: '<div><slot name="t"></slot></div>',
- parentContent: '<p slot="t">1</p><div></div><p slot="t">2</p>'
- })
- expect(child.$el.innerHTML).toBe('<p>1</p><p>2</p>')
- })
- it('default content should only render parts not selected', () => {
- mount({
- childTemplate: `
- <div>
- <slot name="a"></slot>
- <slot></slot>
- <slot name="b"></slot>
- </div>
- `,
- parentContent: '<div>foo</div><p slot="a">1</p><p slot="b">2</p>'
- })
- expect(child.$el.innerHTML).toBe('<p>1</p> <div>foo</div> <p>2</p>')
- })
- it('name should only match children', function () {
- mount({
- childTemplate: `
- <div>
- <slot name="a"><p>fallback a</p></slot>
- <slot name="b"><p>fallback b</p></slot>
- <slot name="c"><p>fallback c</p></slot>
- </div>
- `,
- parentContent: `
- '<p slot="b">select b</p>
- '<span><p slot="b">nested b</p></span>
- '<span><p slot="c">nested c</p></span>
- `
- })
- expect(child.$el.children.length).toBe(3)
- expect(child.$el.children[0].textContent).toBe('fallback a')
- expect(child.$el.children[1].textContent).toBe('select b')
- expect(child.$el.children[2].textContent).toBe('fallback c')
- })
- it('should accept expressions in slot attribute and slot names', () => {
- mount({
- childTemplate: `<div><slot :name="'a'"></slot></div>`,
- parentContent: `<p>one</p><p :slot="'a'">two</p>`
- })
- expect(child.$el.innerHTML).toBe('<p>two</p>')
- })
- it('slot inside v-if', done => {
- const vm = new Vue({
- data: {
- a: 1,
- b: 2,
- show: true
- },
- template: '<test :show="show"><p slot="b">{{b}}</p><p>{{a}}</p></test>',
- components: {
- test: {
- props: ['show'],
- template: '<div v-if="show"><slot></slot><slot name="b"></slot></div>'
- }
- }
- }).$mount()
- expect(vm.$el.textContent).toBe('12')
- vm.a = 2
- waitForUpdate(() => {
- expect(vm.$el.textContent).toBe('22')
- vm.show = false
- })
- .then(() => {
- expect(vm.$el.textContent).toBe('')
- vm.show = true
- vm.a = 3
- })
- .then(() => {
- expect(vm.$el.textContent).toBe('32')
- })
- .then(done)
- })
- it('slot inside v-for', () => {
- mount({
- childTemplate: '<div><slot v-for="i in 3" :name="i"></slot></div>',
- parentContent: '<p v-for="i in 3" :slot="i">{{ i - 1 }}</p>'
- })
- expect(child.$el.innerHTML).toBe('<p>0</p><p>1</p><p>2</p>')
- })
- it('nested slots', done => {
- const vm = new Vue({
- template: '<test><test2><p>{{ msg }}</p></test2></test>',
- data: {
- msg: 'foo'
- },
- components: {
- test: {
- template: '<div><slot></slot></div>'
- },
- test2: {
- template: '<div><slot></slot></div>'
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<div><p>foo</p></div>')
- vm.msg = 'bar'
- waitForUpdate(() => {
- expect(vm.$el.innerHTML).toBe('<div><p>bar</p></div>')
- }).then(done)
- })
- it('v-if on inserted content', done => {
- const vm = new Vue({
- template: '<test><p v-if="ok">{{ msg }}</p></test>',
- data: {
- ok: true,
- msg: 'hi'
- },
- components: {
- test: {
- template: '<div><slot>fallback</slot></div>'
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<p>hi</p>')
- vm.ok = false
- waitForUpdate(() => {
- expect(vm.$el.innerHTML).toBe('fallback')
- vm.ok = true
- vm.msg = 'bye'
- })
- .then(() => {
- expect(vm.$el.innerHTML).toBe('<p>bye</p>')
- })
- .then(done)
- })
- it('template slot', function () {
- const vm = new Vue({
- template: '<test><template slot="test">hello</template></test>',
- components: {
- test: {
- template: '<div><slot name="test"></slot> world</div>'
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('hello world')
- })
- it('combined with v-for', () => {
- const vm = new Vue({
- template: '<div><test v-for="i in 3" :key="i">{{ i }}</test></div>',
- components: {
- test: {
- template: '<div><slot></slot></div>'
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<div>1</div><div>2</div><div>3</div>')
- })
- it('inside template v-if', () => {
- mount({
- childTemplate: `
- <div>
- <template v-if="true"><slot></slot></template>
- </div>
- `,
- parentContent: 'foo'
- })
- expect(child.$el.innerHTML).toBe('foo')
- })
- it('default slot should use fallback content if has only whitespace', () => {
- mount({
- childTemplate: `
- <div>
- <slot name="first"><p>first slot</p></slot>
- <slot><p>this is the default slot</p></slot>
- <slot name="second"><p>second named slot</p></slot>
- </div>
- `,
- parentContent: `<div slot="first">1</div> <div slot="second">2</div> <div slot="second">2+</div>`
- })
- expect(child.$el.innerHTML).toBe(
- '<div>1</div> <p>this is the default slot</p> <div>2</div><div>2+</div>'
- )
- })
- it('programmatic access to $slots', () => {
- const vm = new Vue({
- template: '<test><p slot="a">A</p><div>C</div><p slot="b">B</p></test>',
- components: {
- test: {
- render() {
- expect(this.$slots.a.length).toBe(1)
- expect(this.$slots.a[0].tag).toBe('p')
- expect(this.$slots.a[0].children.length).toBe(1)
- expect(this.$slots.a[0].children[0].text).toBe('A')
- expect(this.$slots.b.length).toBe(1)
- expect(this.$slots.b[0].tag).toBe('p')
- expect(this.$slots.b[0].children.length).toBe(1)
- expect(this.$slots.b[0].children[0].text).toBe('B')
- expect(this.$slots.default.length).toBe(1)
- expect(this.$slots.default[0].tag).toBe('div')
- expect(this.$slots.default[0].children.length).toBe(1)
- expect(this.$slots.default[0].children[0].text).toBe('C')
- return this.$slots.default[0]
- }
- }
- }
- }).$mount()
- expect(vm.$el.tagName).toBe('DIV')
- expect(vm.$el.textContent).toBe('C')
- })
- it('warn if user directly returns array', () => {
- new Vue({
- template: '<test><div slot="foo"></div><div slot="foo"></div></test>',
- components: {
- test: {
- render() {
- return this.$slots.foo
- }
- }
- }
- }).$mount()
- expect(
- 'Render function should return a single root node'
- ).toHaveBeenWarned()
- })
- // #3254
- it('should not keep slot name when passed further down', () => {
- const vm = new Vue({
- template: '<test><span slot="foo">foo</span></test>',
- components: {
- test: {
- template: '<child><slot name="foo"></slot></child>',
- components: {
- child: {
- template: `
- <div>
- <div class="default"><slot></slot></div>
- <div class="named"><slot name="foo"></slot></div>
- </div>
- `
- }
- }
- }
- }
- }).$mount()
- expect(vm.$el.querySelector('.default').textContent).toBe('foo')
- expect(vm.$el.querySelector('.named').textContent).toBe('')
- })
- it('should not keep slot name when passed further down (nested)', () => {
- const vm = new Vue({
- template: '<wrap><test><span slot="foo">foo</span></test></wrap>',
- components: {
- wrap: {
- template: '<div><slot></slot></div>'
- },
- test: {
- template: '<child><slot name="foo"></slot></child>',
- components: {
- child: {
- template: `
- <div>
- <div class="default"><slot></slot></div>
- <div class="named"><slot name="foo"></slot></div>
- </div>
- `
- }
- }
- }
- }
- }).$mount()
- expect(vm.$el.querySelector('.default').textContent).toBe('foo')
- expect(vm.$el.querySelector('.named').textContent).toBe('')
- })
- it('should not keep slot name when passed further down (functional)', () => {
- const child = {
- template: `
- <div>
- <div class="default"><slot></slot></div>
- <div class="named"><slot name="foo"></slot></div>
- </div>
- `
- }
- const vm = new Vue({
- template: '<test><span slot="foo">foo</span></test>',
- components: {
- test: {
- functional: true,
- render(h, ctx) {
- const slots = ctx.slots()
- return h(child, slots.foo)
- }
- }
- }
- }).$mount()
- expect(vm.$el.querySelector('.default').textContent).toBe('foo')
- expect(vm.$el.querySelector('.named').textContent).toBe('')
- })
- // #3400
- it('named slots should be consistent across re-renders', done => {
- const vm = new Vue({
- template: `
- <comp>
- <div slot="foo">foo</div>
- </comp>
- `,
- components: {
- comp: {
- data() {
- return { a: 1 }
- },
- template: `<div><slot name="foo"></slot>{{ a }}</div>`
- }
- }
- }).$mount()
- expect(vm.$el.textContent).toBe('foo1')
- vm.$children[0].a = 2
- waitForUpdate(() => {
- expect(vm.$el.textContent).toBe('foo2')
- }).then(done)
- })
- // #3437
- it('should correctly re-create components in slot', done => {
- const calls: any[] = []
- const vm = new Vue({
- template: `
- <comp ref="child">
- <div slot="foo">
- <child></child>
- </div>
- </comp>
- `,
- components: {
- comp: {
- data() {
- return { ok: true }
- },
- template: `<div><slot name="foo" v-if="ok"></slot></div>`
- },
- child: {
- template: '<div>child</div>',
- created() {
- calls.push(1)
- },
- destroyed() {
- calls.push(2)
- }
- }
- }
- }).$mount()
- expect(calls).toEqual([1])
- vm.$refs.child.ok = false
- waitForUpdate(() => {
- expect(calls).toEqual([1, 2])
- vm.$refs.child.ok = true
- })
- .then(() => {
- expect(calls).toEqual([1, 2, 1])
- vm.$refs.child.ok = false
- })
- .then(() => {
- expect(calls).toEqual([1, 2, 1, 2])
- })
- .then(done)
- })
- it('should support duplicate slots', done => {
- const vm = new Vue({
- template: `
- <foo ref="foo">
- <div slot="a">{{ n }}</div>
- </foo>
- `,
- data: {
- n: 1
- },
- components: {
- foo: {
- data() {
- return { ok: true }
- },
- template: `
- <div>
- <slot name="a" />
- <slot v-if="ok" name="a" />
- <pre><slot name="a" /></pre>
- </div>
- `
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe(
- `<div>1</div> <div>1</div> <pre><div>1</div></pre>`
- )
- vm.n++
- waitForUpdate(() => {
- expect(vm.$el.innerHTML).toBe(
- `<div>2</div> <div>2</div> <pre><div>2</div></pre>`
- )
- vm.n++
- })
- .then(() => {
- expect(vm.$el.innerHTML).toBe(
- `<div>3</div> <div>3</div> <pre><div>3</div></pre>`
- )
- vm.$refs.foo.ok = false
- })
- .then(() => {
- expect(vm.$el.innerHTML).toBe(
- `<div>3</div> <!----> <pre><div>3</div></pre>`
- )
- vm.n++
- vm.$refs.foo.ok = true
- })
- .then(() => {
- expect(vm.$el.innerHTML).toBe(
- `<div>4</div> <div>4</div> <pre><div>4</div></pre>`
- )
- })
- .then(done)
- })
- // #3518
- it('events should not break when slot is toggled by v-if', done => {
- const spy = vi.fn()
- const vm = new Vue({
- template: `<test><div class="click" @click="test">hi</div></test>`,
- methods: {
- test: spy
- },
- components: {
- test: {
- data: () => ({
- toggle: true
- }),
- template: `<div v-if="toggle"><slot></slot></div>`
- }
- }
- }).$mount()
- document.body.appendChild(vm.$el)
- expect(vm.$el.textContent).toBe('hi')
- vm.$children[0].toggle = false
- waitForUpdate(() => {
- vm.$children[0].toggle = true
- })
- .then(() => {
- global.triggerEvent(vm.$el.querySelector('.click'), 'click')
- expect(spy).toHaveBeenCalled()
- })
- .then(() => {
- document.body.removeChild(vm.$el)
- })
- .then(done)
- })
- it('renders static tree with text', () => {
- const vm = new Vue({
- template: `<div><test><template><div></div>Hello<div></div></template></test></div>`,
- components: {
- test: {
- template: '<div><slot></slot></div>'
- }
- }
- })
- vm.$mount()
- expect('Error when rendering root').not.toHaveBeenWarned()
- })
- // #3872
- it('functional component as slot', () => {
- const vm = new Vue({
- template: `
- <parent>
- <child>one</child>
- <child slot="a">two</child>
- </parent>
- `,
- components: {
- parent: {
- template: `<div><slot name="a"></slot><slot></slot></div>`
- },
- child: {
- functional: true,
- render(h, { slots }) {
- return h('div', slots().default)
- }
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML.trim()).toBe('<div>two</div><div>one</div>')
- })
- // #4209
- it('slot of multiple text nodes should not be infinitely merged', done => {
- const wrap = {
- template: `<inner ref="inner">foo<slot></slot></inner>`,
- components: {
- inner: {
- data: () => ({ a: 1 }),
- template: `<div>{{a}}<slot></slot></div>`
- }
- }
- }
- const vm = new Vue({
- template: `<wrap ref="wrap">bar</wrap>`,
- components: { wrap }
- }).$mount()
- expect(vm.$el.textContent).toBe('1foobar')
- vm.$refs.wrap.$refs.inner.a++
- waitForUpdate(() => {
- expect(vm.$el.textContent).toBe('2foobar')
- }).then(done)
- })
- // #4315
- it('functional component passing slot content to stateful child component', done => {
- const ComponentWithSlots = {
- render(h) {
- return h('div', this.$slots.slot1)
- }
- }
- const FunctionalComp = {
- functional: true,
- render(h) {
- return h(ComponentWithSlots, [h('span', { slot: 'slot1' }, 'foo')])
- }
- }
- const vm = new Vue({
- data: { n: 1 },
- render(h) {
- return h('div', [this.n, h(FunctionalComp)])
- }
- }).$mount()
- expect(vm.$el.textContent).toBe('1foo')
- vm.n++
- waitForUpdate(() => {
- // should not lose named slot
- expect(vm.$el.textContent).toBe('2foo')
- }).then(done)
- })
- it('the elements of slot should be updated correctly', done => {
- const vm = new Vue({
- data: { n: 1 },
- template:
- '<div><test><span v-for="i in n" :key="i">{{ i }}</span><input value="a"/></test></div>',
- components: {
- test: {
- template: '<div><slot></slot></div>'
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<div><span>1</span><input value="a"></div>')
- const input = vm.$el.querySelector('input')
- input.value = 'b'
- vm.n++
- waitForUpdate(() => {
- expect(vm.$el.innerHTML).toBe(
- '<div><span>1</span><span>2</span><input value="a"></div>'
- )
- expect(vm.$el.querySelector('input')).toBe(input)
- expect(vm.$el.querySelector('input').value).toBe('b')
- }).then(done)
- })
- // GitHub issue #5888
- it('should resolve correctly slot with keep-alive', () => {
- const vm = new Vue({
- template: `
- <div>
- <container>
- <keep-alive slot="foo">
- <child></child>
- </keep-alive>
- </container>
- </div>
- `,
- components: {
- container: {
- template:
- '<div><slot>default</slot><slot name="foo">named</slot></div>'
- },
- child: {
- template: '<span>foo</span>'
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe('<div>default<span>foo</span></div>')
- })
- // #6372, #6915
- it('should handle nested components in slots properly', done => {
- const TestComponent = {
- template: `
- <component :is="toggleEl ? 'b' : 'i'">
- <slot />
- </component>
- `,
- data() {
- return {
- toggleEl: true
- }
- }
- }
- const vm = new Vue({
- template: `
- <div>
- <test-component ref="test">
- <div>
- <foo/>
- </div>
- <bar>
- <foo/>
- </bar>
- </test-component>
- </div>
- `,
- components: {
- TestComponent,
- foo: {
- template: `<div>foo</div>`
- },
- bar: {
- template: `<div>bar<slot/></div>`
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe(
- `<b><div><div>foo</div></div> <div>bar<div>foo</div></div></b>`
- )
- vm.$refs.test.toggleEl = false
- waitForUpdate(() => {
- expect(vm.$el.innerHTML).toBe(
- `<i><div><div>foo</div></div> <div>bar<div>foo</div></div></i>`
- )
- }).then(done)
- })
- it('should preserve slot attribute if not absorbed by a Vue component', () => {
- const vm = new Vue({
- template: `
- <div>
- <div slot="foo"></div>
- </div>
- `
- }).$mount()
- expect(vm.$el.children[0].getAttribute('slot')).toBe('foo')
- })
- it('passing a slot down as named slot', () => {
- const Bar = {
- template: `<div class="bar"><slot name="foo"/></div>`
- }
- const Foo = {
- components: { Bar },
- template: `<div class="foo"><bar><slot slot="foo"/></bar></div>`
- }
- const vm = new Vue({
- components: { Foo },
- template: `<div><foo>hello</foo></div>`
- }).$mount()
- expect(vm.$el.innerHTML).toBe(
- '<div class="foo"><div class="bar">hello</div></div>'
- )
- })
- it('fallback content for named template slot', () => {
- const Bar = {
- template: `<div class="bar"><slot name="foo">fallback</slot></div>`
- }
- const Foo = {
- components: { Bar },
- template: `<div class="foo"><bar><template slot="foo"/><slot/></template></bar></div>`
- }
- const vm = new Vue({
- components: { Foo },
- template: `<div><foo></foo></div>`
- }).$mount()
- expect(vm.$el.innerHTML).toBe(
- '<div class="foo"><div class="bar">fallback</div></div>'
- )
- })
- // #7106
- it('should not lose functional slot across renders', done => {
- const One = {
- data: () => ({
- foo: true
- }),
- render(h) {
- this.foo
- return h('div', this.$slots.slot)
- }
- }
- const Two = {
- render(h) {
- return h('span', this.$slots.slot)
- }
- }
- const Three = {
- functional: true,
- render: (h, { children }) => h('span', children)
- }
- const vm = new Vue({
- template: `
- <div>
- <one ref="one">
- <two slot="slot">
- <three slot="slot">hello</three>
- </two>
- </one>
- </div>
- `,
- components: { One, Two, Three }
- }).$mount()
- expect(vm.$el.textContent).toBe('hello')
- // trigger re-render of <one>
- vm.$refs.one.foo = false
- waitForUpdate(() => {
- // should still be there
- expect(vm.$el.textContent).toBe('hello')
- }).then(done)
- })
- it('should allow passing named slots as raw children down multiple layers of functional component', () => {
- const CompB = {
- functional: true,
- render(h, { slots }) {
- return slots().foo
- }
- }
- const CompA = {
- functional: true,
- render(h, { children }) {
- return h(CompB, children)
- }
- }
- const vm = new Vue({
- components: {
- CompA
- },
- template: `
- <div>
- <comp-a>
- <span slot="foo">foo</span>
- </comp-a>
- </div>
- `
- }).$mount()
- expect(vm.$el.textContent).toBe('foo')
- })
- // #7817
- it('should not match wrong named slot in functional component on re-render', done => {
- const Functional = {
- functional: true,
- render: (h, ctx) => ctx.slots().default
- }
- const Stateful = {
- data() {
- return { ok: true }
- },
- render(h) {
- this.ok // register dep
- return h('div', [h(Functional, this.$slots.named)])
- }
- }
- const vm = new Vue({
- template: `<stateful ref="stateful"><div slot="named">foo</div></stateful>`,
- components: { Stateful }
- }).$mount()
- expect(vm.$el.textContent).toBe('foo')
- vm.$refs.stateful.ok = false
- waitForUpdate(() => {
- expect(vm.$el.textContent).toBe('foo')
- }).then(done)
- })
- // #7975
- it('should update named slot correctly when its position in the tree changed', done => {
- const ChildComponent = {
- template: '<b>{{ message }}</b>',
- props: ['message']
- }
- let parentVm
- const ParentComponent = {
- template: `
- <div>
- <span v-if="alter">
- <span><slot name="foo" /></span>
- </span>
- <span v-else>
- <slot name="foo" />
- </span>
- </div>
- `,
- data() {
- return {
- alter: true
- }
- },
- mounted() {
- parentVm = this
- }
- }
- const vm = new Vue({
- template: `
- <parent-component>
- <span slot="foo">
- <child-component :message="message" />
- </span>
- </parent-component>
- `,
- components: {
- ChildComponent,
- ParentComponent
- },
- data() {
- return {
- message: 1
- }
- }
- }).$mount()
- expect(vm.$el.firstChild.innerHTML).toBe(
- '<span><span><b>1</b></span></span>'
- )
- parentVm.alter = false
- waitForUpdate(() => {
- vm.message = 2
- })
- .then(() => {
- expect(vm.$el.firstChild.innerHTML).toBe('<span><b>2</b></span>')
- })
- .then(done)
- })
- // #12102
- it('v-if inside scoped slot', () => {
- const vm = new Vue({
- template: `<test><template #custom><span v-if="false">a</span><span>b</span></template></test>`,
- components: {
- test: {
- template: `<div><slot name="custom"/></div>`
- }
- }
- }).$mount()
- expect(vm.$el.innerHTML).toBe(`<!----><span>b</span>`)
- })
- // regression 2.7.0-alpha.4
- it('passing scoped slots through nested parent chain', () => {
- const Foo = {
- template: `
- <div><slot>foo default</slot></div>
- `
- }
- const Bar = {
- components: { Foo },
- template: `<Foo><slot name="bar"/></Foo>`
- }
- const App = {
- components: { Bar },
- template: `<Bar>
- <template #bar>
- <span>App content for Bar#bar</span>
- </template>
- </Bar>`
- }
- const vm = new Vue({
- render: h => h(App)
- }).$mount()
- expect(vm.$el.innerHTML).toMatch(`App content for Bar#bar`)
- })
- })
|