deprecatedDropdownMenu.spec.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. import type {ComponentProps} from 'react';
  2. import {
  3. render,
  4. screen,
  5. userEvent,
  6. waitForElementToBeRemoved,
  7. } from 'sentry-test/reactTestingLibrary';
  8. import DeprecatedDropdownMenu from 'sentry/components/deprecatedDropdownMenu';
  9. jest.useFakeTimers();
  10. describe('dropdownMenuDeprecated', function () {
  11. const DeprecatedDropdownImplementation = (
  12. props: Partial<ComponentProps<typeof DeprecatedDropdownMenu>> = {}
  13. ) => {
  14. return (
  15. <DeprecatedDropdownMenu {...props}>
  16. {({getRootProps, getActorProps, getMenuProps, isOpen}) => (
  17. <span {...getRootProps({})}>
  18. <button {...getActorProps({})}>Open Dropdown</button>
  19. {isOpen && (
  20. <ul {...getMenuProps({})}>
  21. <li>Dropdown Menu Item 1</li>
  22. </ul>
  23. )}
  24. </span>
  25. )}
  26. </DeprecatedDropdownMenu>
  27. );
  28. };
  29. it('renders', function () {
  30. const {container} = render(<DeprecatedDropdownImplementation />);
  31. expect(container).toSnapshot();
  32. });
  33. it('can toggle dropdown menu with actor', function () {
  34. render(<DeprecatedDropdownImplementation />);
  35. userEvent.click(screen.getByRole('button'));
  36. expect(screen.getByRole('listbox')).toBeInTheDocument();
  37. userEvent.click(screen.getByRole('button'));
  38. expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
  39. });
  40. it('closes dropdown when clicking on anything in menu', function () {
  41. render(<DeprecatedDropdownImplementation />);
  42. userEvent.click(screen.getByRole('button'));
  43. userEvent.click(screen.getByRole('listitem'));
  44. expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
  45. });
  46. it('closes dropdown when clicking outside of menu', async function () {
  47. render(
  48. <div data-test-id="outside-element">
  49. <DeprecatedDropdownImplementation />
  50. </div>
  51. );
  52. userEvent.click(screen.getByRole('button'));
  53. userEvent.click(screen.getByTestId('outside-element'));
  54. await waitForElementToBeRemoved(() => screen.queryByRole('listbox'));
  55. });
  56. it('closes dropdown when pressing escape', function () {
  57. render(<DeprecatedDropdownImplementation />);
  58. userEvent.click(screen.getByRole('button'));
  59. userEvent.keyboard('{Escape}');
  60. expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
  61. });
  62. it('ignores "Escape" key if `closeOnEscape` is false', function () {
  63. render(<DeprecatedDropdownImplementation closeOnEscape={false} />);
  64. userEvent.click(screen.getByRole('button'));
  65. userEvent.keyboard('{Escape}');
  66. expect(screen.getByRole('listbox')).toBeInTheDocument();
  67. });
  68. it('keeps dropdown open when clicking on anything in menu with `keepMenuOpen` prop', function () {
  69. render(<DeprecatedDropdownImplementation keepMenuOpen />);
  70. userEvent.click(screen.getByRole('button'));
  71. userEvent.click(screen.getByRole('listitem'));
  72. expect(screen.getByRole('listbox')).toBeInTheDocument();
  73. });
  74. it('render prop getters all extend props and call original onClick handlers', function () {
  75. const rootClick = jest.fn();
  76. const actorClick = jest.fn();
  77. const menuClick = jest.fn();
  78. render(
  79. <DeprecatedDropdownMenu keepMenuOpen>
  80. {({getRootProps, getActorProps, getMenuProps, isOpen}) => (
  81. <span {...getRootProps({onClick: rootClick})} data-test-id="root">
  82. <button {...getActorProps({onClick: actorClick})} data-test-id="actor">
  83. Open Dropdown
  84. </button>
  85. {isOpen && (
  86. <ul {...getMenuProps({onClick: menuClick})} data-test-id="menu">
  87. <li>Dropdown Menu Item 1</li>
  88. </ul>
  89. )}
  90. </span>
  91. )}
  92. </DeprecatedDropdownMenu>
  93. );
  94. expect(screen.queryByRole('listbox')).not.toBeInTheDocument();
  95. userEvent.click(screen.getByTestId('root'));
  96. expect(rootClick).toHaveBeenCalled();
  97. userEvent.click(screen.getByTestId('actor'));
  98. expect(actorClick).toHaveBeenCalled();
  99. userEvent.click(screen.getByTestId('menu'));
  100. expect(menuClick).toHaveBeenCalled();
  101. expect(screen.queryByRole('listbox')).toBeInTheDocument();
  102. });
  103. it('always rendered menus should attach document event listeners only when opened', function () {
  104. const addSpy = jest.spyOn(document, 'addEventListener');
  105. const removeSpy = jest.spyOn(document, 'removeEventListener');
  106. render(
  107. <DeprecatedDropdownMenu alwaysRenderMenu>
  108. {({getRootProps, getActorProps, getMenuProps}) => (
  109. <span {...getRootProps({className: 'root'})}>
  110. <button {...getActorProps({className: 'actor'})}>Open Dropdown</button>
  111. <ul {...getMenuProps({className: 'menu'})}>
  112. <li>Dropdown Menu Item 1</li>
  113. </ul>
  114. </span>
  115. )}
  116. </DeprecatedDropdownMenu>
  117. );
  118. // Make sure this is only called when menu is open
  119. expect(addSpy).not.toHaveBeenCalled();
  120. userEvent.click(screen.getByRole('button'));
  121. expect(addSpy).toHaveBeenCalled();
  122. expect(removeSpy).not.toHaveBeenCalled();
  123. userEvent.click(screen.getByRole('button'));
  124. expect(removeSpy).toHaveBeenCalled();
  125. addSpy.mockRestore();
  126. removeSpy.mockRestore();
  127. });
  128. it('does not close nested dropdown on actor clicks', function () {
  129. render(
  130. <DeprecatedDropdownMenu isNestedDropdown>
  131. {({getRootProps, getActorProps, getMenuProps}) => (
  132. <span {...getRootProps({})}>
  133. <button {...getActorProps({})}>Open Dropdown</button>
  134. {
  135. <ul {...getMenuProps({})}>
  136. <li data-test-id="menu-item">Dropdown Menu Item 1</li>
  137. </ul>
  138. }
  139. </span>
  140. )}
  141. </DeprecatedDropdownMenu>
  142. );
  143. userEvent.hover(screen.getByRole('button'));
  144. expect(screen.getByTestId('menu-item')).toBeInTheDocument();
  145. userEvent.click(screen.getByRole('button'));
  146. // Should still be visible.
  147. expect(screen.getByTestId('menu-item')).toBeInTheDocument();
  148. });
  149. });