pane-container-element-spec.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. const PaneContainer = require('../src/pane-container');
  2. const PaneAxis = require('../src/pane-axis');
  3. const params = {
  4. location: 'center',
  5. config: atom.config,
  6. confirm: atom.confirm.bind(atom),
  7. viewRegistry: atom.views,
  8. applicationDelegate: atom.applicationDelegate
  9. };
  10. describe('PaneContainerElement', function() {
  11. describe('when panes are added or removed', function() {
  12. it('inserts or removes resize elements', function() {
  13. const childTagNames = () =>
  14. Array.from(paneAxisElement.children).map(child =>
  15. child.nodeName.toLowerCase()
  16. );
  17. const paneAxis = new PaneAxis({}, atom.views);
  18. var paneAxisElement = paneAxis.getElement();
  19. expect(childTagNames()).toEqual([]);
  20. paneAxis.addChild(new PaneAxis({}, atom.views));
  21. expect(childTagNames()).toEqual(['atom-pane-axis']);
  22. paneAxis.addChild(new PaneAxis({}, atom.views));
  23. expect(childTagNames()).toEqual([
  24. 'atom-pane-axis',
  25. 'atom-pane-resize-handle',
  26. 'atom-pane-axis'
  27. ]);
  28. paneAxis.addChild(new PaneAxis({}, atom.views));
  29. expect(childTagNames()).toEqual([
  30. 'atom-pane-axis',
  31. 'atom-pane-resize-handle',
  32. 'atom-pane-axis',
  33. 'atom-pane-resize-handle',
  34. 'atom-pane-axis'
  35. ]);
  36. paneAxis.removeChild(paneAxis.getChildren()[2]);
  37. expect(childTagNames()).toEqual([
  38. 'atom-pane-axis',
  39. 'atom-pane-resize-handle',
  40. 'atom-pane-axis'
  41. ]);
  42. });
  43. it('transfers focus to the next pane if a focused pane is removed', function() {
  44. const container = new PaneContainer(params);
  45. const containerElement = container.getElement();
  46. const leftPane = container.getActivePane();
  47. const leftPaneElement = leftPane.getElement();
  48. const rightPane = leftPane.splitRight();
  49. const rightPaneElement = rightPane.getElement();
  50. jasmine.attachToDOM(containerElement);
  51. rightPaneElement.focus();
  52. expect(document.activeElement).toBe(rightPaneElement);
  53. rightPane.destroy();
  54. expect(containerElement).toHaveClass('panes');
  55. expect(document.activeElement).toBe(leftPaneElement);
  56. });
  57. });
  58. describe('when a pane is split', () =>
  59. it('builds appropriately-oriented atom-pane-axis elements', function() {
  60. const container = new PaneContainer(params);
  61. const containerElement = container.getElement();
  62. const pane1 = container.getActivePane();
  63. const pane2 = pane1.splitRight();
  64. const pane3 = pane2.splitDown();
  65. const horizontalPanes = containerElement.querySelectorAll(
  66. 'atom-pane-container > atom-pane-axis.horizontal > atom-pane'
  67. );
  68. expect(horizontalPanes.length).toBe(1);
  69. expect(horizontalPanes[0]).toBe(pane1.getElement());
  70. let verticalPanes = containerElement.querySelectorAll(
  71. 'atom-pane-container > atom-pane-axis.horizontal > atom-pane-axis.vertical > atom-pane'
  72. );
  73. expect(verticalPanes.length).toBe(2);
  74. expect(verticalPanes[0]).toBe(pane2.getElement());
  75. expect(verticalPanes[1]).toBe(pane3.getElement());
  76. pane1.destroy();
  77. verticalPanes = containerElement.querySelectorAll(
  78. 'atom-pane-container > atom-pane-axis.vertical > atom-pane'
  79. );
  80. expect(verticalPanes.length).toBe(2);
  81. expect(verticalPanes[0]).toBe(pane2.getElement());
  82. expect(verticalPanes[1]).toBe(pane3.getElement());
  83. }));
  84. describe('when the resize element is dragged ', function() {
  85. let [container, containerElement] = [];
  86. beforeEach(function() {
  87. container = new PaneContainer(params);
  88. containerElement = container.getElement();
  89. document.querySelector('#jasmine-content').appendChild(containerElement);
  90. });
  91. const dragElementToPosition = function(element, clientX) {
  92. element.dispatchEvent(
  93. new MouseEvent('mousedown', {
  94. view: window,
  95. bubbles: true,
  96. button: 0
  97. })
  98. );
  99. element.dispatchEvent(
  100. new MouseEvent('mousemove', {
  101. view: window,
  102. bubbles: true,
  103. clientX
  104. })
  105. );
  106. element.dispatchEvent(
  107. new MouseEvent('mouseup', {
  108. iew: window,
  109. bubbles: true,
  110. button: 0
  111. })
  112. );
  113. };
  114. const getElementWidth = element => element.getBoundingClientRect().width;
  115. const expectPaneScale = (...pairs) =>
  116. (() => {
  117. const result = [];
  118. for (let [pane, expectedFlexScale] of pairs) {
  119. result.push(
  120. expect(pane.getFlexScale()).toBeCloseTo(expectedFlexScale, 0.1)
  121. );
  122. }
  123. return result;
  124. })();
  125. const getResizeElement = i =>
  126. containerElement.querySelectorAll('atom-pane-resize-handle')[i];
  127. const getPaneElement = i =>
  128. containerElement.querySelectorAll('atom-pane')[i];
  129. it('adds and removes panes in the direction that the pane is being dragged', function() {
  130. const leftPane = container.getActivePane();
  131. expectPaneScale([leftPane, 1]);
  132. const middlePane = leftPane.splitRight();
  133. expectPaneScale([leftPane, 1], [middlePane, 1]);
  134. dragElementToPosition(
  135. getResizeElement(0),
  136. getElementWidth(getPaneElement(0)) / 2
  137. );
  138. expectPaneScale([leftPane, 0.5], [middlePane, 1.5]);
  139. const rightPane = middlePane.splitRight();
  140. expectPaneScale([leftPane, 0.5], [middlePane, 1.5], [rightPane, 1]);
  141. dragElementToPosition(
  142. getResizeElement(1),
  143. getElementWidth(getPaneElement(0)) +
  144. getElementWidth(getPaneElement(1)) / 2
  145. );
  146. expectPaneScale([leftPane, 0.5], [middlePane, 0.75], [rightPane, 1.75]);
  147. waitsForPromise(() => middlePane.close());
  148. runs(() => expectPaneScale([leftPane, 0.44], [rightPane, 1.55]));
  149. waitsForPromise(() => leftPane.close());
  150. runs(() => expectPaneScale([rightPane, 1]));
  151. });
  152. it('splits or closes panes in orthogonal direction that the pane is being dragged', function() {
  153. const leftPane = container.getActivePane();
  154. expectPaneScale([leftPane, 1]);
  155. const rightPane = leftPane.splitRight();
  156. expectPaneScale([leftPane, 1], [rightPane, 1]);
  157. dragElementToPosition(
  158. getResizeElement(0),
  159. getElementWidth(getPaneElement(0)) / 2
  160. );
  161. expectPaneScale([leftPane, 0.5], [rightPane, 1.5]);
  162. // dynamically split pane, pane's flexScale will become to 1
  163. const lowerPane = leftPane.splitDown();
  164. expectPaneScale(
  165. [lowerPane, 1],
  166. [leftPane, 1],
  167. [leftPane.getParent(), 0.5]
  168. );
  169. // dynamically close pane, the pane's flexscale will recover to origin value
  170. waitsForPromise(() => lowerPane.close());
  171. runs(() => expectPaneScale([leftPane, 0.5], [rightPane, 1.5]));
  172. });
  173. it('unsubscribes from mouse events when the pane is detached', function() {
  174. container.getActivePane().splitRight();
  175. const element = getResizeElement(0);
  176. spyOn(document, 'addEventListener').andCallThrough();
  177. spyOn(document, 'removeEventListener').andCallThrough();
  178. spyOn(element, 'resizeStopped').andCallThrough();
  179. element.dispatchEvent(
  180. new MouseEvent('mousedown', {
  181. view: window,
  182. bubbles: true,
  183. button: 0
  184. })
  185. );
  186. waitsFor(() => document.addEventListener.callCount === 2);
  187. runs(function() {
  188. expect(element.resizeStopped.callCount).toBe(0);
  189. container.destroy();
  190. expect(element.resizeStopped.callCount).toBe(1);
  191. expect(document.removeEventListener.callCount).toBe(2);
  192. });
  193. });
  194. it('does not throw an error when resized to fit content in a detached state', function() {
  195. container.getActivePane().splitRight();
  196. const element = getResizeElement(0);
  197. element.remove();
  198. expect(() => element.resizeToFitContent()).not.toThrow();
  199. });
  200. });
  201. describe('pane resizing', function() {
  202. let [leftPane, rightPane] = [];
  203. beforeEach(function() {
  204. const container = new PaneContainer(params);
  205. leftPane = container.getActivePane();
  206. rightPane = leftPane.splitRight();
  207. });
  208. describe('when pane:increase-size is triggered', () =>
  209. it('increases the size of the pane', function() {
  210. expect(leftPane.getFlexScale()).toBe(1);
  211. expect(rightPane.getFlexScale()).toBe(1);
  212. atom.commands.dispatch(leftPane.getElement(), 'pane:increase-size');
  213. expect(leftPane.getFlexScale()).toBe(1.1);
  214. expect(rightPane.getFlexScale()).toBe(1);
  215. atom.commands.dispatch(rightPane.getElement(), 'pane:increase-size');
  216. expect(leftPane.getFlexScale()).toBe(1.1);
  217. expect(rightPane.getFlexScale()).toBe(1.1);
  218. }));
  219. describe('when pane:decrease-size is triggered', () =>
  220. it('decreases the size of the pane', function() {
  221. expect(leftPane.getFlexScale()).toBe(1);
  222. expect(rightPane.getFlexScale()).toBe(1);
  223. atom.commands.dispatch(leftPane.getElement(), 'pane:decrease-size');
  224. expect(leftPane.getFlexScale()).toBe(1 / 1.1);
  225. expect(rightPane.getFlexScale()).toBe(1);
  226. atom.commands.dispatch(rightPane.getElement(), 'pane:decrease-size');
  227. expect(leftPane.getFlexScale()).toBe(1 / 1.1);
  228. expect(rightPane.getFlexScale()).toBe(1 / 1.1);
  229. }));
  230. });
  231. describe('when only a single pane is present', function() {
  232. let [singlePane] = [];
  233. beforeEach(function() {
  234. const container = new PaneContainer(params);
  235. singlePane = container.getActivePane();
  236. });
  237. describe('when pane:increase-size is triggered', () =>
  238. it('does not increases the size of the pane', function() {
  239. expect(singlePane.getFlexScale()).toBe(1);
  240. atom.commands.dispatch(singlePane.getElement(), 'pane:increase-size');
  241. expect(singlePane.getFlexScale()).toBe(1);
  242. atom.commands.dispatch(singlePane.getElement(), 'pane:increase-size');
  243. expect(singlePane.getFlexScale()).toBe(1);
  244. }));
  245. describe('when pane:decrease-size is triggered', () =>
  246. it('does not decreases the size of the pane', function() {
  247. expect(singlePane.getFlexScale()).toBe(1);
  248. atom.commands.dispatch(singlePane.getElement(), 'pane:decrease-size');
  249. expect(singlePane.getFlexScale()).toBe(1);
  250. atom.commands.dispatch(singlePane.getElement(), 'pane:decrease-size');
  251. expect(singlePane.getFlexScale()).toBe(1);
  252. }));
  253. });
  254. });