@gogocat/data-bind 1.12.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/.editorconfig +14 -14
  2. package/.vscode/launch.json +12 -12
  3. package/CONFIGURATION.md +294 -0
  4. package/REACTIVE_MODE.md +553 -0
  5. package/README.md +266 -829
  6. package/babel.config.json +30 -0
  7. package/dist/js/_escape.d.ts +14 -0
  8. package/dist/js/_escape.d.ts.map +1 -0
  9. package/dist/js/applyBinding.d.ts +11 -0
  10. package/dist/js/applyBinding.d.ts.map +1 -0
  11. package/dist/js/attrBinding.d.ts +12 -0
  12. package/dist/js/attrBinding.d.ts.map +1 -0
  13. package/dist/js/binder.d.ts +67 -0
  14. package/dist/js/binder.d.ts.map +1 -0
  15. package/dist/js/changeBinding.d.ts +19 -0
  16. package/dist/js/changeBinding.d.ts.map +1 -0
  17. package/dist/js/commentWrapper.d.ts +39 -0
  18. package/dist/js/commentWrapper.d.ts.map +1 -0
  19. package/dist/js/config.d.ts +55 -0
  20. package/dist/js/config.d.ts.map +1 -0
  21. package/dist/js/createBindingOption.d.ts +32 -0
  22. package/dist/js/createBindingOption.d.ts.map +1 -0
  23. package/dist/js/createEventBinding.d.ts +10 -0
  24. package/dist/js/createEventBinding.d.ts.map +1 -0
  25. package/dist/js/cssBinding.d.ts +15 -0
  26. package/dist/js/cssBinding.d.ts.map +1 -0
  27. package/dist/js/dataBind.js +2756 -2530
  28. package/dist/js/dataBind.min.js +8 -1
  29. package/dist/js/dataBind.min.js.map +1 -1
  30. package/dist/js/domWalker.d.ts +9 -0
  31. package/dist/js/domWalker.d.ts.map +1 -0
  32. package/dist/js/forOfBinding.d.ts +12 -0
  33. package/dist/js/forOfBinding.d.ts.map +1 -0
  34. package/dist/js/hoverBinding.d.ts +13 -0
  35. package/dist/js/hoverBinding.d.ts.map +1 -0
  36. package/dist/js/ifBinding.d.ts +12 -0
  37. package/dist/js/ifBinding.d.ts.map +1 -0
  38. package/dist/js/index.d.ts +10 -0
  39. package/dist/js/index.d.ts.map +1 -0
  40. package/dist/js/modelBinding.d.ts +12 -0
  41. package/dist/js/modelBinding.d.ts.map +1 -0
  42. package/dist/js/postProcess.d.ts +3 -0
  43. package/dist/js/postProcess.d.ts.map +1 -0
  44. package/dist/js/pubSub.d.ts +11 -0
  45. package/dist/js/pubSub.d.ts.map +1 -0
  46. package/dist/js/reactiveProxy.d.ts +28 -0
  47. package/dist/js/reactiveProxy.d.ts.map +1 -0
  48. package/dist/js/renderForOfBinding.d.ts +8 -0
  49. package/dist/js/renderForOfBinding.d.ts.map +1 -0
  50. package/dist/js/renderIfBinding.d.ts +22 -0
  51. package/dist/js/renderIfBinding.d.ts.map +1 -0
  52. package/dist/js/renderIteration.d.ts +16 -0
  53. package/dist/js/renderIteration.d.ts.map +1 -0
  54. package/dist/js/renderTemplate.d.ts +14 -0
  55. package/dist/js/renderTemplate.d.ts.map +1 -0
  56. package/dist/js/renderTemplatesBinding.d.ts +19 -0
  57. package/dist/js/renderTemplatesBinding.d.ts.map +1 -0
  58. package/dist/js/showBinding.d.ts +13 -0
  59. package/dist/js/showBinding.d.ts.map +1 -0
  60. package/dist/js/switchBinding.d.ts +13 -0
  61. package/dist/js/switchBinding.d.ts.map +1 -0
  62. package/dist/js/textBinding.d.ts +13 -0
  63. package/dist/js/textBinding.d.ts.map +1 -0
  64. package/dist/js/types/_escape.d.ts +14 -0
  65. package/dist/js/types/_escape.d.ts.map +1 -0
  66. package/dist/js/types/applyBinding.d.ts +11 -0
  67. package/dist/js/types/applyBinding.d.ts.map +1 -0
  68. package/dist/js/types/attrBinding.d.ts +12 -0
  69. package/dist/js/types/attrBinding.d.ts.map +1 -0
  70. package/dist/js/types/binder.d.ts +67 -0
  71. package/dist/js/types/binder.d.ts.map +1 -0
  72. package/dist/js/types/changeBinding.d.ts +19 -0
  73. package/dist/js/types/changeBinding.d.ts.map +1 -0
  74. package/dist/js/types/commentWrapper.d.ts +39 -0
  75. package/dist/js/types/commentWrapper.d.ts.map +1 -0
  76. package/dist/js/types/config.d.ts +55 -0
  77. package/dist/js/types/config.d.ts.map +1 -0
  78. package/dist/js/types/createBindingOption.d.ts +32 -0
  79. package/dist/js/types/createBindingOption.d.ts.map +1 -0
  80. package/dist/js/types/createEventBinding.d.ts +10 -0
  81. package/dist/js/types/createEventBinding.d.ts.map +1 -0
  82. package/dist/js/types/cssBinding.d.ts +15 -0
  83. package/dist/js/types/cssBinding.d.ts.map +1 -0
  84. package/dist/js/types/domWalker.d.ts +9 -0
  85. package/dist/js/types/domWalker.d.ts.map +1 -0
  86. package/dist/js/types/forOfBinding.d.ts +12 -0
  87. package/dist/js/types/forOfBinding.d.ts.map +1 -0
  88. package/dist/js/types/hoverBinding.d.ts +13 -0
  89. package/dist/js/types/hoverBinding.d.ts.map +1 -0
  90. package/dist/js/types/ifBinding.d.ts +12 -0
  91. package/dist/js/types/ifBinding.d.ts.map +1 -0
  92. package/dist/js/types/index.d.ts +10 -0
  93. package/dist/js/types/index.d.ts.map +1 -0
  94. package/dist/js/types/modelBinding.d.ts +12 -0
  95. package/dist/js/types/modelBinding.d.ts.map +1 -0
  96. package/dist/js/types/postProcess.d.ts +3 -0
  97. package/dist/js/types/postProcess.d.ts.map +1 -0
  98. package/dist/js/types/pubSub.d.ts +11 -0
  99. package/dist/js/types/pubSub.d.ts.map +1 -0
  100. package/dist/js/types/reactiveProxy.d.ts +28 -0
  101. package/dist/js/types/reactiveProxy.d.ts.map +1 -0
  102. package/dist/js/types/renderForOfBinding.d.ts +8 -0
  103. package/dist/js/types/renderForOfBinding.d.ts.map +1 -0
  104. package/dist/js/types/renderIfBinding.d.ts +22 -0
  105. package/dist/js/types/renderIfBinding.d.ts.map +1 -0
  106. package/dist/js/types/renderIteration.d.ts +16 -0
  107. package/dist/js/types/renderIteration.d.ts.map +1 -0
  108. package/dist/js/types/renderTemplate.d.ts +14 -0
  109. package/dist/js/types/renderTemplate.d.ts.map +1 -0
  110. package/dist/js/types/renderTemplatesBinding.d.ts +19 -0
  111. package/dist/js/types/renderTemplatesBinding.d.ts.map +1 -0
  112. package/dist/js/types/showBinding.d.ts +13 -0
  113. package/dist/js/types/showBinding.d.ts.map +1 -0
  114. package/dist/js/types/switchBinding.d.ts +13 -0
  115. package/dist/js/types/switchBinding.d.ts.map +1 -0
  116. package/dist/js/types/textBinding.d.ts +13 -0
  117. package/dist/js/types/textBinding.d.ts.map +1 -0
  118. package/dist/js/types/types.d.ts +111 -0
  119. package/dist/js/types/types.d.ts.map +1 -0
  120. package/dist/js/types/util.d.ts +119 -0
  121. package/dist/js/types/util.d.ts.map +1 -0
  122. package/dist/js/types.d.ts +111 -0
  123. package/dist/js/types.d.ts.map +1 -0
  124. package/dist/js/util.d.ts +119 -0
  125. package/dist/js/util.d.ts.map +1 -0
  126. package/eslint.config.js +124 -0
  127. package/examples/DBMONSTER_COMPARISON.md +123 -0
  128. package/examples/afterRenderDemo.html +119 -0
  129. package/examples/bootstrap/css/animate.css +1579 -1579
  130. package/examples/bootstrap/css/bootstrap.min.css +6 -6
  131. package/examples/bootstrap/css/homeservices.css +378 -390
  132. package/examples/bootstrap/css/open-iconic.css +511 -511
  133. package/examples/bootstrap/fonts/open-iconic.svg +543 -543
  134. package/examples/bootstrap/js/compMessageDialog.js +20 -19
  135. package/examples/bootstrap/js/compSearchBar.js +12 -19
  136. package/examples/bootstrap/js/compSearchResults.js +50 -46
  137. package/examples/bootstrap/js/featureAdsResult.json +65 -65
  138. package/examples/bootstrap/js/searchResult.json +57 -57
  139. package/examples/bootstrap.html +343 -332
  140. package/examples/css/baseTodo.css +141 -141
  141. package/examples/css/dbMonsterStyles.css +27 -27
  142. package/examples/css/indexTodo.css +374 -374
  143. package/examples/dbmonsterForOfReactive.html +40 -0
  144. package/examples/dbmonsterReact.html +19 -0
  145. package/examples/forOfBindingSimpleDebug.html +45 -0
  146. package/examples/globalConfig.html +131 -0
  147. package/examples/js/afterRenderDemo.js +190 -0
  148. package/examples/js/appTodo.js +46 -46
  149. package/examples/js/attrBindingDemo.js +2 -2
  150. package/examples/js/dbMonApp.js +24 -26
  151. package/examples/js/dbMonAppReact.jsx +79 -0
  152. package/examples/js/dbMonAppReactive.js +28 -0
  153. package/examples/js/fiberDemo.js +4 -4
  154. package/examples/js/filtersDemo.js +8 -8
  155. package/examples/js/forOfDemo.js +7 -9
  156. package/examples/js/forOfDemoComplex.js +44 -17
  157. package/examples/js/form.js +14 -14
  158. package/examples/js/globalConfig.js +117 -0
  159. package/examples/js/ifBindingDemo.js +16 -16
  160. package/examples/js/reactiveDemo.js +119 -0
  161. package/examples/js/switchBindingDemo.js +8 -8
  162. package/examples/react-dbmonster/dist/bundle.js +43 -0
  163. package/examples/react-dbmonster/package-lock.json +537 -0
  164. package/examples/react-dbmonster/package.json +16 -0
  165. package/examples/react-dbmonster/src/index.jsx +80 -0
  166. package/examples/reactiveDemo.html +127 -0
  167. package/examples/refreshRateTest.html +75 -75
  168. package/index.html +841 -0
  169. package/package.json +31 -34
  170. package/rollup.config.js +79 -36
  171. package/src/{_escape.js → _escape.ts} +19 -17
  172. package/src/{applyBinding.js → applyBinding.ts} +27 -18
  173. package/src/{attrBinding.js → attrBinding.ts} +14 -13
  174. package/src/{binder.js → binder.ts} +289 -181
  175. package/src/changeBinding.ts +93 -0
  176. package/src/{commentWrapper.js → commentWrapper.ts} +33 -30
  177. package/src/config.ts +107 -0
  178. package/src/{createBindingOption.js → createBindingOption.ts} +39 -15
  179. package/src/createEventBinding.ts +88 -0
  180. package/src/{cssBinding.js → cssBinding.ts} +13 -11
  181. package/src/{domWalker.js → domWalker.ts} +44 -30
  182. package/src/{forOfBinding.js → forOfBinding.ts} +4 -3
  183. package/src/hoverBinding.ts +84 -0
  184. package/src/{ifBinding.js → ifBinding.ts} +14 -12
  185. package/src/index.ts +53 -0
  186. package/src/{modelBinding.js → modelBinding.ts} +11 -9
  187. package/src/{postProcess.js → postProcess.ts} +6 -4
  188. package/src/{pubSub.js → pubSub.ts} +24 -21
  189. package/src/reactiveProxy.ts +285 -0
  190. package/src/{renderForOfBinding.js → renderForOfBinding.ts} +54 -32
  191. package/src/{renderIfBinding.js → renderIfBinding.ts} +41 -19
  192. package/src/{renderIteration.js → renderIteration.ts} +24 -8
  193. package/src/renderTemplate.ts +165 -0
  194. package/src/renderTemplatesBinding.ts +73 -0
  195. package/src/{showBinding.js → showBinding.ts} +4 -3
  196. package/src/{switchBinding.js → switchBinding.ts} +18 -15
  197. package/src/{textBinding.js → textBinding.ts} +5 -4
  198. package/src/types.ts +124 -0
  199. package/src/util.ts +810 -0
  200. package/test/css/reporter.css +9 -9
  201. package/test/globals.d.ts +19 -0
  202. package/test/helpers/testHelper.js +46 -11
  203. package/test/mocks/featureAdsResult.json +65 -65
  204. package/test/mocks/searchResult.json +57 -57
  205. package/test/specs/{attrBinding.spec.js → attrBinding.spec.ts} +103 -106
  206. package/test/specs/{binder.spec.js → binder.spec.ts} +29 -27
  207. package/test/specs/blurBinding.spec.ts +60 -0
  208. package/test/specs/chainableUse.spec.ts +125 -0
  209. package/test/specs/clickBinding.spec.ts +194 -0
  210. package/test/specs/{cssBinding.spec.js → cssBinding.spec.ts} +72 -79
  211. package/test/specs/{dataBindBootstrap.spec.js → dataBindBootstrap.spec.ts} +332 -313
  212. package/test/specs/{filter.spec.js → filter.spec.ts} +75 -76
  213. package/test/specs/{forOfBinding.spec.js → forOfBinding.spec.ts} +208 -219
  214. package/test/specs/formBinding.spec.ts +272 -0
  215. package/test/specs/ifBinding.spec.ts +165 -0
  216. package/test/specs/{nestedComponent.spec.js → nestedComponent.spec.ts} +88 -88
  217. package/test/specs/reactiveProxy.spec.ts +465 -0
  218. package/test/specs/{showBinding.spec.js → showBinding.spec.ts} +148 -149
  219. package/test/specs/{switchBinding.spec.js → switchBinding.spec.ts} +172 -173
  220. package/test/specs/templateBinding.spec.ts +273 -0
  221. package/test/specs/{textBinding.spec.js → textBinding.spec.ts} +47 -48
  222. package/test/tsconfig.json +31 -0
  223. package/test-output.txt +200 -0
  224. package/test-reactive.html +224 -0
  225. package/tsconfig.json +28 -0
  226. package/vendors/lodash.custom.js +4577 -4577
  227. package/vendors/lodash.custom.min.js +45 -45
  228. package/vitest.config.js +27 -0
  229. package/.eslintrc.js +0 -1
  230. package/.grunt/grunt-contrib-jasmine/boot.js +0 -161
  231. package/.grunt/grunt-contrib-jasmine/dist/js/dataBind.js +0 -9
  232. package/.grunt/grunt-contrib-jasmine/grunt-template-jasmine-istanbul/reporter.js +0 -23
  233. package/.grunt/grunt-contrib-jasmine/jasmine-html.js +0 -853
  234. package/.grunt/grunt-contrib-jasmine/jasmine.css +0 -271
  235. package/.grunt/grunt-contrib-jasmine/jasmine.js +0 -9761
  236. package/.grunt/grunt-contrib-jasmine/jasmine_favicon.png +0 -0
  237. package/.grunt/grunt-contrib-jasmine/json2.js +0 -489
  238. package/.grunt/grunt-contrib-jasmine/reporter.js +0 -107
  239. package/coverage/coverage.json +0 -1
  240. package/coverage/lcov/lcov-report/base.css +0 -213
  241. package/coverage/lcov/lcov-report/index.html +0 -93
  242. package/coverage/lcov/lcov-report/js/dataBind.js.html +0 -6596
  243. package/coverage/lcov/lcov-report/js/index.html +0 -93
  244. package/coverage/lcov/lcov-report/prettify.css +0 -1
  245. package/coverage/lcov/lcov-report/prettify.js +0 -1
  246. package/coverage/lcov/lcov-report/sort-arrow-sprite.png +0 -0
  247. package/coverage/lcov/lcov-report/sorter.js +0 -158
  248. package/coverage/lcov/lcov.info +0 -1991
  249. package/eslintrc.json +0 -40
  250. package/examples/bootstrap/js/bootstrap.min.js +0 -6
  251. package/examples/bootstrap/js/popper.min.js +0 -5
  252. package/examples/bootstrap/js/searchSuggestion.js +0 -58
  253. package/examples/bootstrap/js/typeahead.jquery.js +0 -1538
  254. package/gruntfile.js +0 -92
  255. package/gulpfile.js +0 -32
  256. package/src/applyBindingExport.js +0 -5
  257. package/src/changeBinding.js +0 -63
  258. package/src/config.js +0 -66
  259. package/src/createEventBinding.js +0 -46
  260. package/src/eventSystem.js +0 -46
  261. package/src/hoverBinding.js +0 -57
  262. package/src/index.js +0 -26
  263. package/src/renderTemplate.js +0 -128
  264. package/src/renderTemplatesBinding.js +0 -44
  265. package/src/util.js +0 -648
  266. package/test/specs/blurBinding.spec.js +0 -57
  267. package/test/specs/formBinding.spec.js +0 -316
  268. package/test/specs/ifBinding.spec.js +0 -169
  269. package/test/specs/templateBinding.spec.js +0 -117
  270. package/vendors/jasmine-jquery.js +0 -841
  271. package/vendors/jquery-3.2.1.min.js +0 -4
@@ -1,173 +1,172 @@
1
- /* eslint-disable max-len */
2
- describe('Given [data-bind-comp="switch-component"] inited', () => {
3
- const namespace = {};
4
-
5
- // stories data
6
- const storiesData = {
7
- s1: {
8
- title: 'Hansel and Gretel',
9
- pic:
10
- 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRLxVHKpB93yhxRUb8Wbc1NkUA4Nf8QBMPieKeeL1ugcZivy82INw',
11
- description:
12
- '"Hansel and Gretel" (also known as Hansel and Grettel, Hansel and Grethel, or Little Brother and Little Sister) is a well-known fairy tale of German origin, recorded by the Brothers Grimm and published in 1812. Hansel and Gretel are a young brother and sister kidnapped by a cannibalistic witch living deep in the forest in a house constructed of cake and confectionery. The two children escape with their lives by outwitting her. The tale has been adapted to various media, most notably the opera Hänsel und Gretel (1893) by Engelbert Humperdinck. Under the Aarne–Thompson classification system, "Hansel and Gretel" is classified under Class 327.',
13
- },
14
- s2: {
15
- title: 'The Ugly Duckling',
16
- description:
17
- '"The Ugly Duckling" (Danish: Den grimme ælling) is a literary fairy tale by Danish poet and author Hans Christian Andersen (1805–1875). The story tells of a homely little bird born in a barnyard who suffers abuse from the others around him until, much to his delight (and to the surprise of others), he matures into a beautiful swan, the most beautiful bird of all. The story is beloved around the world as a tale about personal transformation for the better. “The Ugly Duckling” was first published 11 November 1843, with three other tales by Andersen in Copenhagen, Denmark to great critical acclaim. The tale has been adapted to various media including opera, musical, and animated film. The tale is completely Andersen\'s invention and owes no debt to fairy tales or folklore.',
18
- },
19
- s3: {
20
- title: 'The Giving Tree',
21
- pic: 'https://images-na.ssl-images-amazon.com/images/I/51EDtk%2B1rNL._SX384_BO1,204,203,200_.jpg',
22
- description:
23
- 'Published by Harper and Row in 1964, The Giving Tree is a children\'s book written and demonstrated by an American author of children\'s books Shel Silverstein. It was one of Silverstein\'s best children books that has been translated into over 30 languages.',
24
- },
25
- };
26
-
27
- jasmine.getFixtures().fixturesPath = 'test';
28
-
29
- beforeEach(() => {
30
- loadFixtures('./fixtures/switchBinding.html');
31
-
32
- namespace.viewModel = {
33
- heading: 'This heading is inside switch binding',
34
- selectedStory: '',
35
- story: {},
36
- storyOptions: [
37
- {title: 'Hansel and Gretel', value: 's1'},
38
- {title: 'The Ugly Duckling', value: 's2'},
39
- {title: 'The Giving Tree', value: 's3'},
40
- ],
41
- onSelectedStory: function(newValue) {
42
- const id = newValue;
43
-
44
- if (storiesData[id] && id !== this.selectedStory) {
45
- this.story = storiesData[id];
46
- this.selectedStory = id;
47
- } else {
48
- this.story = {};
49
- this.selectedStory = '';
50
- }
51
- this.updateView();
52
- },
53
- setStoryImgAttr: function() {
54
- const picPath = this.story.pic || '';
55
- const ret = {
56
- alt: this.story.title,
57
- width: 100,
58
- height: 'auto',
59
- };
60
-
61
- if (picPath) {
62
- ret.src = picPath;
63
- }
64
- return ret;
65
- },
66
- updateView(opt) {
67
- this.APP.render(opt);
68
- },
69
- };
70
-
71
- namespace.mySwitchComponent = dataBind.init(document.querySelector('[data-bind-comp="switch-component"]'), namespace.viewModel);
72
-
73
- namespace.mySwitchComponent.render();
74
- });
75
-
76
- afterEach(() => {
77
- // clean up all app/components
78
- for (const prop in namespace) {
79
- if (namespace.hasOwnProperty(prop)) {
80
- delete namespace[prop];
81
- }
82
- }
83
- });
84
-
85
- it('Then [data-bind-comp="mySwitchComponent"] should have render', (done) => {
86
- setTimeout(() => {
87
- expect(document.getElementById('switch-component-heading').textContent).toBe(namespace.viewModel.heading);
88
- done();
89
- }, 200);
90
- });
91
-
92
- it('Should render only switch default case', (done) => {
93
- setTimeout(() => {
94
- // check child non switch binding element still exists
95
- expect(document.getElementById('switch-component-heading').textContent).toBe(namespace.viewModel.heading);
96
-
97
- expect(document.getElementById('default-case')).toBeDefined();
98
- expect(document.getElementById('case1')).toBe(null);
99
- expect(document.getElementById('case2')).toBe(null);
100
- expect(document.getElementById('case3')).toBe(null);
101
- done();
102
- }, 200);
103
- });
104
-
105
- it('When selectedStory = s1 it then should render case1', (done) => {
106
- namespace.viewModel.onSelectedStory('s1');
107
-
108
- setTimeout(() => {
109
- const $case1 = document.getElementById('case1');
110
- const $storyTitle = $case1.querySelector('h4');
111
- const $storyImg = $case1.querySelector('img');
112
- const $storyDescription = $case1.querySelector('p');
113
-
114
- // check child non switch binding element still exists
115
- expect(document.getElementById('switch-component-heading').textContent).toBe(namespace.viewModel.heading);
116
-
117
- expect($case1).toBeDefined();
118
- // check other binding within this switch binding
119
- expect($storyTitle.textContent).toBe(storiesData.s1.title);
120
- expect($storyImg.getAttribute('src')).toBe(storiesData.s1.pic);
121
- expect($storyDescription.textContent).toBe(storiesData.s1.description);
122
-
123
- expect(document.getElementById('case2')).toBe(null);
124
- expect(document.getElementById('case3')).toBe(null);
125
- expect(document.getElementById('default-case')).toBe(null);
126
- done();
127
- }, 200);
128
- });
129
-
130
- it('When selectedStory = s2 it then should render case2 without image', (done) => {
131
- namespace.viewModel.onSelectedStory('s2');
132
-
133
- setTimeout(() => {
134
- const $case2 = document.getElementById('case2');
135
- const $storyTitle = $case2.querySelector('h4');
136
- const $storyDescription = $case2.querySelector('p');
137
-
138
- // check child non switch binding element still exists
139
- expect(document.getElementById('switch-component-heading').textContent).toBe(namespace.viewModel.heading);
140
-
141
- // check other binding within this switch binding
142
- expect($storyTitle.textContent).toBe(storiesData.s2.title);
143
- // expect($storyImg).toBe(null);
144
- expect($storyDescription.textContent).toBe(storiesData.s2.description);
145
-
146
- expect(document.getElementById('case1')).toBe(null);
147
- expect(document.getElementById('case3')).toBe(null);
148
- expect(document.getElementById('default-case')).toBe(null);
149
- done();
150
- }, 200);
151
- });
152
-
153
- it('When selectedStory non exists it then should render default case', (done) => {
154
- namespace.viewModel.onSelectedStory('xyz');
155
-
156
- setTimeout(() => {
157
- const $defaultCase = document.getElementById('default-case');
158
- const $defaultCaseTextContent = $defaultCase.querySelector('p').textContent;
159
- const defatulCaseText = 'No story found...';
160
-
161
- // check child non switch binding element still exists
162
- expect(document.getElementById('switch-component-heading').textContent).toBe(namespace.viewModel.heading);
163
-
164
- // check other binding within this switch binding
165
- expect($defaultCase).toBeDefined();
166
- expect($defaultCaseTextContent).toBe(defatulCaseText);
167
- expect(document.getElementById('case1')).toBe(null);
168
- expect(document.getElementById('case2')).toBe(null);
169
- expect(document.getElementById('case3')).toBe(null);
170
- done();
171
- }, 200);
172
- });
173
- });
1
+ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
2
+ import {waitFor} from '@testing-library/dom';
3
+
4
+
5
+ describe('Given [data-bind-comp="switch-component"] inited', () => {
6
+ const namespace: any = {};
7
+
8
+ // stories data
9
+ const storiesData: any = {
10
+ s1: {
11
+ title: 'Hansel and Gretel',
12
+ pic:
13
+ 'https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRLxVHKpB93yhxRUb8Wbc1NkUA4Nf8QBMPieKeeL1ugcZivy82INw',
14
+ description:
15
+ '"Hansel and Gretel" (also known as Hansel and Grettel, Hansel and Grethel, or Little Brother and Little Sister) is a well-known fairy tale of German origin, recorded by the Brothers Grimm and published in 1812. Hansel and Gretel are a young brother and sister kidnapped by a cannibalistic witch living deep in the forest in a house constructed of cake and confectionery. The two children escape with their lives by outwitting her. The tale has been adapted to various media, most notably the opera Hänsel und Gretel (1893) by Engelbert Humperdinck. Under the Aarne–Thompson classification system, "Hansel and Gretel" is classified under Class 327.',
16
+ },
17
+ s2: {
18
+ title: 'The Ugly Duckling',
19
+ description:
20
+ '"The Ugly Duckling" (Danish: Den grimme ælling) is a literary fairy tale by Danish poet and author Hans Christian Andersen (1805–1875). The story tells of a homely little bird born in a barnyard who suffers abuse from the others around him until, much to his delight (and to the surprise of others), he matures into a beautiful swan, the most beautiful bird of all. The story is beloved around the world as a tale about personal transformation for the better. "The Ugly Duckling" was first published 11 November 1843, with three other tales by Andersen in Copenhagen, Denmark to great critical acclaim. The tale has been adapted to various media including opera, musical, and animated film. The tale is completely Andersen\'s invention and owes no debt to fairy tales or folklore.',
21
+ },
22
+ s3: {
23
+ title: 'The Giving Tree',
24
+ pic: 'https://images-na.ssl-images-amazon.com/images/I/51EDtk%2B1rNL._SX384_BO1,204,203,200_.jpg',
25
+ description:
26
+ 'Published by Harper and Row in 1964, The Giving Tree is a children\'s book written and demonstrated by an American author of children\'s books Shel Silverstein. It was one of Silverstein\'s best children books that has been translated into over 30 languages.',
27
+ },
28
+ };
29
+
30
+ beforeEach(async () => {
31
+ loadFixture('test/fixtures/switchBinding.html');
32
+
33
+ namespace.viewModel = {
34
+ heading: 'This heading is inside switch binding',
35
+ selectedStory: '',
36
+ story: {},
37
+ storyOptions: [
38
+ {title: 'Hansel and Gretel', value: 's1'},
39
+ {title: 'The Ugly Duckling', value: 's2'},
40
+ {title: 'The Giving Tree', value: 's3'},
41
+ ],
42
+ onSelectedStory(newValue: string) {
43
+ const id = newValue;
44
+
45
+ if (storiesData[id] && id !== this.selectedStory) {
46
+ this.story = storiesData[id];
47
+ this.selectedStory = id;
48
+ } else {
49
+ this.story = {};
50
+ this.selectedStory = '';
51
+ }
52
+ this.updateView();
53
+ },
54
+ setStoryImgAttr() {
55
+ const picPath = this.story.pic || '';
56
+ const ret: any = {
57
+ alt: this.story.title,
58
+ width: 100,
59
+ height: 'auto',
60
+ };
61
+
62
+ if (picPath) {
63
+ ret.src = picPath;
64
+ }
65
+ return ret;
66
+ },
67
+ updateView(opt?: any) {
68
+ this.APP.render(opt);
69
+ },
70
+ };
71
+
72
+ namespace.mySwitchComponent = dataBind.init(document.querySelector('[data-bind-comp="switch-component"]'), namespace.viewModel);
73
+
74
+ await namespace.mySwitchComponent.render();
75
+ });
76
+
77
+ afterEach(() => {
78
+ // clean up all app/components
79
+ for (const prop in namespace) {
80
+ if (Object.prototype.hasOwnProperty.call(namespace, prop)) {
81
+ delete namespace[prop];
82
+ }
83
+ }
84
+ });
85
+
86
+ it('Then [data-bind-comp="mySwitchComponent"] should have render', async () => {
87
+ await waitFor(() => {
88
+ expect(document.getElementById('switch-component-heading')!.textContent).toBe(namespace.viewModel.heading);
89
+ }, {timeout: 500});
90
+ });
91
+
92
+ it('Should render only switch default case', async () => {
93
+ await waitFor(() => {
94
+ // check child non switch binding element still exists
95
+ expect(document.getElementById('switch-component-heading')!.textContent).toBe(namespace.viewModel.heading);
96
+
97
+ expect(document.getElementById('default-case')).toBeDefined();
98
+ expect(document.getElementById('case1')).toBe(null);
99
+ expect(document.getElementById('case2')).toBe(null);
100
+ expect(document.getElementById('case3')).toBe(null);
101
+ }, {timeout: 500});
102
+ });
103
+
104
+
105
+ it('When selectedStory = s1 it then should render case1', async () => {
106
+ namespace.viewModel.onSelectedStory('s1');
107
+ await namespace.mySwitchComponent.render();
108
+
109
+ await waitFor(() => {
110
+ const $case1 = document.getElementById('case1')!;
111
+ const $storyTitle = $case1.querySelector('h4')!;
112
+ const $storyImg = $case1.querySelector('img') as HTMLImageElement;
113
+ const $storyDescription = $case1.querySelector('p')!;
114
+
115
+ // check child non switch binding element still exists
116
+ expect(document.getElementById('switch-component-heading')!.textContent).toBe(namespace.viewModel.heading);
117
+
118
+ expect($case1).toBeDefined();
119
+ // check other binding within this switch binding
120
+ expect($storyTitle.textContent).toBe(storiesData.s1.title);
121
+ expect($storyImg.getAttribute('src')).toBe(storiesData.s1.pic);
122
+ expect($storyDescription.textContent).toBe(storiesData.s1.description);
123
+
124
+ expect(document.getElementById('case2')).toBe(null);
125
+ expect(document.getElementById('case3')).toBe(null);
126
+ expect(document.getElementById('default-case')).toBe(null);
127
+ }, {timeout: 500});
128
+ });
129
+
130
+
131
+ it('When selectedStory = s2 it then should render case2 without image', async () => {
132
+ namespace.viewModel.onSelectedStory('s2');
133
+ await namespace.mySwitchComponent.render();
134
+
135
+ await waitFor(() => {
136
+ const $case2 = document.getElementById('case2')!;
137
+ const $storyTitle = $case2.querySelector('h4')!;
138
+ const $storyDescription = $case2.querySelector('p')!;
139
+
140
+ // check child non switch binding element still exists
141
+ expect(document.getElementById('switch-component-heading')!.textContent).toBe(namespace.viewModel.heading);
142
+
143
+ // check other binding within this switch binding
144
+ expect($storyTitle.textContent).toBe(storiesData.s2.title);
145
+ // expect($storyImg).toBe(null);
146
+ expect($storyDescription.textContent).toBe(storiesData.s2.description);
147
+
148
+ expect(document.getElementById('case1')).toBe(null);
149
+ expect(document.getElementById('case3')).toBe(null);
150
+ expect(document.getElementById('default-case')).toBe(null);
151
+ }, {timeout: 500});
152
+ });
153
+
154
+
155
+ it('When selectedStory non exists it then should render default case', async () => {
156
+ await waitFor(() => {
157
+ const $defaultCase = document.getElementById('default-case')!;
158
+ const $defaultCaseTextContent = $defaultCase.querySelector('p')!.textContent;
159
+ const defatulCaseText = 'No story found...';
160
+
161
+ // check child non switch binding element still exists
162
+ expect(document.getElementById('switch-component-heading')!.textContent).toBe(namespace.viewModel.heading);
163
+
164
+ // check other binding within this switch binding
165
+ expect($defaultCase).toBeDefined();
166
+ expect($defaultCaseTextContent).toBe(defatulCaseText);
167
+ expect(document.getElementById('case1')).toBe(null);
168
+ expect(document.getElementById('case2')).toBe(null);
169
+ expect(document.getElementById('case3')).toBe(null);
170
+ }, {timeout: 500});
171
+ });
172
+ });
@@ -0,0 +1,273 @@
1
+ import {describe, it, expect, beforeEach, afterEach} from 'vitest';
2
+ import {waitFor} from '@testing-library/dom';
3
+
4
+ describe('Given [data-bind-comp="temp-component"] inited', () => {
5
+ const namespace: any = {};
6
+
7
+ beforeEach(async () => {
8
+ loadFixture('test/fixtures/templateBinding.html');
9
+
10
+ namespace.finishTemplateRender = 0;
11
+
12
+ namespace.viewModel = {
13
+ heading: 'Test data-bind-temp binding',
14
+ description: 'This is template binding that also support underscore syntax',
15
+ content: 'test text',
16
+ contentNest1: {
17
+ info: 'Nested content 1',
18
+ },
19
+ contentNest2: {
20
+ info: 'Nested content 2',
21
+ },
22
+ contentNest3: {
23
+ info: 'Nested content 3',
24
+ },
25
+ afterTemplateRender() {
26
+ namespace.finishTemplateRender += 1;
27
+ },
28
+ updateView(opt?: any) {
29
+ this.APP.render(opt);
30
+ },
31
+ };
32
+
33
+ namespace.myComponent = dataBind.init(document.querySelector('[data-bind-comp="temp-component"]'), namespace.viewModel);
34
+
35
+ await namespace.myComponent.render();
36
+ });
37
+
38
+ afterEach(() => {
39
+ // clean up all app/components
40
+ for (const prop in namespace) {
41
+ if (Object.prototype.hasOwnProperty.call(namespace, prop)) {
42
+ delete namespace[prop];
43
+ }
44
+ }
45
+ });
46
+
47
+ it('Then [data-bind-comp="temp-component"] should have render', async () => {
48
+ await waitFor(() => {
49
+ expect(document.getElementById('heading')!.textContent).toBe(namespace.viewModel.heading);
50
+ // afterTemplateRender is called once per template completion
51
+ // With nested templates now working, it's called multiple times
52
+ expect(namespace.finishTemplateRender).toBeGreaterThan(0);
53
+ }, {timeout: 500});
54
+ });
55
+
56
+ it('Should render template1 with viewModel data', async () => {
57
+ await waitFor(() => {
58
+ const viewModel = namespace.viewModel;
59
+ const $compSection = document.getElementById('compSection')!;
60
+ const $heading = document.getElementById('heading')!;
61
+ const $description = document.getElementById('description')!;
62
+ const $content = document.getElementById('content')!;
63
+ const $contentNest1 = document.getElementById('contentNest1')!;
64
+
65
+ expect($compSection.children.length).not.toBe(0);
66
+ expect($heading.textContent).toBe(viewModel.heading);
67
+ expect($description.textContent).toBe(viewModel.description);
68
+ expect($content.textContent).toBe(viewModel.content);
69
+ expect($contentNest1.children.length).not.toBe(0);
70
+ }, {timeout: 500});
71
+ });
72
+
73
+
74
+ it('Should render nested templates with standard dataBindings', async () => {
75
+ await waitFor(() => {
76
+ const viewModel = namespace.viewModel;
77
+ const $compSection = document.getElementById('compSection')!;
78
+ const $contentNest1 = $compSection.querySelector('#contentNest1')!;
79
+ const $contentNest1Info = $compSection.querySelector('#contentNest1Info')!;
80
+ const $nestPrepend = $compSection.querySelector('#nestPrepend')!;
81
+ const $nestAppend = $compSection.querySelector('#nestAppend')!;
82
+
83
+ expect($contentNest1.children.length).not.toBe(0);
84
+ expect($contentNest1Info.textContent).toBe(viewModel.contentNest1.info);
85
+ expect($nestPrepend.children.length).toBeGreaterThan(1);
86
+ expect($nestAppend.children.length).toBeGreaterThan(1);
87
+ }, {timeout: 2000});
88
+ });
89
+
90
+
91
+ it('Should render nested template prepend option', async () => {
92
+ await waitFor(() => {
93
+ const viewModel = namespace.viewModel;
94
+ const $compSection = document.getElementById('compSection')!;
95
+ const $nestPrepend = $compSection.querySelector('#nestPrepend')!;
96
+ const $nestPrependHeading = $compSection.querySelector('#nestPrependHeading');
97
+ const $contentNest2Info = $compSection.querySelector('#contentNest2Info')!;
98
+
99
+ expect($nestPrepend.children.length).toBeGreaterThan(1);
100
+ expect($nestPrependHeading).not.toBe(null);
101
+ expect($nestPrependHeading!.previousElementSibling).toBe($contentNest2Info);
102
+ expect($contentNest2Info.textContent).toBe(viewModel.contentNest2.info);
103
+ }, {timeout: 2000});
104
+ });
105
+
106
+
107
+ it('Should render nested template append option', async () => {
108
+ await waitFor(() => {
109
+ const viewModel = namespace.viewModel;
110
+ const $compSection = document.getElementById('compSection')!;
111
+ const $nestAppend = $compSection.querySelector('#nestAppend')!;
112
+ const $nestAppendHeading = $compSection.querySelector('#nestAppendHeading');
113
+ const $contentNest3Info = $compSection.querySelector('#contentNest3Info')!;
114
+
115
+ expect($nestAppend.children.length).toBeGreaterThan(1);
116
+ expect($nestAppendHeading).not.toBe(null);
117
+ expect($nestAppendHeading!.nextElementSibling).toBe($contentNest3Info);
118
+ expect($contentNest3Info.textContent).toBe(viewModel.contentNest3.info);
119
+ }, {timeout: 2000});
120
+ });
121
+
122
+ it('Should perform minimal DOM updates on re-render (preserve unchanged elements)', async () => {
123
+ // Wait for initial render
124
+ await waitFor(() => {
125
+ expect(document.getElementById('heading')).not.toBe(null);
126
+ }, {timeout: 500});
127
+
128
+ // Get reference to existing DOM elements
129
+ const $headingBefore = document.getElementById('heading');
130
+ const $descriptionBefore = document.getElementById('description');
131
+ const $contentBefore = document.getElementById('content');
132
+
133
+ // Mark elements to track if they are replaced
134
+ $headingBefore!.setAttribute('data-test-marker', 'original-heading');
135
+ $descriptionBefore!.setAttribute('data-test-marker', 'original-description');
136
+ $contentBefore!.setAttribute('data-test-marker', 'original-content');
137
+
138
+ // Change only one property in viewModel
139
+ namespace.viewModel.content = 'updated text';
140
+
141
+ // Re-render
142
+ await namespace.myComponent.render();
143
+
144
+ await waitFor(() => {
145
+ const $headingAfter = document.getElementById('heading');
146
+ const $descriptionAfter = document.getElementById('description');
147
+ const $contentAfter = document.getElementById('content');
148
+
149
+ // Verify heading and description are the SAME DOM elements (not replaced)
150
+ expect($headingAfter).toBe($headingBefore);
151
+ expect($descriptionAfter).toBe($descriptionBefore);
152
+
153
+ // These elements should still have their original markers
154
+ expect($headingAfter!.getAttribute('data-test-marker')).toBe('original-heading');
155
+ expect($descriptionAfter!.getAttribute('data-test-marker')).toBe('original-description');
156
+
157
+ // Content should be the same element but with updated text
158
+ expect($contentAfter).toBe($contentBefore);
159
+ expect($contentAfter!.textContent).toBe('updated text');
160
+ expect($contentAfter!.getAttribute('data-test-marker')).toBe('original-content');
161
+ }, {timeout: 500});
162
+ });
163
+
164
+ it('Should replace only changed attributes during re-render', async () => {
165
+ // Wait for initial render
166
+ await waitFor(() => {
167
+ expect(document.getElementById('heading')).not.toBe(null);
168
+ }, {timeout: 500});
169
+
170
+ const $heading = document.getElementById('heading');
171
+
172
+ // Add a custom attribute
173
+ $heading!.setAttribute('data-custom', 'custom-value');
174
+
175
+ // Store reference to check if it's the same DOM node
176
+ const originalHeading = $heading;
177
+
178
+ // Change heading text
179
+ namespace.viewModel.heading = 'Updated heading text';
180
+
181
+ // Re-render
182
+ await namespace.myComponent.render();
183
+
184
+ await waitFor(() => {
185
+ const $headingAfter = document.getElementById('heading');
186
+
187
+ // Should be the same DOM element
188
+ expect($headingAfter).toBe(originalHeading);
189
+
190
+ // Text should be updated
191
+ expect($headingAfter!.textContent).toBe('Updated heading text');
192
+
193
+ // Original attributes should remain
194
+ expect($headingAfter!.className).toBe('display-4');
195
+ expect($headingAfter!.getAttribute('data-bind-text')).toBe('heading');
196
+ }, {timeout: 500});
197
+ });
198
+
199
+ it('Should preserve root template elements but allow nested templates to re-render', async () => {
200
+ // Wait for initial render with nested templates
201
+ await waitFor(() => {
202
+ expect(document.getElementById('contentNest1Info')).not.toBe(null);
203
+ }, {timeout: 2000});
204
+
205
+ // Get references to root and nested template elements
206
+ const $contentNest1 = document.getElementById('contentNest1');
207
+ const $heading = document.getElementById('heading');
208
+
209
+ // Mark root template container element
210
+ $contentNest1!.setAttribute('data-test-nested1', 'original-nest1-container');
211
+ $heading!.setAttribute('data-test-heading', 'original-heading');
212
+
213
+ // Update nested property
214
+ namespace.viewModel.contentNest1.info = 'Updated nested content 1';
215
+
216
+ // Re-render
217
+ await namespace.myComponent.render();
218
+
219
+ await waitFor(() => {
220
+ const $contentNest1After = document.getElementById('contentNest1');
221
+ const $headingAfter = document.getElementById('heading');
222
+ const $contentNest1InfoAfter = document.getElementById('contentNest1Info');
223
+
224
+ // Root template elements should be preserved (minimal DOM updates)
225
+ expect($contentNest1After).toBe($contentNest1);
226
+ expect($headingAfter).toBe($heading);
227
+ expect($contentNest1After!.getAttribute('data-test-nested1')).toBe('original-nest1-container');
228
+ expect($headingAfter!.getAttribute('data-test-heading')).toBe('original-heading');
229
+
230
+ // Nested template content should be updated
231
+ expect($contentNest1InfoAfter!.textContent).toBe('Updated nested content 1');
232
+ }, {timeout: 2000});
233
+ });
234
+
235
+ it('Should preserve root template structure across multiple re-renders', async () => {
236
+ // Wait for initial render
237
+ await waitFor(() => {
238
+ expect(document.getElementById('heading')).not.toBe(null);
239
+ }, {timeout: 500});
240
+
241
+ const $heading = document.getElementById('heading');
242
+ const originalHeading = $heading;
243
+
244
+ // Multiple re-renders with different data
245
+ namespace.viewModel.heading = 'First update';
246
+ await namespace.myComponent.render();
247
+
248
+ await waitFor(() => {
249
+ const $element1 = document.getElementById('heading');
250
+ expect($element1).toBe(originalHeading);
251
+ expect($element1!.textContent).toBe('First update');
252
+ }, {timeout: 500});
253
+
254
+ namespace.viewModel.heading = 'Second update';
255
+ await namespace.myComponent.render();
256
+
257
+ await waitFor(() => {
258
+ const $element2 = document.getElementById('heading');
259
+ expect($element2).toBe(originalHeading);
260
+ expect($element2!.textContent).toBe('Second update');
261
+ }, {timeout: 500});
262
+
263
+ namespace.viewModel.heading = 'Third update';
264
+ await namespace.myComponent.render();
265
+
266
+ await waitFor(() => {
267
+ const $element3 = document.getElementById('heading');
268
+ // Should still be the same DOM element after 3 re-renders
269
+ expect($element3).toBe(originalHeading);
270
+ expect($element3!.textContent).toBe('Third update');
271
+ }, {timeout: 500});
272
+ });
273
+ });