PopupMenuSpec.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125
  1. /* global sinon */
  2. import {
  3. bootstrapDiagram,
  4. getDiagramJS,
  5. inject
  6. } from 'test/TestHelper';
  7. import testImage from './resources/a.png';
  8. import {
  9. query as domQuery,
  10. queryAll as domQueryAll,
  11. classes as domClasses
  12. } from 'min-dom';
  13. import { createEvent as globalEvent } from '../../../util/MockEvents';
  14. import popupMenuModule from 'lib/features/popup-menu';
  15. import modelingModule from 'lib/features/modeling';
  16. function queryEntry(popupMenu, id) {
  17. return queryPopup(popupMenu, '[data-id="' + id + '"]');
  18. }
  19. function queryPopup(popupMenu, selector) {
  20. return domQuery(selector, popupMenu._current.container);
  21. }
  22. var menuProvider = {
  23. getHeaderEntries: function() {
  24. return [
  25. { id: 'entry1', label: 'foo' }
  26. ];
  27. },
  28. getEntries: function() {
  29. return [
  30. { id: 'entry2', label: 'foo' },
  31. { id: 'entry3', label: 'bar' }
  32. ];
  33. }
  34. };
  35. var betterMenuProvider = {
  36. getHeaderEntries: function() {
  37. return [
  38. { id: 'entryA', label: 'A' }
  39. ];
  40. },
  41. getEntries: function() {
  42. return [
  43. { id: 'entryB', label: 'B' }
  44. ];
  45. }
  46. };
  47. describe('features/popup', function() {
  48. beforeEach(bootstrapDiagram({
  49. modules: [
  50. popupMenuModule,
  51. modelingModule
  52. ]
  53. }));
  54. describe('bootstrap', function() {
  55. it('overlay to be defined', inject(function(popupMenu) {
  56. expect(popupMenu).to.exist;
  57. expect(popupMenu.open).to.exist;
  58. }));
  59. });
  60. describe('#registerProvider', function() {
  61. it('should add provider', inject(function(popupMenu) {
  62. // given
  63. var provider = {};
  64. // when
  65. popupMenu.registerProvider('provider', provider);
  66. // then
  67. expect(popupMenu._providers.provider).to.exist;
  68. }));
  69. it('should add two providers', inject(function(popupMenu) {
  70. // given
  71. var provider1 = {};
  72. var provider2 = {};
  73. // when
  74. popupMenu.registerProvider('provider1', provider1);
  75. popupMenu.registerProvider('provider2', provider2);
  76. // then
  77. expect(popupMenu._providers.provider1).to.exist;
  78. expect(popupMenu._providers.provider2).to.exist;
  79. }));
  80. });
  81. describe('#isEmpty', function() {
  82. it('should return true if empty', inject(function(popupMenu) {
  83. // when
  84. popupMenu.registerProvider('empty-menu', {
  85. getEntries: function() { return []; },
  86. getHeaderEntries: function() { return []; }
  87. });
  88. // then
  89. expect(popupMenu.isEmpty({}, 'empty-menu')).to.be.true;
  90. }));
  91. it('should return false if entries', inject(function(popupMenu) {
  92. // when
  93. popupMenu.registerProvider('entry-menu', {
  94. getEntries: function() { return [ { id: 1 } ]; }
  95. });
  96. // then
  97. expect(popupMenu.isEmpty({}, 'entry-menu')).to.be.false;
  98. }));
  99. it('should return false if header entries', inject(function(popupMenu) {
  100. // when
  101. popupMenu.registerProvider('header-entry-menu', {
  102. getEntries: function() { return [ { id: 1 } ]; }
  103. });
  104. // then
  105. expect(popupMenu.isEmpty({}, 'header-entry-menu')).to.be.false;
  106. }));
  107. it('should throw error when provider id is missing', inject(
  108. function(popupMenu) {
  109. // when
  110. popupMenu.registerProvider('header-entry-menu', {
  111. getEntries: function() { return [ { id: 1 } ]; }
  112. });
  113. // then
  114. expect(function() {
  115. popupMenu.isEmpty({});
  116. }).to.throw('providerId parameter is missing');
  117. }
  118. ));
  119. it('should throw error when element is missing', inject(
  120. function(popupMenu) {
  121. // when
  122. popupMenu.registerProvider('header-entry-menu', {
  123. getEntries: function() { return [ { id: 1 } ]; }
  124. });
  125. // then
  126. expect(function() {
  127. popupMenu.isEmpty();
  128. }).to.throw('element parameter is missing');
  129. }
  130. ));
  131. });
  132. describe('#open', function() {
  133. beforeEach(inject(function(popupMenu) {
  134. popupMenu.registerProvider('menu', menuProvider);
  135. }));
  136. it('should open', inject(function(popupMenu, eventBus) {
  137. // given
  138. var openSpy = sinon.spy();
  139. eventBus.on('popupMenu.open', openSpy);
  140. // when
  141. popupMenu.open({}, 'menu', { x: 100, y: 100 });
  142. // then
  143. expect(popupMenu._current).to.exist;
  144. expect(openSpy).to.have.been.calledOnce;
  145. }));
  146. it('should attach popup to html', inject(function(popupMenu) {
  147. // when
  148. popupMenu.open({}, 'menu' ,{ x: 100, y: 100 });
  149. var container = popupMenu._current.container;
  150. // then
  151. expect(domClasses(container).has('djs-popup')).to.be.true;
  152. expect(domClasses(container).has('menu')).to.be.true;
  153. }));
  154. it('should add entries to menu', inject(function(popupMenu) {
  155. // when
  156. popupMenu.open({}, 'menu' ,{ x: 100, y: 100 });
  157. // then
  158. var domEntry = queryEntry(popupMenu, 'entry1');
  159. expect(domEntry.textContent).to.eql('foo');
  160. }));
  161. it('should add action-id to entry', inject(function(popupMenu) {
  162. // when
  163. popupMenu.registerProvider('item-menu', {
  164. getEntries: function() {
  165. return [
  166. { id: 'save', label: 'SAVE' },
  167. { id: 'load', label: 'LOAD' },
  168. { id: 'undo', label: 'UNDO' }
  169. ];
  170. },
  171. getHeaderEntries: function() {}
  172. });
  173. popupMenu.open({}, 'item-menu' ,{ x: 100, y: 100 });
  174. // then
  175. var parent = queryPopup(popupMenu, '.djs-popup-body');
  176. var entry1 = parent.childNodes[0];
  177. var entry2 = parent.childNodes[1];
  178. var entry3 = parent.childNodes[2];
  179. expect(entry1.getAttribute('data-id')).to.eql('save');
  180. expect(entry2.getAttribute('data-id')).to.eql('load');
  181. expect(entry3.getAttribute('data-id')).to.eql('undo');
  182. }));
  183. it('should open menu for specific element', inject(function(popupMenu) {
  184. // when
  185. popupMenu.registerProvider('menu', menuProvider);
  186. popupMenu.open({}, 'menu', { x: 100, y: 100 });
  187. var currentProvider = popupMenu._current.provider;
  188. // then
  189. expect(currentProvider.getHeaderEntries()).to.deep.include({
  190. id: 'entry1',
  191. label: 'foo'
  192. });
  193. expect(currentProvider.getEntries()).to.deep.include({
  194. id: 'entry2',
  195. label: 'foo'
  196. });
  197. }));
  198. it('should throw error when no provider', inject(function(popupMenu) {
  199. // when not registering a provider
  200. // then
  201. expect(function() {
  202. popupMenu.open({}, 'foo', { x: 100, y: 100 });
  203. }).to.throw('Provider is not registered: foo');
  204. }));
  205. it('should throw error when element is missing', inject(function(popupMenu) {
  206. popupMenu.registerProvider('menu', menuProvider);
  207. expect(function() {
  208. popupMenu.open();
  209. }).to.throw('Element is missing');
  210. }));
  211. describe('multiple providers', function() {
  212. beforeEach(inject(function(popupMenu) {
  213. popupMenu.registerProvider('better-menu', betterMenuProvider);
  214. }));
  215. it('should open first menu', inject(function(popupMenu, eventBus) {
  216. // given
  217. var openSpy = sinon.spy();
  218. eventBus.on('popupMenu.open', openSpy);
  219. // when
  220. popupMenu.open({}, 'menu', { x: 100, y: 100 });
  221. // then
  222. expect(popupMenu._current).to.exist;
  223. expect(popupMenu._current.headerEntries[0].id).to.eql('entry1');
  224. expect(popupMenu._current.entries[0].id).to.eql('entry2');
  225. }));
  226. it('should open second menu', inject(function(popupMenu, eventBus) {
  227. // given
  228. var openSpy = sinon.spy();
  229. eventBus.on('popupMenu.open', openSpy);
  230. // when
  231. popupMenu.open({}, 'better-menu', { x: 100, y: 100 });
  232. // then
  233. expect(popupMenu._current).to.exist;
  234. expect(popupMenu._current.headerEntries[0].id).to.eql('entryA');
  235. expect(popupMenu._current.entries[0].id).to.eql('entryB');
  236. }));
  237. });
  238. });
  239. describe('#close', function() {
  240. beforeEach(inject(function(popupMenu) {
  241. popupMenu.registerProvider('menu', menuProvider);
  242. popupMenu.open({}, 'menu' ,{ x: 100, y: 100 });
  243. }));
  244. it('should close', inject(function(popupMenu, eventBus) {
  245. // given
  246. var closeSpy = sinon.spy();
  247. eventBus.on('popupMenu.close', closeSpy);
  248. // when
  249. popupMenu.close();
  250. // then
  251. var open = popupMenu.isOpen();
  252. expect(open).to.be.false;
  253. expect(closeSpy).to.have.been.calledOnce;
  254. }));
  255. it('should not fail if already closed', inject(function(popupMenu) {
  256. // when
  257. popupMenu.close();
  258. // then
  259. expect(popupMenu.close).not.to.throw;
  260. }));
  261. });
  262. describe('#isOpen', function() {
  263. it('should not be open initially', inject(function(popupMenu) {
  264. // when
  265. popupMenu.registerProvider('menu', menuProvider);
  266. // then
  267. expect(popupMenu.isOpen()).to.be.false;
  268. }));
  269. it('should be open after opening', inject(function(popupMenu) {
  270. // when
  271. popupMenu.registerProvider('menu', menuProvider);
  272. popupMenu.open({}, 'menu' ,{ x: 100, y: 100 });
  273. // then
  274. expect(popupMenu.isOpen()).to.be.true;
  275. }));
  276. it('should be closed after closing', inject(function(popupMenu) {
  277. // given
  278. popupMenu.registerProvider('menu', menuProvider);
  279. popupMenu.open({}, 'menu' ,{ x: 100, y: 100 });
  280. // when
  281. popupMenu.close();
  282. // then
  283. expect(popupMenu.isOpen()).to.be.false;
  284. }));
  285. });
  286. describe('#trigger', function() {
  287. it('should trigger the right action handler', inject(function(popupMenu) {
  288. // given
  289. popupMenu.registerProvider('test-menu', {
  290. getEntries: function() {
  291. return [
  292. {
  293. id: '1',
  294. label: 'Entry 1',
  295. className: 'Entry_1',
  296. action: function(event, entry) {
  297. return 'Entry 1';
  298. }
  299. }, {
  300. id: '2',
  301. label: 'Entry 2',
  302. className: 'Entry_2',
  303. action: function(event, entry) {
  304. return 'Entry 2';
  305. }
  306. }
  307. ];
  308. }
  309. });
  310. popupMenu.open({}, 'test-menu' ,{ x: 100, y: 100 });
  311. var entry = queryPopup(popupMenu, '.Entry_2');
  312. // when
  313. var trigger = popupMenu.trigger(globalEvent(entry, { x: 0, y: 0 }));
  314. // then
  315. expect(trigger).to.eql('Entry 2');
  316. }));
  317. });
  318. describe('integration', function() {
  319. describe('events', function() {
  320. it('should close menu (contextPad.close)', inject(function(popupMenu, eventBus) {
  321. // given
  322. popupMenu.registerProvider('menu', menuProvider);
  323. popupMenu.open({}, 'menu' ,{ x: 100, y: 100 });
  324. // when
  325. eventBus.fire('contextPad.close');
  326. // then
  327. var open = popupMenu.isOpen();
  328. expect(open).to.be.false;
  329. }));
  330. it('should close menu (canvas.viewbox.changing)', inject(function(popupMenu, eventBus) {
  331. // given
  332. popupMenu.registerProvider('menu', menuProvider);
  333. popupMenu.open({}, 'menu' ,{ x: 100, y: 100 });
  334. // when
  335. eventBus.fire('canvas.viewbox.changing');
  336. // then
  337. var open = popupMenu.isOpen();
  338. expect(open).to.be.false;
  339. }));
  340. });
  341. });
  342. describe('menu styling', function() {
  343. it('should add standard class to entry', inject(function(popupMenu) {
  344. // given
  345. var testMenuProvider = {
  346. getEntries: function() {
  347. return [
  348. { id: '1', label: 'Entry 1' },
  349. { id: '2', label: 'Entry 2' }
  350. ];
  351. }
  352. };
  353. popupMenu.registerProvider('test-menu', testMenuProvider);
  354. // when
  355. popupMenu.open({}, 'test-menu' ,{ x: 100, y: 100 });
  356. // then
  357. var elements = domQueryAll('.entry', popupMenu._current.container);
  358. expect(elements.length).to.eql(2);
  359. }));
  360. it('should add custom class to entry if specfied', inject(function(popupMenu) {
  361. // given
  362. var testMenuProvider = {
  363. getEntries: function() {
  364. return [
  365. {
  366. id: '1',
  367. label: 'Entry 1'
  368. },
  369. {
  370. id: '2',
  371. label: 'Entry 2 - special',
  372. className: 'special-entry cls2 cls3'
  373. }
  374. ];
  375. }
  376. };
  377. popupMenu.registerProvider('test-menu', testMenuProvider);
  378. // when
  379. popupMenu.open({}, 'test-menu' ,{ x: 100, y: 100 });
  380. // then
  381. var element = queryPopup(popupMenu, '.special-entry');
  382. expect(element.textContent).to.eql('Entry 2 - special');
  383. expect(element.className).to.eql('entry special-entry cls2 cls3');
  384. }));
  385. it('should name the css classes correctly', inject(function(popupMenu) {
  386. // given
  387. var testMenuProvider = {
  388. getEntries: function() {
  389. return [
  390. { id: '1', label: 'Entry 1' },
  391. { id: '2', label: 'Entry 2' }
  392. ];
  393. },
  394. getHeaderEntries: function() {
  395. return [{ id: 'A', label: 'Header Entry A' }];
  396. }
  397. };
  398. popupMenu.registerProvider('test-menu', testMenuProvider);
  399. // when
  400. popupMenu.open({}, 'test-menu' ,{ x: 100, y: 100 });
  401. var popupBody = queryPopup(popupMenu, '.djs-popup-body');
  402. var popupHeader = queryPopup(popupMenu, '.djs-popup-header');
  403. // then
  404. expect(domQueryAll('.entry', popupBody).length).to.eql(2);
  405. expect(domQueryAll('.entry', popupHeader).length).to.eql(1);
  406. }));
  407. it('should look awesome', inject(function(popupMenu) {
  408. // given
  409. var testMenuProvider = {
  410. getEntries: function() {
  411. return [
  412. { id: '1', label: 'Entry 1' },
  413. { id: '2', label: 'Entry 2', active: true },
  414. { id: '3', label: 'Entry 3' },
  415. { id: '4', label: 'Entry 4', disabled: true },
  416. { id: '5', label: 'Entry 5' }
  417. ];
  418. },
  419. getHeaderEntries: function() {
  420. return [
  421. { id: 'A', label: 'A' },
  422. { id: 'B', label: 'B' },
  423. { id: 'C', label: 'C', active: true },
  424. { id: 'D', label: 'D', disabled: true },
  425. { id: 'E', label: 'E', disabled: true }
  426. ];
  427. }
  428. };
  429. popupMenu.registerProvider('test-menu', testMenuProvider);
  430. // when
  431. popupMenu.open({}, 'test-menu' ,{ x: 100, y: 100 });
  432. // then
  433. // looks awesome?
  434. }));
  435. });
  436. describe('singleton handling', function() {
  437. it('should update the popup menu, when it is opened again' , inject(
  438. function(popupMenu) {
  439. // given
  440. var popupMenu1 = {
  441. getEntries: function() {
  442. return [
  443. { id: '1', label: 'Entry 1' },
  444. { id: '2', label: 'Entry 2' }
  445. ];
  446. }
  447. };
  448. popupMenu.registerProvider('popup-menu1', popupMenu1);
  449. var popupMenu2 = {
  450. getEntries: function() {
  451. return [
  452. { id: '3', label: 'Entry A' },
  453. { id: '4', label: 'Entry B' }
  454. ];
  455. }
  456. };
  457. popupMenu.registerProvider('popup-menu2', popupMenu2);
  458. // when
  459. popupMenu.open({}, 'popup-menu1', { x: 100, y: 100 });
  460. popupMenu.open({}, 'popup-menu2', { x: 200, y: 200 });
  461. var container = popupMenu._current.container,
  462. entriesContainer = domQuery('.djs-popup-body', container);
  463. // then
  464. expect(domQuery('.popup-menu1', document)).to.be.null;
  465. expect(domQuery('.popup-menu2', document)).not.to.be.null;
  466. expect(domClasses(container).has('popup-menu2')).to.be.true;
  467. expect(container.style.left).to.eql('200px');
  468. expect(container.style.top).to.eql('200px');
  469. expect(entriesContainer.childNodes[0].textContent).to.eql('Entry A');
  470. expect(entriesContainer.childNodes[1].textContent).to.eql('Entry B');
  471. }
  472. ));
  473. });
  474. describe('header', function() {
  475. it('should throw an error, if the id of a header entry is not set', inject(
  476. function(popupMenu) {
  477. // when
  478. popupMenu.registerProvider('test-menu', {
  479. getEntries: function() {
  480. return [ { label: 'foo' } ];
  481. }
  482. });
  483. // then
  484. expect(function() {
  485. popupMenu.open({}, 'test-menu', { x: 100, y: 100 });
  486. }).to.throw('every entry must have the id property set');
  487. }
  488. ));
  489. it('should be attached to the top of the popup menu, if set' , inject(
  490. function(popupMenu) {
  491. // when
  492. popupMenu.registerProvider('menu', menuProvider);
  493. popupMenu.open({}, 'menu', { x: 100, y: 100 });
  494. // then
  495. expect(queryPopup(popupMenu, '.djs-popup-header')).to.exist;
  496. }
  497. ));
  498. it('should add a custom css class to the header section, if specified', inject(
  499. function(popupMenu) {
  500. var testMenuProvider = {
  501. getHeaderEntries: function() {
  502. return [ { id: '1', className: 'header-entry-1' } ];
  503. },
  504. getEntries: function() {
  505. return [ { id: '2', label: 'foo' } ];
  506. }
  507. };
  508. popupMenu.registerProvider('test-menu', testMenuProvider);
  509. popupMenu.open({}, 'test-menu', { x: 100, y: 100 });
  510. // then
  511. expect(queryPopup(popupMenu, '.header-entry-1')).to.exist;
  512. }
  513. ));
  514. it('should add an image to the header section, if specified', inject(function(popupMenu) {
  515. // given
  516. var testMenuProvider = {
  517. getHeaderEntries: function() {
  518. return [
  519. {
  520. id: '1',
  521. imageUrl: testImage,
  522. className: 'image-1'
  523. }
  524. ];
  525. },
  526. getEntries: function() { return []; }
  527. };
  528. popupMenu.registerProvider('test-menu', testMenuProvider);
  529. popupMenu.open({}, 'test-menu', { x: 100, y: 100 });
  530. // then
  531. var img = queryPopup(popupMenu, '.image-1 img');
  532. expect(img).to.exist;
  533. expect(img.getAttribute('src')).to.eql(testImage);
  534. }));
  535. it('should add a labeled element to the header section, if specified', inject(
  536. function(popupMenu) {
  537. var testMenuProvider = {
  538. getHeaderEntries: function() {
  539. return [ { id: '1', label: 'foo', className: 'label-1' } ];
  540. },
  541. getEntries: function() { return []; }
  542. };
  543. popupMenu.registerProvider('test-menu', testMenuProvider);
  544. popupMenu.open({}, 'test-menu', { x: 100, y: 100 });
  545. // then
  546. var headerEntry = queryPopup(popupMenu, '.label-1');
  547. expect(headerEntry.textContent).to.eql('foo');
  548. }
  549. ));
  550. it('should throw an error if the position argument is missing', inject(
  551. function(popupMenu) {
  552. popupMenu.registerProvider('menu', menuProvider);
  553. // then
  554. expect(function() {
  555. popupMenu.open({}, 'menu');
  556. }).to.throw('the position argument is missing');
  557. }
  558. ));
  559. it('should only render body if entries exist', inject(function(popupMenu) {
  560. // when
  561. var testMenuProvider = {
  562. getEntries: function() {
  563. return [ ]; }
  564. };
  565. popupMenu.registerProvider('test-menu', testMenuProvider);
  566. popupMenu.open({}, 'test-menu', { x: 100, y: 100 });
  567. // then
  568. expect(queryPopup(popupMenu, '.djs-popup-header')).not.to.exist;
  569. expect(queryPopup(popupMenu, '.djs-popup-body')).not.to.exist;
  570. }));
  571. it('should trigger action on click', inject(function(popupMenu) {
  572. // given
  573. var actionListener = sinon.spy();
  574. var testProvider = {
  575. getHeaderEntries: function() {
  576. return [{
  577. id: '1',
  578. action: actionListener,
  579. label: 'foo'
  580. }];
  581. },
  582. getEntries: function() { return []; }
  583. };
  584. popupMenu.registerProvider('test-menu', testProvider);
  585. popupMenu.open({}, 'test-menu', { x: 100, y: 100 });
  586. var entry = queryPopup(popupMenu, '.entry');
  587. // when
  588. popupMenu.trigger(globalEvent(entry, { x: 0, y: 0 }));
  589. // then
  590. expect(actionListener).to.have.been.called;
  591. }));
  592. it('should add disabled and active classes', inject(function(popupMenu) {
  593. // given
  594. var entry;
  595. var testMenuProvider = {
  596. getHeaderEntries: function() {
  597. return [
  598. {
  599. id: 'foo',
  600. active: true,
  601. disabled: true,
  602. label: 'foo'
  603. }
  604. ];
  605. },
  606. getEntries: function() { return []; }
  607. };
  608. // when
  609. popupMenu.registerProvider('test-menu', testMenuProvider);
  610. popupMenu.open({}, 'test-menu', { x: 100, y: 100 });
  611. // then
  612. entry = queryEntry(popupMenu, 'foo');
  613. expect(domClasses(entry).has('active')).to.be.true;
  614. expect(domClasses(entry).has('disabled')).to.be.true;
  615. }));
  616. });
  617. // different browsers, different outcomes
  618. describe('position', function() {
  619. beforeEach(inject(function(popupMenu, elementRegistry) {
  620. var customProvider = {
  621. getEntries: function() {
  622. return [
  623. { id: '1', label: 'Entry 1' },
  624. { id: '2', label: 'Entry 2', active: true },
  625. { id: '3', label: 'Entry 3' },
  626. { id: '4', label: 'Entry 4', disabled: true },
  627. { id: '5', label: 'Entry 5' }
  628. ];
  629. },
  630. getHeaderEntries: function() {
  631. return [
  632. { id: 'A', label: 'A' },
  633. { id: 'B', label: 'B' },
  634. { id: 'C', label: 'C', active: true },
  635. { id: 'D', label: 'D', disabled: true },
  636. { id: 'E', label: 'E', disabled: true }
  637. ];
  638. }
  639. };
  640. popupMenu.registerProvider('custom-provider', customProvider);
  641. }));
  642. it('should open within bounds above', inject(function(popupMenu, canvas) {
  643. // given
  644. var clientRect = canvas._container.getBoundingClientRect();
  645. var cursorPosition = { x: clientRect.left + 100, y: clientRect.top + 500 };
  646. // when
  647. popupMenu.open({}, 'custom-provider', { x: 100, y: 500, cursor: cursorPosition });
  648. var menu = popupMenu._current.container;
  649. var menuDimensions = {
  650. width: menu.scrollWidth,
  651. height: menu.scrollHeight
  652. };
  653. // then
  654. expect(menu.offsetTop).to.equal(500 - menuDimensions.height);
  655. }));
  656. it('should open within bounds above (limited client rect height)', inject(
  657. function(popupMenu, canvas) {
  658. // given
  659. // limited client rect height
  660. canvas._container.parentElement.style.height = '200px';
  661. var clientRect = canvas._container.getBoundingClientRect();
  662. var cursorPosition = { x: clientRect.left + 10, y: clientRect.top + 150 };
  663. // when
  664. popupMenu.open({}, 'custom-provider', { x: 100, y: 150, cursor: cursorPosition });
  665. var menu = popupMenu._current.container;
  666. // then
  667. expect(menu.offsetTop).to.equal(10);
  668. }
  669. ));
  670. it('should open within bounds to the left', inject(function(popupMenu, canvas) {
  671. // given
  672. var clientRect = canvas._container.getBoundingClientRect();
  673. var cursorPosition = { x: clientRect.left + 2000, y: clientRect.top + 100 };
  674. // when
  675. popupMenu.open({}, 'custom-provider', { x: 2000, y: 100, cursor: cursorPosition });
  676. var menu = popupMenu._current.container;
  677. var menuDimensions = {
  678. width: menu.scrollWidth,
  679. height: menu.scrollHeight
  680. };
  681. expect(menu.offsetLeft).to.equal(2000 - menuDimensions.width);
  682. }));
  683. });
  684. describe('scaling', function() {
  685. var NUM_REGEX = /([+-]?\d*[.]?\d+)(?=,|\))/g;
  686. var zoomLevels = [ 1.0, 1.2, 3.5, 10, 0.5 ];
  687. function asVector(scaleStr) {
  688. if (scaleStr && scaleStr !== 'none') {
  689. var m = scaleStr.match(NUM_REGEX);
  690. var x = parseFloat(m[0], 10);
  691. var y = m[1] ? parseFloat(m[1], 10) : x;
  692. return {
  693. x: x,
  694. y: y
  695. };
  696. }
  697. }
  698. function scaleVector(element) {
  699. return asVector(element.style.transform);
  700. }
  701. function verifyScales(expectedScales) {
  702. getDiagramJS().invoke(function(canvas, popupMenu) {
  703. // given
  704. popupMenu.registerProvider('menu', menuProvider);
  705. // test multiple zoom steps
  706. zoomLevels.forEach(function(zoom, index) {
  707. var expectedScale = expectedScales[index];
  708. // when
  709. canvas.zoom(zoom);
  710. popupMenu.open({}, 'menu', { x: 100, y: 100 });
  711. var menu = popupMenu._current.container;
  712. var actualScale = scaleVector(menu) || { x: 1, y: 1 };
  713. // then
  714. expect(actualScale.x).to.eql(actualScale.y);
  715. expect(actualScale.x).to.be.closeTo(expectedScale, 0.00001);
  716. });
  717. });
  718. }
  719. it('should scale within [ 1.0, 1.5 ] by default', function() {
  720. // given
  721. var expectedScales = [ 1.0, 1.2, 1.5, 1.5, 1.0 ];
  722. bootstrapDiagram({
  723. modules: [
  724. popupMenuModule,
  725. modelingModule
  726. ]
  727. })();
  728. // when
  729. verifyScales(expectedScales);
  730. });
  731. it('should scale within [ 1.0, 1.5 ] without scale config', function() {
  732. // given
  733. var expectedScales = [ 1.0, 1.2, 1.5, 1.5, 1.0 ];
  734. bootstrapDiagram({
  735. modules: [
  736. popupMenuModule,
  737. modelingModule
  738. ],
  739. popupMenu: {}
  740. })();
  741. // when
  742. verifyScales(expectedScales);
  743. });
  744. it('should scale within the limits set in config', function() {
  745. // given
  746. var config = {
  747. scale: {
  748. min: 1.0,
  749. max: 1.2
  750. }
  751. };
  752. var expectedScales = [ 1.0, 1.2, 1.2, 1.2, 1.0 ];
  753. bootstrapDiagram({
  754. modules: [
  755. popupMenuModule,
  756. modelingModule
  757. ],
  758. popupMenu: config
  759. })();
  760. // when
  761. verifyScales(expectedScales);
  762. });
  763. it('should scale with scale = true', function() {
  764. // given
  765. var config = {
  766. scale: true
  767. };
  768. var expectedScales = zoomLevels;
  769. bootstrapDiagram({
  770. modules: [
  771. popupMenuModule,
  772. modelingModule
  773. ],
  774. popupMenu: config
  775. })();
  776. // when
  777. verifyScales(expectedScales);
  778. });
  779. it('should not scale with scale = false', function() {
  780. // given
  781. var config = {
  782. scale: false
  783. };
  784. var expectedScales = [ 1.0, 1.0, 1.0, 1.0, 1.0 ];
  785. bootstrapDiagram({
  786. modules: [
  787. popupMenuModule,
  788. modelingModule
  789. ],
  790. popupMenu: config
  791. })();
  792. // when
  793. verifyScales(expectedScales);
  794. });
  795. });
  796. });