component-scoped-slot.spec.js 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. import Vue from 'vue'
  2. describe('Component scoped slot', () => {
  3. it('default slot', done => {
  4. const vm = new Vue({
  5. template: `
  6. <test ref="test">
  7. <template scope="props">
  8. <span>{{ props.msg }}</span>
  9. </template>
  10. </test>
  11. `,
  12. components: {
  13. test: {
  14. data () {
  15. return { msg: 'hello' }
  16. },
  17. template: `
  18. <div>
  19. <slot :msg="msg"></slot>
  20. </div>
  21. `
  22. }
  23. }
  24. }).$mount()
  25. expect(vm.$el.innerHTML).toBe('<span>hello</span>')
  26. vm.$refs.test.msg = 'world'
  27. waitForUpdate(() => {
  28. expect(vm.$el.innerHTML).toBe('<span>world</span>')
  29. }).then(done)
  30. })
  31. it('with v-bind', done => {
  32. const vm = new Vue({
  33. template: `
  34. <test ref="test">
  35. <template scope="props">
  36. <span>{{ props.msg }} {{ props.msg2 }}</span>
  37. </template>
  38. </test>
  39. `,
  40. components: {
  41. test: {
  42. data () {
  43. return {
  44. msg: 'hello',
  45. obj: { msg2: 'world' }
  46. }
  47. },
  48. template: `
  49. <div>
  50. <slot :msg="msg" v-bind="obj"></slot>
  51. </div>
  52. `
  53. }
  54. }
  55. }).$mount()
  56. expect(vm.$el.innerHTML).toBe('<span>hello world</span>')
  57. vm.$refs.test.msg = 'bye'
  58. vm.$refs.test.obj.msg2 = 'bye'
  59. waitForUpdate(() => {
  60. expect(vm.$el.innerHTML).toBe('<span>bye bye</span>')
  61. }).then(done)
  62. })
  63. it('template slot', done => {
  64. const vm = new Vue({
  65. template: `
  66. <test ref="test">
  67. <template slot="item" scope="props">
  68. <span>{{ props.foo }}</span><span>{{ props.bar }}</span>
  69. </template>
  70. </test>
  71. `,
  72. components: {
  73. test: {
  74. data () {
  75. return { foo: 'FOO', bar: 'BAR' }
  76. },
  77. template: `
  78. <div>
  79. <slot name="item" :foo="foo" :bar="bar"></slot>
  80. </div>
  81. `
  82. }
  83. }
  84. }).$mount()
  85. expect(vm.$el.innerHTML).toBe('<span>FOO</span><span>BAR</span>')
  86. vm.$refs.test.foo = 'BAZ'
  87. waitForUpdate(() => {
  88. expect(vm.$el.innerHTML).toBe('<span>BAZ</span><span>BAR</span>')
  89. }).then(done)
  90. })
  91. it('fallback content', () => {
  92. const vm = new Vue({
  93. template: `<test></test>`,
  94. components: {
  95. test: {
  96. data () {
  97. return { msg: 'hello' }
  98. },
  99. template: `
  100. <div>
  101. <slot name="item" :text="msg">
  102. <span>{{ msg }} fallback</span>
  103. </slot>
  104. </div>
  105. `
  106. }
  107. }
  108. }).$mount()
  109. expect(vm.$el.innerHTML).toBe('<span>hello fallback</span>')
  110. })
  111. it('slot with v-for', done => {
  112. const vm = new Vue({
  113. template: `
  114. <test ref="test">
  115. <template slot="item" scope="props">
  116. <span>{{ props.text }}</span>
  117. </template>
  118. </test>
  119. `,
  120. components: {
  121. test: {
  122. data () {
  123. return {
  124. items: ['foo', 'bar', 'baz']
  125. }
  126. },
  127. template: `
  128. <div>
  129. <slot v-for="item in items" name="item" :text="item"></slot>
  130. </div>
  131. `
  132. }
  133. }
  134. }).$mount()
  135. function assertOutput () {
  136. expect(vm.$el.innerHTML).toBe(vm.$refs.test.items.map(item => {
  137. return `<span>${item}</span>`
  138. }).join(''))
  139. }
  140. assertOutput()
  141. vm.$refs.test.items.reverse()
  142. waitForUpdate(assertOutput).then(() => {
  143. vm.$refs.test.items.push('qux')
  144. }).then(assertOutput).then(done)
  145. })
  146. it('slot inside v-for', done => {
  147. const vm = new Vue({
  148. template: `
  149. <test ref="test">
  150. <template slot="item" scope="props">
  151. <span>{{ props.text }}</span>
  152. </template>
  153. </test>
  154. `,
  155. components: {
  156. test: {
  157. data () {
  158. return {
  159. items: ['foo', 'bar', 'baz']
  160. }
  161. },
  162. template: `
  163. <ul>
  164. <li v-for="item in items">
  165. <slot name="item" :text="item"></slot>
  166. </li>
  167. </ul>
  168. `
  169. }
  170. }
  171. }).$mount()
  172. function assertOutput () {
  173. expect(vm.$el.innerHTML).toBe(vm.$refs.test.items.map(item => {
  174. return `<li><span>${item}</span></li>`
  175. }).join(''))
  176. }
  177. assertOutput()
  178. vm.$refs.test.items.reverse()
  179. waitForUpdate(assertOutput).then(() => {
  180. vm.$refs.test.items.push('qux')
  181. }).then(assertOutput).then(done)
  182. })
  183. it('scoped slot without scope alias', () => {
  184. const vm = new Vue({
  185. template: `
  186. <test ref="test">
  187. <span slot="item">I am static</span>
  188. </test>
  189. `,
  190. components: {
  191. test: {
  192. data () {
  193. return { msg: 'hello' }
  194. },
  195. template: `
  196. <div>
  197. <slot name="item" :text="msg"></slot>
  198. </div>
  199. `
  200. }
  201. }
  202. }).$mount()
  203. expect(vm.$el.innerHTML).toBe('<span>I am static</span>')
  204. })
  205. it('non-scoped slot with scope alias', () => {
  206. const vm = new Vue({
  207. template: `
  208. <test ref="test">
  209. <template slot="item" scope="props">
  210. <span>{{ props.text || 'meh' }}</span>
  211. </template>
  212. </test>
  213. `,
  214. components: {
  215. test: {
  216. data () {
  217. return { msg: 'hello' }
  218. },
  219. template: `
  220. <div>
  221. <slot name="item"></slot>
  222. </div>
  223. `
  224. }
  225. }
  226. }).$mount()
  227. expect(vm.$el.innerHTML).toBe('<span>meh</span>')
  228. })
  229. it('warn key on slot', () => {
  230. new Vue({
  231. template: `
  232. <test ref="test">
  233. <template slot="item" scope="props">
  234. <span>{{ props.text }}</span>
  235. </template>
  236. </test>
  237. `,
  238. components: {
  239. test: {
  240. data () {
  241. return {
  242. items: ['foo', 'bar', 'baz']
  243. }
  244. },
  245. template: `
  246. <div>
  247. <slot v-for="item in items" name="item" :text="item" :key="item"></slot>
  248. </div>
  249. `
  250. }
  251. }
  252. }).$mount()
  253. expect(`\`key\` does not work on <slot>`).toHaveBeenWarned()
  254. })
  255. it('render function usage (named, via data)', done => {
  256. const vm = new Vue({
  257. render (h) {
  258. return h('test', {
  259. ref: 'test',
  260. scopedSlots: {
  261. item: props => h('span', props.text)
  262. }
  263. })
  264. },
  265. components: {
  266. test: {
  267. data () {
  268. return { msg: 'hello' }
  269. },
  270. render (h) {
  271. return h('div', [
  272. this.$scopedSlots.item({
  273. text: this.msg
  274. })
  275. ])
  276. }
  277. }
  278. }
  279. }).$mount()
  280. expect(vm.$el.innerHTML).toBe('<span>hello</span>')
  281. vm.$refs.test.msg = 'world'
  282. waitForUpdate(() => {
  283. expect(vm.$el.innerHTML).toBe('<span>world</span>')
  284. }).then(done)
  285. })
  286. it('render function usage (default, as children)', () => {
  287. const vm = new Vue({
  288. render (h) {
  289. return h('test', [
  290. props => h('span', [props.msg])
  291. ])
  292. },
  293. components: {
  294. test: {
  295. data () {
  296. return { msg: 'hello' }
  297. },
  298. render (h) {
  299. return h('div', [
  300. this.$scopedSlots.default({ msg: this.msg })
  301. ])
  302. }
  303. }
  304. }
  305. }).$mount()
  306. expect(vm.$el.innerHTML).toBe('<span>hello</span>')
  307. })
  308. // #4779
  309. it('should support dynamic slot target', done => {
  310. const Child = {
  311. template: `
  312. <div>
  313. <slot name="a" msg="a" />
  314. <slot name="b" msg="b" />
  315. </div>
  316. `
  317. }
  318. const vm = new Vue({
  319. data: {
  320. a: 'a',
  321. b: 'b'
  322. },
  323. template: `
  324. <child>
  325. <template :slot="a" scope="props">A {{ props.msg }}</template>
  326. <template :slot="b" scope="props">B {{ props.msg }}</template>
  327. </child>
  328. `,
  329. components: { Child }
  330. }).$mount()
  331. expect(vm.$el.textContent.trim()).toBe('A a B b')
  332. // switch slots
  333. vm.a = 'b'
  334. vm.b = 'a'
  335. waitForUpdate(() => {
  336. expect(vm.$el.textContent.trim()).toBe('B a A b')
  337. }).then(done)
  338. })
  339. it('render function usage (JSX)', () => {
  340. const vm = new Vue({
  341. render (h) {
  342. return <test>{
  343. props => <span>{props.msg}</span>
  344. }</test>
  345. },
  346. components: {
  347. test: {
  348. data () {
  349. return { msg: 'hello' }
  350. },
  351. render (h) {
  352. return <div>
  353. {this.$scopedSlots.default({ msg: this.msg })}
  354. </div>
  355. }
  356. }
  357. }
  358. }).$mount()
  359. expect(vm.$el.innerHTML).toBe('<span>hello</span>')
  360. })
  361. // #5615
  362. it('scoped slot with v-for', done => {
  363. const vm = new Vue({
  364. data: { names: ['foo', 'bar'] },
  365. template: `
  366. <test ref="test">
  367. <template v-for="n in names" :slot="n" scope="props">
  368. <span>{{ props.msg }}</span>
  369. </template>
  370. <template slot="abc" scope="props">
  371. <span>{{ props.msg }}</span>
  372. </template>
  373. </test>
  374. `,
  375. components: {
  376. test: {
  377. data: () => ({ msg: 'hello' }),
  378. template: `
  379. <div>
  380. <slot name="foo" :msg="msg + ' foo'"></slot>
  381. <slot name="bar" :msg="msg + ' bar'"></slot>
  382. <slot name="abc" :msg="msg + ' abc'"></slot>
  383. </div>
  384. `
  385. }
  386. }
  387. }).$mount()
  388. expect(vm.$el.innerHTML).toBe('<span>hello foo</span> <span>hello bar</span> <span>hello abc</span>')
  389. vm.$refs.test.msg = 'world'
  390. waitForUpdate(() => {
  391. expect(vm.$el.innerHTML).toBe('<span>world foo</span> <span>world bar</span> <span>world abc</span>')
  392. }).then(done)
  393. })
  394. })