dropdownLink.spec.jsx 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. import $ from 'jquery';
  2. import React from 'react';
  3. import {mount} from 'sentry-test/enzyme';
  4. import DropdownLink from 'app/components/dropdownLink';
  5. import {MENU_CLOSE_DELAY} from 'app/constants';
  6. jest.useFakeTimers();
  7. describe('DropdownLink', function() {
  8. const INPUT_1 = {
  9. title: 'test',
  10. onOpen: () => {},
  11. onClose: () => {},
  12. topLevelClasses: 'top-level-class',
  13. alwaysRenderMenu: true,
  14. menuClasses: '',
  15. };
  16. describe('renders', function() {
  17. it('and anchors to left by default', function() {
  18. const component = mount(
  19. <DropdownLink {...INPUT_1}>
  20. <div>1</div>
  21. <div>2</div>
  22. </DropdownLink>
  23. );
  24. expect(component).toMatchSnapshot();
  25. });
  26. it('and anchors to right', function() {
  27. const component = mount(
  28. <DropdownLink {...INPUT_1} anchorRight>
  29. <div>1</div>
  30. <div>2</div>
  31. </DropdownLink>
  32. );
  33. expect(component).toMatchSnapshot();
  34. });
  35. });
  36. describe('Uncontrolled', function() {
  37. let wrapper;
  38. beforeEach(function() {
  39. if (wrapper) {
  40. wrapper.unmount();
  41. }
  42. wrapper = mount(
  43. <DropdownLink alwaysRenderMenu={false} title="test">
  44. <li>hi</li>
  45. </DropdownLink>
  46. );
  47. });
  48. describe('While Closed', function() {
  49. it('displays dropdown menu when dropdown actor button clicked', function() {
  50. expect(wrapper.find('li')).toHaveLength(0);
  51. // open
  52. wrapper.find('a').simulate('click');
  53. expect(wrapper.find('li')).toHaveLength(1);
  54. });
  55. });
  56. describe('While Opened', function() {
  57. beforeEach(function() {
  58. // Opens dropdown menu
  59. wrapper.find('a').simulate('click');
  60. });
  61. it('closes when clicked outside', async function() {
  62. const evt = document.createEvent('HTMLEvents');
  63. evt.initEvent('click', false, true);
  64. document.body.dispatchEvent(evt);
  65. jest.runAllTimers();
  66. await Promise.resolve();
  67. wrapper.update();
  68. expect(wrapper.find('li')).toHaveLength(0);
  69. });
  70. it('closes when dropdown actor button is clicked', function() {
  71. wrapper.find('a').simulate('click');
  72. expect(wrapper.find('li')).toHaveLength(0);
  73. });
  74. it('closes when dropdown menu item is clicked', function() {
  75. wrapper.find('li').simulate('click');
  76. expect(wrapper.find('li')).toHaveLength(0);
  77. });
  78. it('does not close when menu is clicked and `keepMenuOpen` is on', function() {
  79. wrapper = mount(
  80. <DropdownLink title="test" alwaysRenderMenu={false} keepMenuOpen>
  81. <li>hi</li>
  82. </DropdownLink>
  83. );
  84. wrapper.find('a').simulate('click');
  85. wrapper.find('li').simulate('click');
  86. expect(wrapper.find('li')).toHaveLength(1);
  87. wrapper.unmount();
  88. });
  89. });
  90. });
  91. describe('Controlled', function() {
  92. let wrapper;
  93. beforeEach(function() {
  94. if (wrapper) {
  95. wrapper.unmount();
  96. }
  97. });
  98. describe('Opened', function() {
  99. beforeEach(function() {
  100. wrapper = mount(
  101. <DropdownLink isOpen alwaysRenderMenu={false} title="test">
  102. <li>hi</li>
  103. </DropdownLink>
  104. );
  105. });
  106. it('does not close when menu is clicked', function() {
  107. // open
  108. wrapper.find('li').simulate('click');
  109. // State does not change
  110. expect(wrapper.find('.dropdown-menu')).toHaveLength(1);
  111. });
  112. it('does not close when document is clicked', function() {
  113. $(document).click();
  114. // State does not change
  115. expect(wrapper.find('.dropdown-menu')).toHaveLength(1);
  116. });
  117. it('does not close when dropdown actor is clicked', function() {
  118. wrapper.find('a').simulate('click');
  119. // State does not change
  120. expect(wrapper.find('.dropdown-menu')).toHaveLength(1);
  121. });
  122. });
  123. describe('Closed', function() {
  124. beforeEach(function() {
  125. wrapper = mount(
  126. <DropdownLink isOpen={false} alwaysRenderMenu={false} title="test">
  127. <li>hi</li>
  128. </DropdownLink>
  129. );
  130. });
  131. it('does not open when dropdown actor is clicked', function() {
  132. wrapper.find('a').simulate('click');
  133. // State does not change
  134. expect(wrapper.find('.dropdown-menu')).toHaveLength(0);
  135. });
  136. });
  137. });
  138. describe('Nested Dropdown', function() {
  139. let wrapper;
  140. beforeEach(function() {
  141. if (wrapper) {
  142. wrapper.unmount();
  143. }
  144. wrapper = mount(
  145. <DropdownLink title="parent" alwaysRenderMenu={false}>
  146. <li id="nested-actor">
  147. <DropdownLink
  148. className="nested-menu"
  149. alwaysRenderMenu={false}
  150. title="nested"
  151. isNestedDropdown
  152. >
  153. <li id="nested-actor-2">
  154. <DropdownLink
  155. className="nested-menu-2"
  156. alwaysRenderMenu={false}
  157. title="nested #2"
  158. isNestedDropdown
  159. >
  160. <li id="nested-actor-3">Hello</li>
  161. </DropdownLink>
  162. </li>
  163. </DropdownLink>
  164. </li>
  165. <li id="no-nest">Item 2</li>
  166. </DropdownLink>
  167. );
  168. // Start when menu open
  169. wrapper.find('a').simulate('click');
  170. });
  171. it('closes when top-level actor is clicked', function() {
  172. wrapper
  173. .find('a')
  174. .first()
  175. .simulate('click');
  176. expect(wrapper.find('.dropdown-menu')).toHaveLength(0);
  177. });
  178. it('Opens / closes on mouse enter and leave', function() {
  179. // Nested menus have delay on open
  180. wrapper.find('.dropdown-menu a').simulate('mouseEnter');
  181. jest.runAllTimers();
  182. wrapper.update();
  183. expect(wrapper.find('.dropdown-menu')).toHaveLength(2);
  184. // Leaving Nested Menu
  185. wrapper.find('a.nested-menu').simulate('mouseLeave');
  186. // Nested menus have close delay
  187. expect(wrapper.find('.dropdown-menu')).toHaveLength(2);
  188. jest.advanceTimersByTime(MENU_CLOSE_DELAY - 1);
  189. wrapper.update();
  190. // Re-entering nested menu will cancel close
  191. expect(wrapper.find('.dropdown-menu')).toHaveLength(2);
  192. wrapper.find('a.nested-menu').simulate('mouseEnter');
  193. jest.advanceTimersByTime(2);
  194. wrapper.update();
  195. expect(wrapper.find('.dropdown-menu')).toHaveLength(2);
  196. // Re-entering an actor will also cancel close
  197. expect(wrapper.find('.dropdown-menu')).toHaveLength(2);
  198. jest.advanceTimersByTime(MENU_CLOSE_DELAY - 1);
  199. wrapper.update();
  200. wrapper
  201. .find('.dropdown-menu a')
  202. .first()
  203. .simulate('mouseEnter');
  204. jest.advanceTimersByTime(2);
  205. wrapper.update();
  206. expect(wrapper.find('.dropdown-menu')).toHaveLength(2);
  207. // Leave menu
  208. wrapper.find('a.nested-menu').simulate('mouseLeave');
  209. jest.runAllTimers();
  210. wrapper.update();
  211. expect(wrapper.find('.dropdown-menu')).toHaveLength(1);
  212. });
  213. it('closes when first level nested actor is clicked', function() {
  214. wrapper.find('#nested-actor').simulate('click');
  215. expect(wrapper.find('.dropdown-menu')).toHaveLength(0);
  216. });
  217. it('closes when second level nested actor is clicked', function() {
  218. wrapper.find('a.nested-menu').simulate('mouseEnter');
  219. jest.runAllTimers();
  220. wrapper.update();
  221. wrapper.find('a.nested-menu-2 span').simulate('click');
  222. expect(wrapper.find('.dropdown-menu')).toHaveLength(0);
  223. });
  224. it('closes when third level nested actor is clicked', function() {
  225. wrapper.find('a.nested-menu').simulate('mouseEnter');
  226. jest.runAllTimers();
  227. wrapper.update();
  228. wrapper.find('a.nested-menu-2').simulate('mouseEnter');
  229. jest.runAllTimers();
  230. wrapper.update();
  231. wrapper.find('#nested-actor-3').simulate('click');
  232. expect(wrapper.find('.dropdown-menu')).toHaveLength(0);
  233. });
  234. });
  235. });