@gogocat/data-bind 1.11.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 (274) 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 +2772 -2519
  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/form.html +20 -4
  147. package/examples/globalConfig.html +131 -0
  148. package/examples/js/afterRenderDemo.js +190 -0
  149. package/examples/js/appTodo.js +46 -46
  150. package/examples/js/attrBindingDemo.js +2 -2
  151. package/examples/js/dbMonApp.js +24 -26
  152. package/examples/js/dbMonAppReact.jsx +79 -0
  153. package/examples/js/dbMonAppReactive.js +28 -0
  154. package/examples/js/fiberDemo.js +4 -4
  155. package/examples/js/filtersDemo.js +8 -8
  156. package/examples/js/forOfDemo.js +7 -9
  157. package/examples/js/forOfDemoComplex.js +44 -17
  158. package/examples/js/form.js +44 -12
  159. package/examples/js/globalConfig.js +117 -0
  160. package/examples/js/ifBindingDemo.js +16 -16
  161. package/examples/js/reactiveDemo.js +119 -0
  162. package/examples/js/switchBindingDemo.js +8 -8
  163. package/examples/react-dbmonster/dist/bundle.js +43 -0
  164. package/examples/react-dbmonster/package-lock.json +537 -0
  165. package/examples/react-dbmonster/package.json +16 -0
  166. package/examples/react-dbmonster/src/index.jsx +80 -0
  167. package/examples/reactiveDemo.html +127 -0
  168. package/examples/refreshRateTest.html +75 -75
  169. package/index.html +841 -0
  170. package/package.json +31 -34
  171. package/rollup.config.js +79 -36
  172. package/src/{_escape.js → _escape.ts} +19 -17
  173. package/src/applyBinding.ts +179 -0
  174. package/src/{attrBinding.js → attrBinding.ts} +14 -13
  175. package/src/binder.ts +289 -0
  176. package/src/changeBinding.ts +93 -0
  177. package/src/{commentWrapper.js → commentWrapper.ts} +33 -30
  178. package/src/config.ts +107 -0
  179. package/src/createBindingOption.ts +91 -0
  180. package/src/createEventBinding.ts +88 -0
  181. package/src/{cssBinding.js → cssBinding.ts} +13 -11
  182. package/src/{domWalker.js → domWalker.ts} +44 -30
  183. package/src/{forOfBinding.js → forOfBinding.ts} +4 -3
  184. package/src/hoverBinding.ts +84 -0
  185. package/src/{ifBinding.js → ifBinding.ts} +14 -12
  186. package/src/index.ts +53 -0
  187. package/src/{modelBinding.js → modelBinding.ts} +11 -9
  188. package/src/postProcess.ts +22 -0
  189. package/src/{pubSub.js → pubSub.ts} +24 -15
  190. package/src/reactiveProxy.ts +285 -0
  191. package/src/{renderForOfBinding.js → renderForOfBinding.ts} +55 -33
  192. package/src/{renderIfBinding.js → renderIfBinding.ts} +45 -20
  193. package/src/renderIteration.ts +53 -0
  194. package/src/renderTemplate.ts +165 -0
  195. package/src/renderTemplatesBinding.ts +73 -0
  196. package/src/{showBinding.js → showBinding.ts} +4 -3
  197. package/src/{switchBinding.js → switchBinding.ts} +18 -15
  198. package/src/{textBinding.js → textBinding.ts} +5 -4
  199. package/src/types.ts +124 -0
  200. package/src/util.ts +810 -0
  201. package/test/css/reporter.css +9 -9
  202. package/test/fixtures/dataBindBootstrap.html +2 -2
  203. package/test/fixtures/formBindings.html +9 -1
  204. package/test/globals.d.ts +19 -0
  205. package/test/helpers/testHelper.js +46 -11
  206. package/test/mocks/featureAdsResult.json +65 -65
  207. package/test/mocks/searchResult.json +57 -57
  208. package/test/specs/{attrBinding.spec.js → attrBinding.spec.ts} +103 -106
  209. package/test/specs/{binder.spec.js → binder.spec.ts} +29 -27
  210. package/test/specs/blurBinding.spec.ts +60 -0
  211. package/test/specs/chainableUse.spec.ts +125 -0
  212. package/test/specs/clickBinding.spec.ts +194 -0
  213. package/test/specs/{cssBinding.spec.js → cssBinding.spec.ts} +72 -79
  214. package/test/specs/{dataBindBootstrap.spec.js → dataBindBootstrap.spec.ts} +332 -313
  215. package/test/specs/{filter.spec.js → filter.spec.ts} +75 -76
  216. package/test/specs/{forOfBinding.spec.js → forOfBinding.spec.ts} +208 -219
  217. package/test/specs/formBinding.spec.ts +272 -0
  218. package/test/specs/ifBinding.spec.ts +165 -0
  219. package/test/specs/{nestedComponent.spec.js → nestedComponent.spec.ts} +88 -88
  220. package/test/specs/reactiveProxy.spec.ts +465 -0
  221. package/test/specs/{showBinding.spec.js → showBinding.spec.ts} +148 -149
  222. package/test/specs/{switchBinding.spec.js → switchBinding.spec.ts} +172 -173
  223. package/test/specs/templateBinding.spec.ts +273 -0
  224. package/test/specs/{textBinding.spec.js → textBinding.spec.ts} +47 -48
  225. package/test/tsconfig.json +31 -0
  226. package/test-output.txt +200 -0
  227. package/test-reactive.html +224 -0
  228. package/tsconfig.json +28 -0
  229. package/vendors/lodash.custom.js +4577 -4577
  230. package/vendors/lodash.custom.min.js +45 -45
  231. package/vitest.config.js +27 -0
  232. package/.eslintrc.js +0 -1
  233. package/.grunt/grunt-contrib-jasmine/boot.js +0 -161
  234. package/.grunt/grunt-contrib-jasmine/dist/js/dataBind.js +0 -9
  235. package/.grunt/grunt-contrib-jasmine/grunt-template-jasmine-istanbul/reporter.js +0 -23
  236. package/.grunt/grunt-contrib-jasmine/jasmine-html.js +0 -853
  237. package/.grunt/grunt-contrib-jasmine/jasmine.css +0 -271
  238. package/.grunt/grunt-contrib-jasmine/jasmine.js +0 -9761
  239. package/.grunt/grunt-contrib-jasmine/jasmine_favicon.png +0 -0
  240. package/.grunt/grunt-contrib-jasmine/json2.js +0 -489
  241. package/.grunt/grunt-contrib-jasmine/reporter.js +0 -107
  242. package/coverage/coverage.json +0 -1
  243. package/coverage/lcov/lcov-report/base.css +0 -213
  244. package/coverage/lcov/lcov-report/index.html +0 -93
  245. package/coverage/lcov/lcov-report/js/dataBind.js.html +0 -6596
  246. package/coverage/lcov/lcov-report/js/index.html +0 -93
  247. package/coverage/lcov/lcov-report/prettify.css +0 -1
  248. package/coverage/lcov/lcov-report/prettify.js +0 -1
  249. package/coverage/lcov/lcov-report/sort-arrow-sprite.png +0 -0
  250. package/coverage/lcov/lcov-report/sorter.js +0 -158
  251. package/coverage/lcov/lcov.info +0 -1991
  252. package/eslintrc.json +0 -40
  253. package/examples/bootstrap/js/bootstrap.min.js +0 -6
  254. package/examples/bootstrap/js/popper.min.js +0 -5
  255. package/examples/bootstrap/js/searchSuggestion.js +0 -58
  256. package/examples/bootstrap/js/typeahead.jquery.js +0 -1538
  257. package/gruntfile.js +0 -92
  258. package/gulpfile.js +0 -32
  259. package/src/binder.js +0 -422
  260. package/src/changeBinding.js +0 -57
  261. package/src/config.js +0 -65
  262. package/src/createBindingOption.js +0 -66
  263. package/src/createEventBinding.js +0 -46
  264. package/src/eventSystem.js +0 -46
  265. package/src/hoverBinding.js +0 -57
  266. package/src/index.js +0 -26
  267. package/src/renderTemplate.js +0 -128
  268. package/src/util.js +0 -648
  269. package/test/specs/blurBinding.spec.js +0 -57
  270. package/test/specs/formBinding.spec.js +0 -292
  271. package/test/specs/ifBinding.spec.js +0 -169
  272. package/test/specs/templateBinding.spec.js +0 -117
  273. package/vendors/jasmine-jquery.js +0 -841
  274. package/vendors/jquery-3.2.1.min.js +0 -4
@@ -1,313 +1,332 @@
1
- /* eslint-disable max-len */
2
- describe('Given dataBindBootstrp initised', () => {
3
- const namespace = {};
4
- const converResultsData = function(data) {
5
- ret = [];
6
- data.forEach(function(item, index) {
7
- const newItem = $.extend({}, item);
8
- if (newItem.bookmarked) {
9
- newItem.bookmarkedCss = 'active';
10
- }
11
- if (newItem.highlight) {
12
- newItem.highlightCss = 'result-item--highlight';
13
- }
14
- ret.push(newItem);
15
- });
16
- return ret;
17
- };
18
-
19
- jasmine.getFixtures().fixturesPath = 'test';
20
-
21
- beforeEach(() => {
22
- const searchUrl = '/test/mocks/searchResult.json';
23
- // var featureAdsResultUrl = '/test/mocks/featureAdsResult.json';
24
- const el = {
25
- searchResultColumns: '#search-result-columns',
26
- messageModal: '#message-modal',
27
- messageTextArea: '#message',
28
- };
29
-
30
- loadFixtures('./fixtures/dataBindBootstrap.html');
31
-
32
- $.each(el, function(k, v) {
33
- el[k] = $(v);
34
- });
35
-
36
- namespace.searchBarComponentVM = {
37
- searchWord: '',
38
- searchLocation: '',
39
- searching: false,
40
- onSearchWordChange: function(e, $el, newValue, oldValue) {
41
- console.log('onSearchWordChange');
42
- expect(typeof e.preventDefault).toBe('function');
43
- expect($el instanceof jQuery).toBe(true);
44
- expect(newValue).not.toBe(oldValue);
45
- },
46
- onSearchLocationChange: function(e, $el, newValue, oldValue) {
47
- expect(typeof e.preventDefault).toBe('function');
48
- expect($el instanceof jQuery).toBe(true);
49
- expect(newValue).not.toBe(oldValue);
50
- },
51
- onSearchSubmit: function(e, $form, formData) {
52
- e.preventDefault();
53
- expect($form instanceof jQuery).toBe(true);
54
- expect($form[0].tagName).toBe('FORM');
55
- expect($.isPlainObject(formData)).toBe(true);
56
-
57
- // TODO: may be test publish even elsewhere
58
- formData.searchWord = formData.searchWord.trim();
59
- formData.location = formData.location.trim();
60
- namespace.searchBarComponent.publish('SEARCH-AD', formData);
61
-
62
- this.searching = true;
63
- this.updateStatus();
64
- },
65
- onSearchCompleted: function() {
66
- this.searching = false;
67
- this.updateStatus();
68
- },
69
- updateStatus: function() {
70
- namespace.searchBarComponent.render();
71
- },
72
- };
73
-
74
- namespace.searchResultsComponentVM = {
75
- searchResultTitle: 'Featured service providers',
76
- messageTriggerCss: '',
77
- bookmarkCss: '',
78
- searchResults: [],
79
- selectedResults: [],
80
- startIndex: -1,
81
- loading: false,
82
- showMore: false,
83
- replacedInitResults: false,
84
- isNewSearch: false,
85
- currentQuery: null,
86
- getSearchResults: function(formData) {
87
- const self = this;
88
-
89
- this.isNewSearch = JSON.stringify(self.currentQuery) !== JSON.stringify(formData);
90
-
91
- self.currentQuery = formData;
92
-
93
- $.getJSON(searchUrl, self.currentQuery)
94
- .done(function(data) {
95
- // mock network delay
96
- setTimeout(function() {
97
- self.loading = false;
98
- self.onSearchResult(data);
99
- namespace.searchResultsComponent.publish('SEARCH-COMPLETED', data);
100
- }, 20);
101
- })
102
- .fail(function(jqXHR, textStatus, errorThrown) {
103
- this.loading = false;
104
- namespace.searchResultsComponent.publish('SEARCH-COMPLETED', {
105
- fail: errorThrown,
106
- });
107
- self.logError(jqXHR, textStatus, errorThrown);
108
- });
109
- },
110
- onSearchResult: function(data) {
111
- let newResults = [];
112
-
113
- if (this.isNewSearch) {
114
- el.searchResultColumns.empty();
115
- this.searchResults.length = 0;
116
- }
117
- // first search result - remove current results (featured items)
118
- if (namespace.searchResultsComponent.isServerRendered && !this.replacedInitResults) {
119
- this.searchResultTitle = 'Search results';
120
- this.replacedInitResults = true;
121
- } else {
122
- // using append template logic - results will be append into existing results
123
- // mark startIndex using current last item index
124
- this.startIndex = this.searchResults.length - 1;
125
- }
126
- // covert raw data to searchResults view data
127
- newResults = converResultsData(data);
128
- // add new results into current searchResults
129
- this.searchResults = this.searchResults.concat(newResults);
130
- // flag display showMore button
131
- this.showMore = true;
132
- // update template which use startIndex to append new content
133
- this.updateStatus({
134
- templateBinding: true,
135
- });
136
- },
137
- logError: function() {
138
- console.warn('search result error: ', ...rest);
139
- },
140
- onMoreResults: function() {
141
- // get same mock result as example only
142
- this.loading = true;
143
- this.updateStatus();
144
- this.getSearchResults();
145
- },
146
- onAdBookmarkClick: function(e, $el) {
147
- const activeCss = 'active';
148
- const isActivated = $el.hasClass(activeCss);
149
- const resultIndex = $el.attr('data-index');
150
-
151
- this.searchResults[resultIndex].bookmarked = !isActivated;
152
- this.searchResults[resultIndex].bookmarkedCss = isActivated ? '' : 'active';
153
- this.updateStatus();
154
- },
155
- onAdMessageCheck: function(e, $el, newValue, oldValue) {
156
- expect(newValue).not.toBe(oldValue);
157
- this.updateStatus();
158
- },
159
- onMessageTriggerClick: function(e, $el) {
160
- namespace.searchResultsComponent.publish('TRIGGER-MESSAGE-DIALOG', this.selectedResults);
161
- console.log('onMessageTriggerClick: ', $el);
162
- },
163
- updateStatus: function(opt) {
164
- this.selectedResults = this.searchResults.filter(function(result, index) {
165
- return result.selected;
166
- });
167
- this.messageTriggerCss = this.selectedResults.length ? 'show' : '';
168
- namespace.searchResultsComponent.render(opt);
169
- },
170
- };
171
-
172
- namespace.messageDialogComponentVM = {
173
- numSelectedProviders: '',
174
- selectedAdData: [],
175
- adIds: [],
176
- selectedProviders: '',
177
- defaultMessageText: 'Hi,\nCould you please provide a quote for the following work:\n',
178
- sendingMessage: false,
179
- onMessageSubmit: function(e, $el, formData) {
180
- e.preventDefault();
181
- formData.ids = this.adIds;
182
- this.sendingMessage = true;
183
- this.updateStatus();
184
- console.log('post data: ', formData);
185
- },
186
- onTriggerSelectedAds: function(selectedAdData) {
187
- this.selectedAdData = selectedAdData;
188
- this.numSelectedProviders = selectedAdData.length > 1 ? selectedAdData.length : '';
189
- this.selectedProviders = selectedAdData.length > 1 ? 'advertisers ID: ' : 'advertiser ID:';
190
- this.adIds = selectedAdData.map(function(item, index) {
191
- return item.id;
192
- });
193
- this.selectedProviders += this.adIds.toString();
194
- this.updateStatus();
195
- },
196
- updateStatus: function() {
197
- namespace.messageDialogComponent.render();
198
- },
199
- };
200
-
201
- namespace.searchBarComponent = dataBind.init(document.querySelector('[data-bind-comp="search-bar"]'), namespace.searchBarComponentVM);
202
- namespace.searchBarComponent.render().then(function(ctx) {
203
- const self = ctx;
204
- namespace.searchBarComponent.subscribe('SEARCH-COMPLETED', self.viewModel.onSearchCompleted);
205
- });
206
-
207
- namespace.searchResultsComponent = dataBind.init(
208
- document.querySelector('[data-bind-comp="search-results-component"]'),
209
- namespace.searchResultsComponentVM,
210
- );
211
- namespace.searchResultsComponent
212
- .render() // overwrite default server rendered option
213
- .then(function(ctx) {
214
- const self = ctx;
215
- // subscribe events
216
- namespace.searchResultsComponent.subscribe('SEARCH-AD', self.viewModel.getSearchResults);
217
- });
218
-
219
- namespace.messageDialogComponent = dataBind.init(
220
- document.querySelector('[data-bind-comp="message-dialog-component"]'),
221
- namespace.messageDialogComponentVM,
222
- );
223
- namespace.messageDialogComponent.render().then(function(ctx) {
224
- const self = ctx;
225
- namespace.messageDialogComponent.subscribe('TRIGGER-MESSAGE-DIALOG', self.viewModel.onTriggerSelectedAds);
226
-
227
- el.messageModal.on('shown.bs.modal', function() {
228
- el.messageTextArea[0].defaultValue = self.viewModel.defaultMessageText;
229
- el.messageTextArea.focus();
230
- });
231
-
232
- el.messageModal.on('hidden.bs.modal', function() {
233
- el.messageTextArea[0].defaultValue = self.viewModel.defaultMessageText;
234
- self.viewModel.sendingMessage = false;
235
- });
236
- });
237
-
238
- // jasmine spies
239
- spyOn(namespace.searchBarComponentVM, 'onSearchWordChange');
240
- spyOn(namespace.searchBarComponentVM, 'onSearchLocationChange');
241
- // spyOn(namespace.searchBarComponentVM, 'onSearchSubmit');
242
- });
243
-
244
- afterEach(() => {
245
- // clean up all app/components
246
- for (const prop in namespace) {
247
- if (namespace.hasOwnProperty(prop)) {
248
- delete namespace[prop];
249
- }
250
- }
251
- });
252
-
253
- it('Then [data-bind-comp="search-bar"] should has bond with namespace.searchBarComponentVM', (done) => {
254
- const $searchBar = $('[data-bind-comp="search-bar"]');
255
- const searchBarRoot = $searchBar[0]['$root'];
256
-
257
- setTimeout(function() {
258
- expect(searchBarRoot).toBeDefined();
259
- expect(searchBarRoot).toEqual(jasmine.objectContaining(namespace.searchBarComponentVM));
260
- done();
261
- }, 200);
262
- });
263
-
264
- it('When [data-bind-comp="search-bar"] was server rendered then show binding should not update element', (done) => {
265
- setTimeout(function() {
266
- expect(namespace.searchBarComponent.isServerRendered).toBe(true);
267
- expect(namespace.searchBarComponentVM.searching).toBe(false);
268
- expect($('.spinner--search').is(':visible')).toBe(true);
269
- done();
270
- }, 300);
271
- });
272
-
273
- it('When change #searchWord input value to "Handyman" viewModel onSearchWordChange should have been called', (done) => {
274
- const newSearchWord = 'Handyman';
275
- setTimeout(function() {
276
- const $searchInput = document.getElementById('searchWord');
277
- const evt = document.createEvent('HTMLEvents');
278
- evt.initEvent('change', true, true);
279
-
280
- $searchInput.value = newSearchWord;
281
- $searchInput.dispatchEvent(evt);
282
-
283
- expect(namespace.searchBarComponentVM.onSearchWordChange).toHaveBeenCalled();
284
- expect(namespace.searchBarComponentVM.searchWord).toEqual(newSearchWord);
285
- namespace.searchBarComponentVM.onSearchWordChange.calls.reset();
286
- done();
287
- }, 200);
288
- });
289
-
290
- it('When change #location input value to "Melbourne" viewModel onSearchLocationChange should have been called', (done) => {
291
- const newLocation = 'Melbourne';
292
- setTimeout(function() {
293
- const $locationInput = document.getElementById('location');
294
- const evt = document.createEvent('HTMLEvents');
295
- evt.initEvent('change', true, true);
296
-
297
- $locationInput.value = newLocation;
298
- $locationInput.dispatchEvent(evt);
299
-
300
- expect(namespace.searchBarComponentVM.onSearchLocationChange).toHaveBeenCalled();
301
- expect(namespace.searchBarComponentVM.searchLocation).toEqual(newLocation);
302
- namespace.searchBarComponentVM.onSearchLocationChange.calls.reset();
303
- done();
304
- }, 200);
305
- });
306
-
307
- it('When [data-bind-comp="search-bar"] was server rendered "data-server-rendered" attribute should have removed', (done) => {
308
- setTimeout(function() {
309
- expect($('[data-bind-comp="search-bar"]').attr('data-server-rendered')).not.toBeDefined();
310
- done();
311
- }, 200);
312
- });
313
- });
1
+ import {describe, it, expect, beforeEach, afterEach, vi} from 'vitest';
2
+ import {waitFor} from '@testing-library/dom';
3
+
4
+
5
+ describe('Given dataBindBootstrp initised', () => {
6
+ const namespace: any = {};
7
+ const converResultsData = function (data: any[]) {
8
+ const ret: any[] = [];
9
+ data.forEach((item, _index) => {
10
+ const newItem = Object.assign({}, item);
11
+ if (newItem.bookmarked) {
12
+ newItem.bookmarkedCss = 'active';
13
+ }
14
+ if (newItem.highlight) {
15
+ newItem.highlightCss = 'result-item--highlight';
16
+ }
17
+ ret.push(newItem);
18
+ });
19
+ return ret;
20
+ };
21
+
22
+ beforeEach(async () => {
23
+ const searchUrl = '/test/mocks/searchResult.json';
24
+ // var featureAdsResultUrl = '/test/mocks/featureAdsResult.json';
25
+ const el: any = {
26
+ searchResultColumns: '#search-result-columns',
27
+ messageModal: '#message-modal',
28
+ messageTextArea: '#message',
29
+ };
30
+
31
+ loadFixture('test/fixtures/dataBindBootstrap.html');
32
+
33
+ for (const k in el) {
34
+ if (Object.prototype.hasOwnProperty.call(el, k)) {
35
+ const v = el[k];
36
+ el[k] = document.querySelector(v);
37
+ }
38
+ }
39
+
40
+ namespace.searchBarComponentVM = {
41
+ searchWord: '',
42
+ searchLocation: '',
43
+ searching: false,
44
+ onSearchWordChange(e: Event, $el: any, newValue: any, oldValue: any) {
45
+ console.log('onSearchWordChange');
46
+ expect(typeof e.preventDefault).toBe('function');
47
+ expect($el instanceof HTMLElement || $el instanceof Element).toBe(true);
48
+ expect(newValue).not.toBe(oldValue);
49
+ },
50
+ onSearchLocationChange(e: Event, $el: any, newValue: any, oldValue: any) {
51
+ expect(typeof e.preventDefault).toBe('function');
52
+ expect($el instanceof HTMLElement || $el instanceof Element).toBe(true);
53
+ expect(newValue).not.toBe(oldValue);
54
+ },
55
+ onSearchSubmit(e: Event, $form: any, formData: any) {
56
+ e.preventDefault();
57
+ expect($form instanceof HTMLElement || $form instanceof Element).toBe(true);
58
+ expect($form.tagName).toBe('FORM');
59
+ expect(typeof formData === 'object' && formData !== null).toBe(true);
60
+
61
+ // TODO: may be test publish even elsewhere
62
+ formData.searchWord = formData.searchWord.trim();
63
+ formData.location = formData.location.trim();
64
+ namespace.searchBarComponent.publish('SEARCH-AD', formData);
65
+
66
+ this.searching = true;
67
+ this.updateStatus();
68
+ },
69
+ onSearchCompleted() {
70
+ this.searching = false;
71
+ this.updateStatus();
72
+ },
73
+ updateStatus() {
74
+ namespace.searchBarComponent.render();
75
+ },
76
+ };
77
+
78
+ namespace.searchResultsComponentVM = {
79
+ searchResultTitle: 'Featured service providers',
80
+ messageTriggerCss: '',
81
+ bookmarkCss: '',
82
+ searchResults: [],
83
+ selectedResults: [],
84
+ startIndex: -1,
85
+ loading: false,
86
+ showMore: false,
87
+ replacedInitResults: false,
88
+ isNewSearch: false,
89
+ currentQuery: null,
90
+ getSearchResults(formData?: any) {
91
+ const self = this;
92
+
93
+ this.isNewSearch = JSON.stringify(self.currentQuery) !== JSON.stringify(formData);
94
+
95
+ self.currentQuery = formData;
96
+
97
+ // Note: $ is jQuery, used in test fixture
98
+
99
+ $.getJSON(searchUrl, self.currentQuery)
100
+ .done((data: any) => {
101
+ // mock network delay
102
+ setTimeout(() => {
103
+ self.loading = false;
104
+ self.onSearchResult(data);
105
+ namespace.searchResultsComponent.publish('SEARCH-COMPLETED', data);
106
+ }, 20);
107
+ })
108
+ .fail(function (jqXHR: any, textStatus: any, errorThrown: any) {
109
+ this.loading = false;
110
+ namespace.searchResultsComponent.publish('SEARCH-COMPLETED', {
111
+ fail: errorThrown,
112
+ });
113
+ self.logError(jqXHR, textStatus, errorThrown);
114
+ });
115
+ },
116
+ onSearchResult(data: any) {
117
+ let newResults = [];
118
+
119
+ if (this.isNewSearch) {
120
+ el.searchResultColumns.empty();
121
+ this.searchResults.length = 0;
122
+ }
123
+ // first search result - remove current results (featured items)
124
+ if (namespace.searchResultsComponent.isServerRendered && !this.replacedInitResults) {
125
+ this.searchResultTitle = 'Search results';
126
+ this.replacedInitResults = true;
127
+ } else {
128
+ // using append template logic - results will be append into existing results
129
+ // mark startIndex using current last item index
130
+ this.startIndex = this.searchResults.length - 1;
131
+ }
132
+ // covert raw data to searchResults view data
133
+ newResults = converResultsData(data);
134
+ // add new results into current searchResults
135
+ this.searchResults = this.searchResults.concat(newResults);
136
+ // flag display showMore button
137
+ this.showMore = true;
138
+ // update template which use startIndex to append new content
139
+ this.updateStatus({
140
+ templateBinding: true,
141
+ });
142
+ },
143
+ logError(...args: any[]) {
144
+ console.warn('search result error: ', ...args);
145
+ },
146
+ onMoreResults() {
147
+ // get same mock result as example only
148
+ this.loading = true;
149
+ this.updateStatus();
150
+ this.getSearchResults();
151
+ },
152
+ onAdBookmarkClick(e: Event, $el: any) {
153
+ const activeCss = 'active';
154
+ const isActivated = $el.hasClass(activeCss);
155
+ const resultIndex = $el.getAttribute('data-index');
156
+
157
+ this.searchResults[resultIndex].bookmarked = !isActivated;
158
+ this.searchResults[resultIndex].bookmarkedCss = isActivated ? '' : 'active';
159
+ this.updateStatus();
160
+ },
161
+ onAdMessageCheck(e: Event, $el: any, newValue: any, oldValue: any) {
162
+ expect(newValue).not.toBe(oldValue);
163
+ this.updateStatus();
164
+ },
165
+ onMessageTriggerClick(e: Event, $el: any) {
166
+ namespace.searchResultsComponent.publish('TRIGGER-MESSAGE-DIALOG', this.selectedResults);
167
+ console.log('onMessageTriggerClick: ', $el);
168
+ },
169
+ updateStatus(opt?: any) {
170
+ this.selectedResults = this.searchResults.filter((result: any, _index: number) => {
171
+ return result.selected;
172
+ });
173
+ this.messageTriggerCss = this.selectedResults.length ? 'show' : '';
174
+ namespace.searchResultsComponent.render(opt);
175
+ },
176
+ };
177
+
178
+ namespace.messageDialogComponentVM = {
179
+ numSelectedProviders: '',
180
+ selectedAdData: [],
181
+ adIds: [],
182
+ selectedProviders: '',
183
+ defaultMessageText: 'Hi,\nCould you please provide a quote for the following work:\n',
184
+ sendingMessage: false,
185
+ onMessageSubmit(e: Event, $el: any, formData: any) {
186
+ e.preventDefault();
187
+ formData.ids = this.adIds;
188
+ this.sendingMessage = true;
189
+ this.updateStatus();
190
+ console.log('post data: ', formData);
191
+ },
192
+ onTriggerSelectedAds(selectedAdData: any[]) {
193
+ this.selectedAdData = selectedAdData;
194
+ this.numSelectedProviders = selectedAdData.length > 1 ? selectedAdData.length : '';
195
+ this.selectedProviders = selectedAdData.length > 1 ? 'advertisers ID: ' : 'advertiser ID:';
196
+ this.adIds = selectedAdData.map((item, _index) => {
197
+ return item.id;
198
+ });
199
+ this.selectedProviders += this.adIds.toString();
200
+ this.updateStatus();
201
+ },
202
+ updateStatus() {
203
+ namespace.messageDialogComponent.render();
204
+ },
205
+ };
206
+
207
+ // vitest spies - set up BEFORE render so they intercept the event handlers
208
+ vi.spyOn(namespace.searchBarComponentVM, 'onSearchWordChange');
209
+ vi.spyOn(namespace.searchBarComponentVM, 'onSearchLocationChange');
210
+ // vi.spyOn(namespace.searchBarComponentVM, 'onSearchSubmit');
211
+
212
+ namespace.searchBarComponent = dataBind.init(document.querySelector('[data-bind-comp="search-bar"]'), namespace.searchBarComponentVM);
213
+ await namespace.searchBarComponent.render().then((ctx: any) => {
214
+ const self = ctx;
215
+ namespace.searchBarComponent.subscribe('SEARCH-COMPLETED', self.viewModel.onSearchCompleted);
216
+ });
217
+
218
+ namespace.searchResultsComponent = dataBind.init(
219
+ document.querySelector('[data-bind-comp="search-results-component"]'),
220
+ namespace.searchResultsComponentVM,
221
+ );
222
+ await namespace.searchResultsComponent
223
+ .render() // overwrite default server rendered option
224
+ .then((ctx: any) => {
225
+ const self = ctx;
226
+ // subscribe events
227
+ namespace.searchResultsComponent.subscribe('SEARCH-AD', self.viewModel.getSearchResults);
228
+ });
229
+
230
+ namespace.messageDialogComponent = dataBind.init(
231
+ document.querySelector('[data-bind-comp="message-dialog-component"]'),
232
+ namespace.messageDialogComponentVM,
233
+ );
234
+ await namespace.messageDialogComponent.render().then((ctx: any) => {
235
+ const self = ctx;
236
+ namespace.messageDialogComponent.subscribe('TRIGGER-MESSAGE-DIALOG', self.viewModel.onTriggerSelectedAds);
237
+
238
+ if (el.messageModal) {
239
+ el.messageModal.addEventListener('shown.bs.modal', () => {
240
+ if (el.messageTextArea) {
241
+ el.messageTextArea.defaultValue = self.viewModel.defaultMessageText;
242
+ el.messageTextArea.focus();
243
+ }
244
+ });
245
+
246
+ el.messageModal.addEventListener('hidden.bs.modal', () => {
247
+ if (el.messageTextArea) {
248
+ el.messageTextArea.defaultValue = self.viewModel.defaultMessageText;
249
+ self.viewModel.sendingMessage = false;
250
+ }
251
+ });
252
+ }
253
+ });
254
+ });
255
+
256
+ afterEach(() => {
257
+ // clean up all app/components
258
+ for (const prop in namespace) {
259
+ if (Object.prototype.hasOwnProperty.call(namespace, prop)) {
260
+ delete namespace[prop];
261
+ }
262
+ }
263
+ vi.restoreAllMocks();
264
+ });
265
+
266
+ it('Then [data-bind-comp="search-bar"] should has bond with namespace.searchBarComponentVM', async () => {
267
+ const $searchBar = document.querySelector('[data-bind-comp="search-bar"]') as any;
268
+ const searchBarRoot = $searchBar['$root'];
269
+
270
+ await waitFor(() => {
271
+ expect(searchBarRoot).toBeDefined();
272
+ expect(searchBarRoot).toEqual(expect.objectContaining(namespace.searchBarComponentVM));
273
+ }, {timeout: 200});
274
+ });
275
+
276
+ it('When [data-bind-comp="search-bar"] was server rendered then show binding should not update element', async () => {
277
+ await waitFor(() => {
278
+ expect(namespace.searchBarComponent.isServerRendered).toBe(true);
279
+ expect(namespace.searchBarComponentVM.searching).toBe(false);
280
+ expect((document.querySelector('.spinner--search') as HTMLElement)!.style.display !== 'none').toBe(true);
281
+ }, {timeout: 500});
282
+ });
283
+
284
+ it('When change #searchWord input value to "Handyman" viewModel onSearchWordChange should have been called', async () => {
285
+ const newSearchWord = 'Handyman';
286
+
287
+ await waitFor(() => {
288
+ const $searchInput = document.getElementById('searchWord');
289
+ expect($searchInput).not.toBeNull();
290
+ }, {timeout: 500});
291
+
292
+ const $searchInput = document.getElementById('searchWord') as HTMLInputElement;
293
+ const evt = document.createEvent('HTMLEvents');
294
+ evt.initEvent('change', true, true);
295
+
296
+ $searchInput.value = newSearchWord;
297
+ $searchInput.dispatchEvent(evt);
298
+
299
+ await waitFor(() => {
300
+ expect(namespace.searchBarComponentVM.onSearchWordChange).toHaveBeenCalled();
301
+ expect(namespace.searchBarComponentVM.searchWord).toEqual(newSearchWord);
302
+ }, {timeout: 500});
303
+ });
304
+
305
+ it('When change #location input value to "Melbourne" viewModel onSearchLocationChange should have been called', async () => {
306
+ const newLocation = 'Melbourne';
307
+
308
+ await waitFor(() => {
309
+ const $locationInput = document.getElementById('location');
310
+ expect($locationInput).not.toBeNull();
311
+ }, {timeout: 500});
312
+
313
+ const $locationInput = document.getElementById('location') as HTMLInputElement;
314
+ const evt = document.createEvent('HTMLEvents');
315
+ evt.initEvent('change', true, true);
316
+
317
+ $locationInput.value = newLocation;
318
+ $locationInput.dispatchEvent(evt);
319
+
320
+ await waitFor(() => {
321
+ expect(namespace.searchBarComponentVM.onSearchLocationChange).toHaveBeenCalled();
322
+ expect(namespace.searchBarComponentVM.searchLocation).toEqual(newLocation);
323
+ }, {timeout: 500});
324
+ });
325
+
326
+ it('When [data-bind-comp="search-bar"] was server rendered "data-server-rendered" attribute should have removed', async () => {
327
+ await waitFor(() => {
328
+ const searchBar = document.querySelector('[data-bind-comp="search-bar"]');
329
+ expect(searchBar!.getAttribute('data-server-rendered')).toBeNull();
330
+ }, {timeout: 500});
331
+ });
332
+ });