DraggingSpec.js 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. import {
  2. bootstrapDiagram,
  3. inject
  4. } from 'test/TestHelper';
  5. import { createCanvasEvent as canvasEvent } from '../../../util/MockEvents';
  6. import { assign } from 'min-dash';
  7. import dragModule from 'lib/features/dragging';
  8. import zoomScrollModule from 'lib/navigation/zoomscroll';
  9. import createModule from 'lib/features/create';
  10. import modelingModule from 'lib/features/modeling';
  11. import { classes as svgClasses } from 'tiny-svg';
  12. describe('features/dragging - Dragging', function() {
  13. beforeEach(bootstrapDiagram({
  14. modules: [
  15. dragModule
  16. ]
  17. }));
  18. beforeEach(inject(function(canvas) {
  19. canvas.addShape({ id: 'shape', x: 10, y: 10, width: 50, height: 50 });
  20. }));
  21. describe('behavior', function() {
  22. beforeEach(inject(function(dragging) {
  23. dragging.setOptions({ manual: true });
  24. }));
  25. var recordEvents;
  26. beforeEach(inject(function(eventBus) {
  27. recordEvents = function(prefix) {
  28. var events = [];
  29. var eventTypes = [
  30. 'start',
  31. 'move',
  32. 'end',
  33. 'ended',
  34. 'hover',
  35. 'out',
  36. 'cancel',
  37. 'canceled',
  38. 'cleanup',
  39. 'init'
  40. ];
  41. eventTypes.forEach(function(type) {
  42. eventBus.on(prefix + '.' + type, function(e) {
  43. events.push(assign({}, e));
  44. });
  45. });
  46. return events;
  47. };
  48. }));
  49. function raw(e) {
  50. var omitted = assign({}, e);
  51. delete omitted.originalEvent;
  52. delete omitted.previousSelection;
  53. delete omitted.isTouch;
  54. return omitted;
  55. }
  56. it('should stop original event propagation on init', inject(function(dragging) {
  57. // given
  58. var event = canvasEvent({ x: 10, y: 10 });
  59. // when
  60. dragging.init(event, 'foo');
  61. // then
  62. expect(event.cancelBubble).to.be.true;
  63. }));
  64. it('should pass custom data', inject(function(dragging) {
  65. // given
  66. var events = recordEvents('foo');
  67. // when
  68. dragging.init(canvasEvent({ x: 10, y: 10 }), 'foo', {
  69. data: { foo: 'BAR' }
  70. });
  71. // then
  72. expect(events.length).to.equal(1);
  73. expect(events).to.eql([
  74. { foo: 'BAR', type: 'foo.init', isTouch: false }
  75. ]);
  76. }));
  77. describe('trapClick', function() {
  78. it('should prevent default action on drag end', inject(function(dragging) {
  79. // given
  80. dragging.setOptions({
  81. manual: false
  82. });
  83. dragging.init(canvasEvent({ x: 10, y: 10 }), 'foo', { trapClick: true });
  84. dragging.move(canvasEvent({ x: 30, y: 20 }));
  85. // when
  86. var realEvent = mouseDown(document, { x: 20, y: 20 });
  87. // then
  88. expect(realEvent.defaultPrevented).to.be.true;
  89. }));
  90. it('should NOT prevent default action if drag did not start', inject(function(dragging) {
  91. // given
  92. dragging.setOptions({
  93. manual: false
  94. });
  95. dragging.init(canvasEvent({ x: 10, y: 10 }), 'foo', { trapClick: true });
  96. // when
  97. var realEvent = mouseDown(document, { x: 10, y: 10 });
  98. // then
  99. expect(realEvent.defaultPrevented).to.be.false;
  100. }));
  101. // helpers //////////////////
  102. function mouseDown(element, canvasPosition) {
  103. var mockEvent = canvasEvent(canvasPosition);
  104. var event = document.createEvent('MouseEvent');
  105. if (event.initMouseEvent) {
  106. event.initMouseEvent(
  107. 'mousedown', true, true, window, 0, 0, 0,
  108. mockEvent.x, mockEvent.y, false, false, false, false,
  109. 0, null
  110. );
  111. }
  112. element.dispatchEvent(event);
  113. return event;
  114. }
  115. });
  116. it('should fire life-cycle on successful drag', inject(function(dragging, canvas) {
  117. // given
  118. var events = recordEvents('foo');
  119. // when
  120. dragging.init(canvasEvent({ x: 10, y: 10 }), 'foo');
  121. dragging.move(canvasEvent({ x: 30, y: 20 }));
  122. dragging.move(canvasEvent({ x: 5, y: 10 }));
  123. dragging.end();
  124. // then
  125. expect(events.map(raw)).to.eql([
  126. { type: 'foo.init' },
  127. { x: 10, y: 10, dx: 0, dy: 0, type: 'foo.start' },
  128. { x: 30, y: 20, dx: 20, dy: 10, type: 'foo.move' },
  129. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.move' },
  130. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.end' },
  131. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.cleanup' },
  132. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.ended' }
  133. ]);
  134. }));
  135. it('should fire life-cycle events', inject(function(dragging, canvas) {
  136. // given
  137. var events = recordEvents('foo');
  138. // when
  139. dragging.init(canvasEvent({ x: 10, y: 10 }), 'foo');
  140. dragging.move(canvasEvent({ x: 30, y: 20 }));
  141. dragging.move(canvasEvent({ x: 5, y: 10 }));
  142. dragging.cancel();
  143. // then
  144. expect(events.map(raw)).to.eql([
  145. { type: 'foo.init' },
  146. { x: 10, y: 10, dx: 0, dy: 0, type: 'foo.start' },
  147. { x: 30, y: 20, dx: 20, dy: 10, type: 'foo.move' },
  148. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.move' },
  149. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.cancel' },
  150. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.cleanup' },
  151. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.canceled' }
  152. ]);
  153. }));
  154. it('should cancel running', inject(function(dragging) {
  155. // given
  156. var events = recordEvents('foo');
  157. // a is active
  158. dragging.init(canvasEvent({ x: 10, y: 10 }), 'foo', { data: { element: 'a' } });
  159. // when
  160. // activate b
  161. dragging.init(canvasEvent({ x: 10, y: 10 }), 'foo', { data: { element: 'b' } });
  162. // then
  163. expect(events.map(raw)).to.eql([
  164. { element: 'a', type: 'foo.init' },
  165. { element: 'a', type: 'foo.cleanup' },
  166. { element: 'b', type: 'foo.init' }
  167. ]);
  168. }));
  169. describe('djs-drag-active marker', function() {
  170. it('should not add to root on drag start', inject(function(dragging, canvas, elementRegistry) {
  171. // given
  172. var rootElement = canvas.getRootElement(),
  173. rootGfx = elementRegistry.getGraphics(rootElement);
  174. // when
  175. dragging.init(canvasEvent({ x: 10, y: 10 }), 'foo');
  176. // then
  177. expect(svgClasses(rootGfx).has('djs-drag-active')).to.be.false;
  178. // but when
  179. dragging.move(canvasEvent({ x: 30, y: 20 }));
  180. // then
  181. expect(svgClasses(rootGfx).has('djs-drag-active')).to.be.true;
  182. }));
  183. it('should remove from root on drag end', inject(function(dragging, canvas, elementRegistry) {
  184. // given
  185. var rootElement = canvas.getRootElement(),
  186. rootGfx = elementRegistry.getGraphics(rootElement);
  187. // when
  188. dragging.init(canvasEvent({ x: 10, y: 10 }), 'foo');
  189. dragging.move(canvasEvent({ x: 30, y: 20 }));
  190. dragging.cancel();
  191. // then
  192. expect(svgClasses(rootGfx).has('djs-drag-active')).to.be.false;
  193. }));
  194. });
  195. it('should adjust positions to local point', inject(function(dragging) {
  196. // given
  197. var events = recordEvents('foo');
  198. // when
  199. dragging.init(canvasEvent({ x: 0, y: 0 }), { x: 10, y: 10 }, 'foo');
  200. dragging.move(canvasEvent({ x: 20, y: 10 }));
  201. dragging.move(canvasEvent({ x: -5, y: 0 }));
  202. dragging.cancel();
  203. // then
  204. expect(events.map(raw)).to.eql([
  205. { type: 'foo.init' },
  206. { x: 10, y: 10, dx: 0, dy: 0, type: 'foo.start' },
  207. { x: 30, y: 20, dx: 20, dy: 10, type: 'foo.move' },
  208. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.move' },
  209. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.cancel' },
  210. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.cleanup' },
  211. { x: 5, y: 10, dx: -5, dy: 0, type: 'foo.canceled' }
  212. ]);
  213. }));
  214. it('should normalize to full pixel coordinates', inject(
  215. function(dragging, canvas) {
  216. // given
  217. canvas.zoom(0.3713);
  218. // assume viewbox has sub-pixel x coordinate
  219. expect(canvas.viewbox().x).not.to.eql(Math.round(canvas.viewbox().x));
  220. expect(canvas.viewbox().y).not.to.eql(Math.round(canvas.viewbox().y));
  221. var events = recordEvents('foo');
  222. // when
  223. dragging.init(canvasEvent({ x: 0, y: 0 }), { x: 10.12321312, y: 9.9123821 }, 'foo');
  224. dragging.move(canvasEvent({ x: 20, y: 10 }));
  225. dragging.cancel();
  226. // then
  227. expect(events.map(raw)).to.eql([
  228. { type: 'foo.init' },
  229. { x: 10, y: 10, dx: 0, dy: 0, type: 'foo.start' },
  230. { x: 64, y: 37, dx: 54, dy: 27, type: 'foo.move' },
  231. { x: 64, y: 37, dx: 54, dy: 27, type: 'foo.cancel' },
  232. { x: 64, y: 37, dx: 54, dy: 27, type: 'foo.cleanup' },
  233. { x: 64, y: 37, dx: 54, dy: 27, type: 'foo.canceled' }
  234. ]);
  235. }
  236. ));
  237. });
  238. });
  239. describe('features/dragging - Dragging - zoomScroll integration', function() {
  240. beforeEach(bootstrapDiagram({
  241. modules: [
  242. createModule,
  243. dragModule,
  244. modelingModule,
  245. zoomScrollModule
  246. ]
  247. }));
  248. beforeEach(inject(function(dragging) {
  249. dragging.setOptions({ manual: true });
  250. }));
  251. it('should keep shape at cursor when zooming while creating', inject(
  252. function(elementFactory, zoomScroll, dragging, create) {
  253. // given
  254. var shape = elementFactory.createShape({
  255. id: 'shape',
  256. x: 0, y: 0,
  257. width: 50, height: 50
  258. });
  259. // when
  260. create.start(canvasEvent({ x: 0, y: 0 }), shape);
  261. dragging.move(canvasEvent({ x: 100, y: 100 }));
  262. var elementBefore = dragging.context().data.context.visual.getBBox();
  263. zoomScroll.stepZoom(-1.25);
  264. zoomScroll.stepZoom(0.25);
  265. dragging.move(canvasEvent({ x: 100, y: 100 }));
  266. var elementAfter = dragging.context().data.context.visual.getBBox();
  267. // then
  268. expect(elementBefore).to.eql(elementAfter);
  269. }
  270. ));
  271. });