drag.js 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. 'use strict';
  2. var test = require('tape');
  3. var events = require('./lib/events');
  4. var dragula = require('..');
  5. test('drag event gets emitted when clicking an item', function (t) {
  6. testCase('works for left clicks', { which: 1 });
  7. testCase('works for wheel clicks', { which: 1 });
  8. testCase('works when clicking buttons by default', { which: 1 }, { tag: 'button', passes: true });
  9. testCase('works when clicking anchors by default', { which: 1 }, { tag: 'a', passes: true });
  10. testCase('fails for right clicks', { which: 2 }, { passes: false });
  11. testCase('fails for meta-clicks', { which: 1, metaKey: true }, { passes: false });
  12. testCase('fails for ctrl-clicks', { which: 1, ctrlKey: true }, { passes: false });
  13. testCase('fails when clicking containers', { which: 1 }, { containerClick: true, passes: false });
  14. testCase('fails whenever invalid returns true', { which: 1 }, { passes: false, dragulaOpts: { invalid: always } });
  15. testCase('fails whenever moves returns false', { which: 1 }, { passes: false, dragulaOpts: { moves: never } });
  16. t.end();
  17. function testCase (desc, eventOptions, options) {
  18. t.test(desc, function subtest (st) {
  19. var o = options || {};
  20. var div = document.createElement('div');
  21. var item = document.createElement(o.tag || 'div');
  22. var passes = o.passes !== false;
  23. var drake = dragula([div], o.dragulaOpts);
  24. div.appendChild(item);
  25. document.body.appendChild(div);
  26. drake.on('drag', drag);
  27. events.raise(o.containerClick ? div : item, 'mousedown', eventOptions);
  28. events.raise(o.containerClick ? div : item, 'mousemove');
  29. st.plan(passes ? 4 : 1);
  30. st.equal(drake.dragging, passes, desc + ': final state is drake is ' + (passes ? '' : 'not ') + 'dragging');
  31. st.end();
  32. function drag (target, container) {
  33. st[passes ? 'pass' : 'fail'](desc + ': drag event was emitted synchronously');
  34. st.equal(target, item, desc + ': first argument is selected item');
  35. st.equal(container, div, desc + ': second argument is container');
  36. }
  37. });
  38. }
  39. });
  40. test('when already dragging, mousedown/mousemove ends (cancels) previous drag', function (t) {
  41. var div = document.createElement('div');
  42. var item1 = document.createElement('div');
  43. var item2 = document.createElement('div');
  44. var drake = dragula([div]);
  45. div.appendChild(item1);
  46. div.appendChild(item2);
  47. document.body.appendChild(div);
  48. drake.start(item1);
  49. drake.on('dragend', end);
  50. drake.on('cancel', cancel);
  51. drake.on('drag', drag);
  52. events.raise(item2, 'mousedown', { which: 1 });
  53. events.raise(item2, 'mousemove', { which: 1 });
  54. t.plan(7);
  55. t.equal(drake.dragging, true, 'final state is drake is dragging');
  56. t.end();
  57. function end (item) {
  58. t.equal(item, item1, 'dragend invoked with correct item');
  59. }
  60. function cancel (item, source) {
  61. t.equal(item, item1, 'cancel invoked with correct item');
  62. t.equal(source, div, 'cancel invoked with correct source');
  63. }
  64. function drag (item, container) {
  65. t.pass('drag event was emitted synchronously');
  66. t.equal(item, item2, 'first argument is selected item');
  67. t.equal(container, div, 'second argument is container');
  68. }
  69. });
  70. test('when already dragged, ends (drops) previous drag', function (t) {
  71. var div = document.createElement('div');
  72. var div2 = document.createElement('div');
  73. var item1 = document.createElement('div');
  74. var item2 = document.createElement('div');
  75. var drake = dragula([div, div2]);
  76. div.appendChild(item1);
  77. div.appendChild(item2);
  78. document.body.appendChild(div);
  79. document.body.appendChild(div2);
  80. drake.start(item1);
  81. div2.appendChild(item1);
  82. drake.on('dragend', end);
  83. drake.on('drop', drop);
  84. drake.on('drag', drag);
  85. events.raise(item2, 'mousedown', { which: 1 });
  86. events.raise(item2, 'mousemove', { which: 1 });
  87. t.plan(8);
  88. t.equal(drake.dragging, true, 'final state is drake is dragging');
  89. t.end();
  90. function end (item) {
  91. t.equal(item, item1, 'dragend invoked with correct item');
  92. }
  93. function drop (item, target, source) {
  94. t.equal(item, item1, 'drop invoked with correct item');
  95. t.equal(source, div, 'drop invoked with correct source');
  96. t.equal(target, div2, 'drop invoked with correct target');
  97. }
  98. function drag (item, container) {
  99. t.pass('drag event was emitted synchronously');
  100. t.equal(item, item2, 'first argument is selected item');
  101. t.equal(container, div, 'second argument is container');
  102. }
  103. });
  104. test('when copying, emits cloned with the copy', function (t) {
  105. var div = document.createElement('div');
  106. var item1 = document.createElement('div');
  107. var item2 = document.createElement('span');
  108. var drake = dragula([div], { copy: true });
  109. item2.innerHTML = '<em>the force is <strong>with this one</strong></em>';
  110. div.appendChild(item1);
  111. div.appendChild(item2);
  112. document.body.appendChild(div);
  113. drake.start(item1);
  114. drake.on('cloned', cloned);
  115. drake.on('drag', drag);
  116. events.raise(item2, 'mousedown', { which: 1 });
  117. events.raise(item2, 'mousemove', { which: 1 });
  118. t.plan(12);
  119. t.equal(drake.dragging, true, 'final state is drake is dragging');
  120. t.end();
  121. function cloned (copy, item) {
  122. t.notEqual(copy, item2, 'first argument is not exactly the target');
  123. t.equal(copy.tagName, item2.tagName, 'first argument has same tag as target');
  124. t.equal(copy.innerHTML, item2.innerHTML, 'first argument has same inner html as target');
  125. t.equal(item, item2, 'second argument is clicked item');
  126. }
  127. function drag (item, container) {
  128. t.pass('drag event was emitted synchronously');
  129. t.equal(item, item2, 'first argument is selected item');
  130. t.equal(container, div, 'second argument is container');
  131. }
  132. });
  133. test('when dragging, element gets gu-transit class', function (t) {
  134. var div = document.createElement('div');
  135. var item = document.createElement('div');
  136. dragula([div]);
  137. div.appendChild(item);
  138. document.body.appendChild(div);
  139. events.raise(item, 'mousedown', { which: 1 });
  140. events.raise(item, 'mousemove', { which: 1 });
  141. t.equal(item.className, 'gu-transit', 'item has gu-transit class');
  142. t.end();
  143. });
  144. test('when dragging, body gets gu-unselectable class', function (t) {
  145. var div = document.createElement('div');
  146. var item = document.createElement('div');
  147. dragula([div]);
  148. div.appendChild(item);
  149. document.body.appendChild(div);
  150. events.raise(item, 'mousedown', { which: 1 });
  151. events.raise(item, 'mousemove', { which: 1 });
  152. t.equal(document.body.className, 'gu-unselectable', 'body has gu-unselectable class');
  153. t.end();
  154. });
  155. test('when dragging, element gets a mirror image for show', function (t) {
  156. var div = document.createElement('div');
  157. var item = document.createElement('div');
  158. var drake = dragula([div]);
  159. item.innerHTML = '<em>the force is <strong>with this one</strong></em>';
  160. div.appendChild(item);
  161. document.body.appendChild(div);
  162. drake.on('cloned', cloned);
  163. events.raise(item, 'mousedown', { which: 1 });
  164. events.raise(item, 'mousemove', { which: 1 });
  165. t.plan(4);
  166. t.end();
  167. function cloned (mirror, target) {
  168. t.equal(item.className, 'gu-transit', 'item does not have gu-mirror class');
  169. t.equal(mirror.className, 'gu-mirror', 'mirror only has gu-mirror class');
  170. t.equal(mirror.innerHTML, item.innerHTML, 'mirror is passed to \'cloned\' event');
  171. t.equal(target, item, 'cloned lets you know that the mirror is a clone of `item`');
  172. }
  173. });
  174. test('when dragging, mirror element gets appended to configured mirrorContainer', function (t) {
  175. var mirrorContainer = document.createElement('div');
  176. var div = document.createElement('div');
  177. var item = document.createElement('div');
  178. var drake = dragula([div], {
  179. 'mirrorContainer': mirrorContainer
  180. });
  181. item.innerHTML = '<em>the force is <strong>with this one</strong></em>';
  182. div.appendChild(item);
  183. document.body.appendChild(div);
  184. drake.on('cloned', cloned);
  185. events.raise(item, 'mousedown', { which: 1 });
  186. events.raise(item, 'mousemove', { which: 1 });
  187. t.plan(1);
  188. t.end();
  189. function cloned (mirror) {
  190. t.equal(mirror.parentNode, mirrorContainer, 'mirrors parent is the configured mirrorContainer');
  191. }
  192. });
  193. test('when dragging stops, element gets gu-transit class removed', function (t) {
  194. var div = document.createElement('div');
  195. var item = document.createElement('div');
  196. var drake = dragula([div]);
  197. div.appendChild(item);
  198. document.body.appendChild(div);
  199. events.raise(item, 'mousedown', { which: 1 });
  200. events.raise(item, 'mousemove', { which: 1 });
  201. t.equal(item.className, 'gu-transit', 'item has gu-transit class');
  202. drake.end();
  203. t.equal(item.className, '', 'item has gu-transit class removed');
  204. t.end();
  205. });
  206. test('when dragging stops, body becomes selectable again', function (t) {
  207. var div = document.createElement('div');
  208. var item = document.createElement('div');
  209. var drake = dragula([div]);
  210. div.appendChild(item);
  211. document.body.appendChild(div);
  212. events.raise(item, 'mousedown', { which: 1 });
  213. events.raise(item, 'mousemove', { which: 1 });
  214. t.equal(document.body.className, 'gu-unselectable', 'body has gu-unselectable class');
  215. drake.end();
  216. t.equal(document.body.className, '', 'body got gu-unselectable class removed');
  217. t.end();
  218. });
  219. test('when drag begins, check for copy option', function (t) {
  220. var div = document.createElement('div');
  221. var item = document.createElement('div');
  222. item.className = 'copyable';
  223. div.className = 'contains';
  224. var drake = dragula([div], {
  225. copy: checkCondition
  226. });
  227. item.innerHTML = '<em>the force is <strong>with this one</strong></em>';
  228. div.appendChild(item);
  229. document.body.appendChild(div);
  230. events.raise(item, 'mousedown', { which: 1 });
  231. events.raise(item, 'mousemove', { which: 1 });
  232. events.raise(item, 'mousemove', { which: 1 }); // ensure the copy method condition is only asserted once
  233. t.plan(2);
  234. t.end();
  235. function checkCondition (el, source) {
  236. t.equal(el.className, 'copyable', 'dragged element classname is copyable');
  237. t.equal(source.className, 'contains', 'source container classname is contains');
  238. return true;
  239. }
  240. drake.end();
  241. });
  242. function always () { return true; }
  243. function never () { return false; }