content_spec.js 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. var Vue = require('../../../../src/vue')
  2. var _ = require('../../../../src/util')
  3. describe('Content Transclusion', function () {
  4. var el, vm, options
  5. beforeEach(function () {
  6. el = document.createElement('div')
  7. options = {
  8. el: el
  9. }
  10. })
  11. function mount () {
  12. vm = new Vue(options)
  13. }
  14. it('no content', function () {
  15. options.template = '<div><content></content></div>'
  16. mount()
  17. expect(el.firstChild.childNodes.length).toBe(0)
  18. })
  19. it('default content', function () {
  20. el.innerHTML = '<p>hi</p>'
  21. options.template = '<div><content></content></div>'
  22. mount()
  23. expect(el.firstChild.tagName).toBe('DIV')
  24. expect(el.firstChild.firstChild.tagName).toBe('P')
  25. expect(el.firstChild.firstChild.textContent).toBe('hi')
  26. })
  27. it('no template auto content', function () {
  28. el.innerHTML = '<p>hi</p>'
  29. options._asComponent = true
  30. mount()
  31. expect(el.firstChild.tagName).toBe('P')
  32. expect(el.firstChild.textContent).toBe('hi')
  33. })
  34. it('fallback content', function () {
  35. options.template = '<content><p>fallback</p></content>'
  36. mount()
  37. expect(el.firstChild.tagName).toBe('P')
  38. expect(el.firstChild.textContent).toBe('fallback')
  39. })
  40. it('fallback content with multiple select', function () {
  41. el.innerHTML = '<p class="b">select b</p>'
  42. options.template =
  43. '<content select=".a"><p>fallback a</p></content>' +
  44. '<content select=".b">fallback b</content>'
  45. mount()
  46. expect(el.childNodes.length).toBe(2)
  47. expect(el.firstChild.textContent).toBe('fallback a')
  48. expect(el.lastChild.textContent).toBe('select b')
  49. })
  50. it('selector matching multiple elements', function () {
  51. el.innerHTML = '<p class="t">1</p><div></div><p class="t">2</p>'
  52. options.template = '<content select=".t"></content>'
  53. mount()
  54. expect(el.innerHTML).toBe('<p class="t">1</p><p class="t">2</p>')
  55. })
  56. it('default content should only render parts not selected', function () {
  57. el.innerHTML = '<div>hi</div><p class="a">1</p><p class="b">2</p>'
  58. options.template =
  59. '<content select=".a"></content>' +
  60. '<content></content>' +
  61. '<content select=".b"></content>'
  62. mount()
  63. expect(el.innerHTML).toBe('<p class="a">1</p><div>hi</div><p class="b">2</p>')
  64. })
  65. it('content transclusion with replace', function () {
  66. el.innerHTML = '<p>hi</p>'
  67. options.template = '<div><div><content></content></div></div>'
  68. options.replace = true
  69. mount()
  70. var res = vm.$el
  71. expect(res).not.toBe(el)
  72. expect(res.firstChild.tagName).toBe('DIV')
  73. expect(res.firstChild.firstChild.tagName).toBe('P')
  74. expect(res.firstChild.firstChild.textContent).toBe('hi')
  75. })
  76. it('block instance content transclusion', function () {
  77. el.innerHTML = '<p>hi</p><span>ho</span>'
  78. options.template = '<div></div><content select="p"></content><content select="span"></content>'
  79. options.replace = true
  80. mount()
  81. expect(getChild(1).tagName).toBe('DIV')
  82. expect(getChild(2).tagName).toBe('P')
  83. expect(getChild(3).tagName).toBe('SPAN')
  84. function getChild (n) {
  85. var el = vm._fragmentStart
  86. while (n--) {
  87. el = el.nextSibling
  88. }
  89. return el
  90. }
  91. })
  92. it('select should only match children', function () {
  93. el.innerHTML =
  94. '<p class="b">select b</p>' +
  95. '<span><p class="b">nested b</p></span>' +
  96. '<span><p class="c">nested c</p></span>'
  97. options.template =
  98. '<content select=".a"><p>fallback a</p></content>' +
  99. '<content select=".b">fallback b</content>' +
  100. '<content select=".c">fallback c</content>'
  101. mount()
  102. expect(el.childNodes.length).toBe(3)
  103. expect(el.firstChild.textContent).toBe('fallback a')
  104. expect(el.childNodes[1].textContent).toBe('select b')
  105. expect(el.lastChild.textContent).toBe('fallback c')
  106. })
  107. it('should accept expressions in selectors', function () {
  108. el.innerHTML = '<p>one</p><p>two</p>'
  109. options.template = '<content select="p:nth-child({{i}})"></content>'
  110. options.data = {
  111. i: 2
  112. }
  113. mount()
  114. expect(el.innerHTML).toBe('<p>two</p>')
  115. })
  116. it('content should be dynamic and compiled in parent scope', function (done) {
  117. var vm = new Vue({
  118. el: el,
  119. data: {
  120. msg: 'hello'
  121. },
  122. template: '<test>{{msg}}</test>',
  123. components: {
  124. test: {
  125. template: '<content></content>'
  126. }
  127. }
  128. })
  129. expect(el.innerHTML).toBe('<test>hello</test>')
  130. vm.msg = 'what'
  131. _.nextTick(function () {
  132. expect(el.innerHTML).toBe('<test>what</test>')
  133. done()
  134. })
  135. })
  136. it('v-if with content transclusion', function (done) {
  137. var vm = new Vue({
  138. el: el,
  139. data: {
  140. a: 1,
  141. show: true
  142. },
  143. template: '<test show="{{show}}">{{a}}</test>',
  144. components: {
  145. test: {
  146. props: ['show'],
  147. template: '<div v-if="show"><content></cotent></div>'
  148. }
  149. }
  150. })
  151. expect(el.textContent).toBe('1')
  152. vm.a = 2
  153. _.nextTick(function () {
  154. expect(el.textContent).toBe('2')
  155. vm.show = false
  156. _.nextTick(function () {
  157. expect(el.textContent).toBe('')
  158. vm.show = true
  159. vm.a = 3
  160. _.nextTick(function () {
  161. expect(el.textContent).toBe('3')
  162. done()
  163. })
  164. })
  165. })
  166. })
  167. it('inline v-repeat', function () {
  168. el.innerHTML = '<p>1</p><p>2</p><p>3</p>'
  169. new Vue({
  170. el: el,
  171. template: '<div v-repeat="list"><content select="p:nth-child({{$index + 1}})"></content></div>',
  172. data: {
  173. list: 0
  174. },
  175. beforeCompile: function () {
  176. this.list = this.$options._content.querySelectorAll('p').length
  177. }
  178. })
  179. expect(el.innerHTML).toBe('<div><p>1</p></div><div><p>2</p></div><div><p>3</p></div>')
  180. })
  181. it('v-repeat + component + parent directive + transclusion', function (done) {
  182. var vm = new Vue({
  183. el: el,
  184. template: '<test v-repeat="list" v-class="cls">{{msg}}</test>',
  185. data: {
  186. cls: 'parent',
  187. msg: 'hi',
  188. list: [{a: 1}, {a: 2}, {a: 3}]
  189. },
  190. components: {
  191. test: {
  192. replace: true,
  193. template: '<div class="child">{{a}} <content></content></div>'
  194. }
  195. }
  196. })
  197. var markup = vm.list.map(function (item) {
  198. return '<div class="child parent">' + item.a + ' hi</div>'
  199. }).join('')
  200. expect(el.innerHTML).toBe(markup)
  201. vm.msg = 'ho'
  202. markup = vm.list.map(function (item) {
  203. return '<div class="child parent">' + item.a + ' ho</div>'
  204. }).join('')
  205. _.nextTick(function () {
  206. expect(el.innerHTML).toBe(markup)
  207. done()
  208. })
  209. })
  210. it('nested transclusions', function (done) {
  211. vm = new Vue({
  212. el: el,
  213. template:
  214. '<testa>' +
  215. '<testb>' +
  216. '<div v-repeat="list">{{$value}}</div>' +
  217. '</testb>' +
  218. '</testa>',
  219. data: {
  220. list: [1, 2]
  221. },
  222. components: {
  223. testa: { template: '<content></content>' },
  224. testb: { template: '<content></content>' }
  225. }
  226. })
  227. expect(el.innerHTML).toBe(
  228. '<testa><testb>' +
  229. '<div>1</div><div>2</div>' +
  230. '</testb></testa>'
  231. )
  232. vm.list.push(3)
  233. _.nextTick(function () {
  234. expect(el.innerHTML).toBe(
  235. '<testa><testb>' +
  236. '<div>1</div><div>2</div><div>3</div>' +
  237. '</testb></testa>'
  238. )
  239. done()
  240. })
  241. })
  242. it('nested transclusion, container dirs & props', function (done) {
  243. vm = new Vue({
  244. el: el,
  245. template:
  246. '<testa>' +
  247. '<testb v-if="ok" prop="{{msg}}"></testb>' +
  248. '</testa>',
  249. data: {
  250. ok: false,
  251. msg: 'hello'
  252. },
  253. components: {
  254. testa: { template: '<content></content>' },
  255. testb: {
  256. props: ['prop'],
  257. template: '{{prop}}'
  258. }
  259. }
  260. })
  261. expect(el.innerHTML).toBe('<testa></testa>')
  262. vm.ok = true
  263. _.nextTick(function () {
  264. expect(el.innerHTML).toBe('<testa><testb>hello</testb></testa>')
  265. done()
  266. })
  267. })
  268. // #1010
  269. it('v-repeat inside transcluded content', function () {
  270. vm = new Vue({
  271. el: el,
  272. template:
  273. '<testa>' +
  274. '{{inner}} {{outer}}' +
  275. '<div v-repeat="list"> {{inner}} {{outer}}</div>' +
  276. '</testa>',
  277. data: {
  278. outer: 'outer',
  279. inner: 'parent-inner',
  280. list: [
  281. { inner: 'list-inner' }
  282. ]
  283. },
  284. components: {
  285. testa: {
  286. data: function () {
  287. return {
  288. inner: 'component-inner'
  289. }
  290. },
  291. template: '<content></content>'
  292. }
  293. }
  294. })
  295. expect(el.textContent).toBe('parent-inner outer list-inner outer')
  296. })
  297. it('single content outlet with replace: true', function () {
  298. vm = new Vue({
  299. el: el,
  300. template:
  301. '<test><p>1</p><p>2</p></test>',
  302. components: {
  303. test: {
  304. template: '<content></content>',
  305. replace: true
  306. }
  307. }
  308. })
  309. expect(el.innerHTML).toBe('<p>1</p><p>2</p>')
  310. })
  311. })