Dom.spec.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. describe('Handsontable.Dom', () => {
  2. describe('offset', () => {
  3. var $window = $(window),
  4. $forceScrollbar = $('<div id="forceScrollbar"></div>').css({
  5. position: 'absolute',
  6. height: '4000px',
  7. width: '4000px',
  8. top: 0,
  9. left: 0
  10. });
  11. beforeEach(function() {
  12. $forceScrollbar.appendTo(document.body);
  13. this.$div = $('<div id="test"></div>').appendTo($forceScrollbar);
  14. this.div = this.$div[0];
  15. });
  16. afterEach(function() {
  17. this.$div.remove();
  18. $forceScrollbar.remove();
  19. });
  20. describe('top', () => {
  21. it('should return offset top with position absolute', function() {
  22. this.$div.css({position: 'absolute', top: 200});
  23. expect(Handsontable.dom.offset(this.div).top).toEqual(200);
  24. });
  25. it('should return offset top with position absolute & scrolled window', function() {
  26. this.$div.css({position: 'absolute', top: 200});
  27. $window.scrollTop(1900);
  28. expect(Handsontable.dom.offset(this.div).top).toEqual(200);
  29. $window.scrollTop(0);
  30. });
  31. it('should return offset top with position fixed', function() {
  32. this.$div.css({position: 'fixed', top: 200});
  33. expect(Handsontable.dom.offset(this.div).top).toEqual(200);
  34. });
  35. it('should return offset top with position fixed & scrolled window', function() {
  36. this.$div.css({position: 'fixed', top: 200});
  37. $window.scrollTop(1900);
  38. expect(Handsontable.dom.offset(this.div).top).toEqual(2100); // this is the same jQuery offset returns
  39. $window.scrollTop(0);
  40. });
  41. });
  42. describe('left', () => {
  43. it('should return offset left with position absolute', function() {
  44. this.$div.css({position: 'absolute', left: 200});
  45. expect(Handsontable.dom.offset(this.div).left).toEqual(200);
  46. });
  47. it('should return offset left with position absolute & scrolled window', function() {
  48. this.$div.css({position: 'absolute', left: 200});
  49. $window.scrollLeft(1900);
  50. expect(Handsontable.dom.offset(this.div).left).toEqual(200);
  51. $window.scrollLeft(0);
  52. });
  53. it('should return offset left with position fixed', function() {
  54. this.$div.css({position: 'fixed', left: 200});
  55. expect(Handsontable.dom.offset(this.div).left).toEqual(200);
  56. });
  57. it('should return offset left with position fixed & scrolled window', function() {
  58. this.$div.css({position: 'fixed', left: 200});
  59. $window.scrollLeft(1900);
  60. expect(Handsontable.dom.offset(this.div).left).toEqual(2100); // this is the same jQuery offset returns
  61. $window.scrollLeft(0);
  62. });
  63. });
  64. });
  65. describe('isVisible', () => {
  66. it('should return true for appended table', () => {
  67. var $table = $('<table></table>').appendTo('body');
  68. expect(Handsontable.dom.isVisible($table[0])).toBe(true);
  69. $table.remove();
  70. });
  71. it('should return false for not appended table', () => {
  72. var $table = $('<table></table>');
  73. expect(Handsontable.dom.isVisible($table[0])).toBe(false);
  74. $table.remove();
  75. });
  76. it('should return false for table with `display: none`', () => {
  77. var $table = $('<table style="display: none"></table>').appendTo('body');
  78. expect(Handsontable.dom.isVisible($table[0])).toBe(false);
  79. $table.remove();
  80. });
  81. it('should return false for table with parent `display: none`', () => {
  82. var $div = $('<div style="display: none"></div>').appendTo('body');
  83. var $table = $('<table></table>').appendTo($div);
  84. expect(Handsontable.dom.isVisible($table[0])).toBe(false);
  85. $table.remove();
  86. });
  87. it('should return false for something detached from DOM', () => {
  88. var $table = $('<table><tr><td></td></tr></table>').appendTo('body');
  89. var TD = $table.find('td')[0];
  90. var TR = TD.parentNode;
  91. expect(Handsontable.dom.isVisible(TD)).toBe(true);
  92. TR.parentNode.removeChild(TR);
  93. expect(Handsontable.dom.isVisible(TD)).toBe(false);
  94. $table.remove();
  95. });
  96. });
  97. describe('outerHeight', () => {
  98. it('should return correct outerHeight for table', () => {
  99. var $table = $('<table style="border-width: 0;"><tbody><tr><td style="border: 1px solid black"><div style="height: 30px">test</div></td>' +
  100. '</tr></tbody></table>').appendTo('body');
  101. expect(Handsontable.dom.outerHeight($table[0])).toBe(38); // this is according to current stylesheet
  102. expect($table.outerHeight()).toBe(38); // jQuery check to confirm
  103. $table.remove();
  104. });
  105. it('should return correct outerHeight for table (with caption)', () => {
  106. var $table = $('<table style="border-width: 0;"><caption style="padding: 0; margin:0"><div style="height: 30px">caption</div></caption><tbody>' +
  107. '<tr><td style="border: 1px solid black"><div style="height: 30px">test</div></td></tr></tbody></table>').appendTo('body');
  108. expect(Handsontable.dom.outerHeight($table[0])).toBe(68); // this is according to current stylesheet
  109. $table.remove();
  110. });
  111. });
  112. it('should return correct offset for table cell (table with caption)', () => {
  113. var $table = $('<table style="border-width: 0;"><caption style="padding: 0; margin:0"><div style="height: 30px">caption</div></caption><tbody>' +
  114. '<tr><td style="border: 1px solid black"><div style="height: 30px">test</div></td></tr></tbody></table>').appendTo('body');
  115. var tableOffset = Handsontable.dom.offset($table[0]);
  116. var tdOffset = Handsontable.dom.offset($table.find('td')[0]);
  117. expect(parseInt(tdOffset.left - tableOffset.left, 10)).toBeAroundValue(2); // this is according to current stylesheet
  118. expect(parseInt(tdOffset.top - tableOffset.top, 10)).toBeAroundValue(32); // this is according to current stylesheet
  119. $table.remove();
  120. });
  121. it('should return font size', () => {
  122. var $html = $('<style>.bigText{font: 12px serif;}</style><div class="bigText"><span id="testable"></span></div>').appendTo('body');
  123. var span = document.getElementById('testable');
  124. var compStyle = Handsontable.dom.getComputedStyle(span);
  125. expect(compStyle.fontSize).toBe('12px');
  126. $html.remove();
  127. });
  128. it('should return top border width', () => {
  129. var $html = $('<style>.redBorder{border: 10px solid red;}</style><div class="redBorder" id="testable"></div>').appendTo('body');
  130. var div = document.getElementById('testable');
  131. var compStyle = Handsontable.dom.getComputedStyle(div);
  132. expect(compStyle.borderTopWidth).toBe('10px');
  133. $html.remove();
  134. });
  135. it('should insert HTML properly', () => {
  136. var $html = $('<div id="testable"></div>').appendTo('body');
  137. var text = '<span>test<br>test</span>';
  138. var div = document.getElementById('testable');
  139. Handsontable.dom.fastInnerHTML(div, text);
  140. Handsontable.dom.fastInnerHTML(div, text);
  141. expect(div.childNodes[0].childNodes.length).toEqual(3);
  142. $html.remove();
  143. });
  144. it('should set the immediatePropagation properties properly for given event', () => {
  145. var event = document.createEvent('MouseEvents');
  146. event.initMouseEvent('mousedown', true, true, window, null, null, null, null, null, null, null, null, null, null, null);
  147. Handsontable.dom.stopImmediatePropagation(event);
  148. expect(event.isImmediatePropagationEnabled).toBe(false);
  149. expect(Handsontable.dom.isImmediatePropagationStopped(event)).toBe(true);
  150. });
  151. describe('getScrollableElement', () => {
  152. it('should return scrollable element with \'scroll\' value of \'overflow\', \'overflowX\' or \'overflowY\' property', () => {
  153. var $html = $([
  154. '<div style="overflow: scroll"><span class="overflow"></span></div>',
  155. '<div style="overflow-x: scroll"><span class="overflowX"></span></div>',
  156. '<div style="overflow-y: scroll"><span class="overflowY"></span></div>'
  157. ].join('')).appendTo('body');
  158. expect(Handsontable.dom.getScrollableElement($html.find('.overflow')[0])).toBe($html[0]);
  159. expect(Handsontable.dom.getScrollableElement($html.find('.overflowX')[0])).toBe($html[1]);
  160. expect(Handsontable.dom.getScrollableElement($html.find('.overflowY')[0])).toBe($html[2]);
  161. $html.remove();
  162. });
  163. it('should return scrollable element with \'auto\' value of \'overflow\' or \'overflowY\' property', () => {
  164. var $html = $([
  165. '<div style="overflow: auto; height: 50px;"><div class="knob" style="height: 100px"></div></div>',
  166. '<div style="overflow-y: auto; height: 50px;"><div class="knob" style="height: 100px"></div></div>',
  167. '<div style="overflow-y: auto; height: 50px;">',
  168. '<div>',
  169. '<div class="knob" style="height: 100px;"></div>',
  170. '</div>',
  171. '</div>'
  172. ].join('')).appendTo('body');
  173. expect(Handsontable.dom.getScrollableElement($html.find('.knob')[0])).toBe($html[0]);
  174. expect(Handsontable.dom.getScrollableElement($html.find('.knob')[1])).toBe($html[1]);
  175. expect(Handsontable.dom.getScrollableElement($html.find('.knob')[2])).toBe($html[2]);
  176. $html.remove();
  177. });
  178. it('should return scrollable element with \'auto\' value of \'overflow\' or \'overflowX\' property', () => {
  179. var $html = $([
  180. '<div style="overflow: auto; width: 50px; height: 10px"><div class="knob" style="width: 100px; height: 5px"></div></div>',
  181. '<div style="overflow-x: auto; width: 50px; height: 10px"><div class="knob" style="width: 100px; height: 5px"></div></div>',
  182. '<div style="overflow-x: auto; width: 50px; height: 10px">',
  183. '<div>',
  184. '<div class="knob" style="width: 100px; height: 5px"></div>',
  185. '</div>',
  186. '</div>'
  187. ].join('')).appendTo('body');
  188. expect(Handsontable.dom.getScrollableElement($html.find('.knob')[0])).toBe($html[0]);
  189. expect(Handsontable.dom.getScrollableElement($html.find('.knob')[1])).toBe($html[1]);
  190. expect(Handsontable.dom.getScrollableElement($html.find('.knob')[2])).toBe($html[2]);
  191. $html.remove();
  192. });
  193. it('should return window object as scrollable element', () => {
  194. var $html = $([
  195. '<div style="overflow: hidden; width: 50px; height: 10px"><div class="knob" style="width: 100px; height: 5px"></div></div>',
  196. '<div style="width: 50px; height: 10px"><div class="knob" style="width: 100px; height: 5px"></div></div>'
  197. ].join('')).appendTo('body');
  198. expect(Handsontable.dom.getScrollableElement($html.find('.knob')[0])).toBe(window);
  199. expect(Handsontable.dom.getScrollableElement($html.find('.knob')[1])).toBe(window);
  200. $html.remove();
  201. });
  202. });
  203. //
  204. // Handsontable.dom.isChildOfWebComponentTable
  205. //
  206. describe('isChildOfWebComponentTable', () => {
  207. it('should return correct Boolean value depending on whether an element exists in `hot-table` or not', () => {
  208. // skip if browser not support Shadow DOM natively
  209. if (!document.createElement('div').createShadowRoot) {
  210. // Fix for "no exceptations" warnings
  211. expect(true).toBe(true);
  212. return;
  213. }
  214. var hotTable = document.createElement('hot-table');
  215. var outsideDiv = document.createElement('div');
  216. expect(Handsontable.dom.isChildOfWebComponentTable(hotTable)).toBe(true);
  217. expect(Handsontable.dom.isChildOfWebComponentTable(outsideDiv)).toBe(false);
  218. var hotTableDiv = document.createElement('div');
  219. hotTable.appendChild(hotTableDiv);
  220. expect(Handsontable.dom.isChildOfWebComponentTable(hotTableDiv)).toBe(true);
  221. var fragment = document.createDocumentFragment();
  222. expect(Handsontable.dom.isChildOfWebComponentTable(fragment)).toBe(false);
  223. var myElement = document.createElement('my-element');
  224. expect(Handsontable.dom.isChildOfWebComponentTable(myElement)).toBe(false);
  225. var shadowRoot = myElement.createShadowRoot();
  226. var insideDiv = shadowRoot.appendChild(document.createElement('div'));
  227. hotTable.createShadowRoot().appendChild(myElement);
  228. expect(Handsontable.dom.isChildOfWebComponentTable(myElement)).toBe(true);
  229. expect(Handsontable.dom.isChildOfWebComponentTable(insideDiv)).toBe(true);
  230. });
  231. });
  232. //
  233. // Handsontable.dom.polymerWrap
  234. //
  235. describe('polymerWrap', () => {
  236. it('should wrap element into polymer wrapper if exists', () => {
  237. expect(Handsontable.dom.polymerWrap(1)).toBe(1);
  238. window.wrap = function() { return 'wrapped'; };
  239. window.Polymer = {};
  240. expect(Handsontable.dom.polymerWrap(1)).toBe('wrapped');
  241. // Test https://github.com/handsontable/handsontable/issues/2283
  242. window.wrap = document.createElement('div');
  243. expect(Handsontable.dom.polymerWrap(1)).toBe(1);
  244. delete window.wrap;
  245. delete window.Polymer;
  246. });
  247. });
  248. //
  249. // Handsontable.dom.polymerUnwrap
  250. //
  251. describe('polymerUnwrap', () => {
  252. it('should unwrap element from polymer wrapper if exists', () => {
  253. expect(Handsontable.dom.polymerUnwrap('wrapped')).toBe('wrapped');
  254. window.unwrap = function() { return 1; };
  255. window.Polymer = {};
  256. expect(Handsontable.dom.polymerUnwrap('wrapped')).toBe(1);
  257. window.unwrap = document.createElement('div');
  258. expect(Handsontable.dom.polymerUnwrap('wrapped')).toBe('wrapped');
  259. delete window.unwrap;
  260. delete window.Polymer;
  261. });
  262. });
  263. //
  264. // Handsontable.dom.addClass
  265. //
  266. describe('addClass', () => {
  267. it('should add class names as string to an element', () => {
  268. var element = document.createElement('div');
  269. expect(element.className).toBe('');
  270. Handsontable.dom.addClass(element, 'test');
  271. expect(element.className).toBe('test');
  272. Handsontable.dom.addClass(element, 'test test1 test2');
  273. expect(element.className).toBe('test test1 test2');
  274. Handsontable.dom.addClass(element, 'test3');
  275. expect(element.className).toBe('test test1 test2 test3');
  276. Handsontable.dom.addClass(element, '');
  277. expect(element.className).toBe('test test1 test2 test3');
  278. });
  279. it('should add class names as array to an element', () => {
  280. var element = document.createElement('div');
  281. expect(element.className).toBe('');
  282. Handsontable.dom.addClass(element, ['test']);
  283. expect(element.className).toBe('test');
  284. Handsontable.dom.addClass(element, ['test1', 'test2', 'test3']);
  285. expect(element.className).toBe('test test1 test2 test3');
  286. Handsontable.dom.addClass(element, 'test4');
  287. expect(element.className).toBe('test test1 test2 test3 test4');
  288. Handsontable.dom.addClass(element, '');
  289. expect(element.className).toBe('test test1 test2 test3 test4');
  290. });
  291. });
  292. //
  293. // Handsontable.dom.removeClass
  294. //
  295. describe('removeClass', () => {
  296. it('should remove class names as string from an element', () => {
  297. var element = document.createElement('div');
  298. element.className = 'test test1 test2 test3 test4';
  299. Handsontable.dom.removeClass(element, 'not-exists');
  300. expect(element.className).toBe('test test1 test2 test3 test4');
  301. Handsontable.dom.removeClass(element, 'test');
  302. expect(element.className).toBe('test1 test2 test3 test4');
  303. Handsontable.dom.removeClass(element, 'test test1 test4');
  304. expect(element.className).toBe('test2 test3');
  305. Handsontable.dom.removeClass(element, '');
  306. expect(element.className).toBe('test2 test3');
  307. });
  308. it('should remove class names as array from an element', () => {
  309. var element = document.createElement('div');
  310. element.className = 'test test1 test2 test3 test4';
  311. Handsontable.dom.removeClass(element, ['not-exists']);
  312. expect(element.className).toBe('test test1 test2 test3 test4');
  313. Handsontable.dom.removeClass(element, ['test']);
  314. expect(element.className).toBe('test1 test2 test3 test4');
  315. Handsontable.dom.removeClass(element, ['test', 'test1', 'test4']);
  316. expect(element.className).toBe('test2 test3');
  317. Handsontable.dom.removeClass(element, ['test', '', '']);
  318. expect(element.className).toBe('test2 test3');
  319. });
  320. });
  321. //
  322. // Handsontable.dom.hasClass
  323. //
  324. describe('hasClass', () => {
  325. it('should checks if an element has passed class name', () => {
  326. var element = document.createElement('div');
  327. element.className = 'test test1 test2 test3 test4';
  328. expect(Handsontable.dom.hasClass(element, 'not-exists')).toBe(false);
  329. expect(Handsontable.dom.hasClass(element, 'test3')).toBe(true);
  330. expect(Handsontable.dom.hasClass(element, 'test')).toBe(true);
  331. expect(Handsontable.dom.hasClass(element, '')).toBe(false);
  332. });
  333. });
  334. });