floatingpanelview.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. /**
  2. * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  3. * For licensing, see LICENSE.md.
  4. */
  5. /* global document, Event */
  6. import global from '@ckeditor/ckeditor5-utils/src/dom/global';
  7. import FloatingPanelView from '../../../src/panel/floating/floatingpanelview';
  8. import View from '../../../src/view';
  9. import ViewCollection from '../../../src/viewcollection';
  10. import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
  11. import * as positionUtils from '@ckeditor/ckeditor5-utils/src/dom/position';
  12. testUtils.createSinonSandbox();
  13. describe( 'FloatingPanelView', () => {
  14. let locale, view, target;
  15. beforeEach( () => {
  16. locale = {};
  17. view = new FloatingPanelView( locale );
  18. target = global.document.createElement( 'a' );
  19. global.document.body.appendChild( view.element );
  20. global.document.body.appendChild( target );
  21. view.targetElement = target;
  22. return view.init();
  23. } );
  24. afterEach( () => {
  25. view.element.remove();
  26. return view.destroy();
  27. } );
  28. describe( 'constructor()', () => {
  29. it( 'should inherit from View', () => {
  30. expect( view ).to.be.instanceOf( View );
  31. } );
  32. it( 'should set view#locale', () => {
  33. expect( view.locale ).to.equal( locale );
  34. } );
  35. it( 'should set #isActive', () => {
  36. expect( view.isActive ).to.be.false;
  37. } );
  38. it( 'should set #top', () => {
  39. expect( view.top ).to.equal( 0 );
  40. } );
  41. it( 'should set #left', () => {
  42. expect( view.left ).to.equal( 0 );
  43. } );
  44. it( 'should set #targetElement', () => {
  45. view = new FloatingPanelView( locale );
  46. expect( view.targetElement ).to.be.null;
  47. } );
  48. it( 'creates view#content collection', () => {
  49. expect( view.content ).to.be.instanceOf( ViewCollection );
  50. } );
  51. } );
  52. describe( 'template', () => {
  53. it( 'should create element from template', () => {
  54. expect( view.element.classList.contains( 'ck-floating-panel' ) ).to.be.true;
  55. } );
  56. describe( 'bindings', () => {
  57. describe( 'class', () => {
  58. it( 'reacts on #isActive', () => {
  59. view.isActive = false;
  60. expect( view.element.classList.contains( 'ck-floating-panel_active' ) ).to.be.false;
  61. view.isActive = true;
  62. expect( view.element.classList.contains( 'ck-floating-panel_active' ) ).to.be.true;
  63. } );
  64. } );
  65. describe( 'style', () => {
  66. it( 'reacts on #top', () => {
  67. view.top = 30;
  68. expect( view.element.style.top ).to.equal( '30px' );
  69. } );
  70. it( 'reacts on #left', () => {
  71. view.left = 20;
  72. expect( view.element.style.left ).to.equal( '20px' );
  73. } );
  74. } );
  75. describe( 'children', () => {
  76. it( 'should react on view#content', () => {
  77. expect( view.element.childNodes.length ).to.equal( 0 );
  78. const item = new View();
  79. item.element = document.createElement( 'div' );
  80. view.content.add( item );
  81. expect( view.element.childNodes.length ).to.equal( 1 );
  82. } );
  83. } );
  84. } );
  85. } );
  86. describe( 'init()', () => {
  87. it( 'calls #_updatePosition on window.scroll', () => {
  88. const spy = sinon.spy( view, '_updatePosition' );
  89. global.window.dispatchEvent( new Event( 'scroll', { bubbles: true } ) );
  90. sinon.assert.calledOnce( spy );
  91. } );
  92. it( 'calls #_updatePosition on #change:isActive', () => {
  93. view.isActive = false;
  94. const spy = sinon.spy( view, '_updatePosition' );
  95. view.isActive = true;
  96. sinon.assert.calledOnce( spy );
  97. view.isActive = false;
  98. sinon.assert.calledTwice( spy );
  99. } );
  100. } );
  101. describe( '_updatePosition()', () => {
  102. it( 'does not update when not #isActive', () => {
  103. const spy = testUtils.sinon.spy( positionUtils, 'getOptimalPosition' );
  104. view.isActive = false;
  105. view._updatePosition();
  106. sinon.assert.notCalled( spy );
  107. view.isActive = true;
  108. view._updatePosition();
  109. // Note: #_updatePosition() is called on #change:isActive.
  110. sinon.assert.calledTwice( spy );
  111. } );
  112. it( 'uses getOptimalPosition() utility', () => {
  113. const { nw, sw, ne, se } = FloatingPanelView.defaultPositions;
  114. view.isActive = true;
  115. const spy = testUtils.sinon.stub( positionUtils, 'getOptimalPosition' ).returns( {
  116. top: 5,
  117. left: 10
  118. } );
  119. view._updatePosition();
  120. sinon.assert.calledWithExactly( spy, {
  121. element: view.element,
  122. target: target,
  123. positions: [ nw, sw, ne, se ],
  124. limiter: global.document.body,
  125. fitInViewport: true
  126. } );
  127. expect( view.top ).to.equal( 5 );
  128. expect( view.left ).to.equal( 10 );
  129. } );
  130. } );
  131. describe( 'defaultPositions', () => {
  132. let targetRect, panelRect, defaultPositions;
  133. beforeEach( () => {
  134. defaultPositions = FloatingPanelView.defaultPositions;
  135. targetRect = {
  136. top: 10,
  137. width: 100,
  138. left: 10,
  139. height: 10,
  140. bottom: 20
  141. };
  142. panelRect = {
  143. width: 20,
  144. height: 10
  145. };
  146. } );
  147. it( 'should provide "nw" position', () => {
  148. expect( defaultPositions.nw( targetRect, panelRect ) ).to.deep.equal( {
  149. top: 0,
  150. left: 10,
  151. name: 'nw'
  152. } );
  153. } );
  154. it( 'should provide "sw" position', () => {
  155. expect( defaultPositions.sw( targetRect, panelRect ) ).to.deep.equal( {
  156. top: 20,
  157. left: 10,
  158. name: 'sw'
  159. } );
  160. } );
  161. it( 'should provide "ne" position', () => {
  162. expect( defaultPositions.ne( targetRect, panelRect ) ).to.deep.equal( {
  163. top: 0,
  164. left: 90,
  165. name: 'ne'
  166. } );
  167. } );
  168. it( 'should provide "se" position', () => {
  169. expect( defaultPositions.se( targetRect, panelRect ) ).to.deep.equal( {
  170. top: 20,
  171. left: 90,
  172. name: 'se'
  173. } );
  174. } );
  175. } );
  176. } );