index.spec.tsx 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. import {RouterContextFixture} from 'sentry-fixture/routerContextFixture';
  2. import {render, screen, userEvent, within} from 'sentry-test/reactTestingLibrary';
  3. import {TabList, TabPanels, Tabs} from 'sentry/components/tabs';
  4. const TABS = [
  5. {key: 'details', label: 'Details', content: 'So by colonel hearted ferrars.'},
  6. {
  7. key: 'activity',
  8. label: 'Activity',
  9. content:
  10. 'Draw from upon here gone add one. He in sportsman household otherwise it perceived instantly.',
  11. },
  12. {
  13. key: 'user-feedback',
  14. label: 'User Feedback',
  15. content: 'Is inquiry no he several excited am.',
  16. },
  17. {
  18. key: 'attachments',
  19. label: 'Attachments',
  20. content: 'Called though excuse length ye needed it he having.',
  21. },
  22. ];
  23. describe('Tabs', () => {
  24. it('renders tabs list', () => {
  25. render(
  26. <Tabs>
  27. <TabList>
  28. {TABS.map(tab => (
  29. <TabList.Item key={tab.key}>{tab.label}</TabList.Item>
  30. ))}
  31. </TabList>
  32. <TabPanels>
  33. {TABS.map(tab => (
  34. <TabPanels.Item key={tab.key}>{tab.content}</TabPanels.Item>
  35. ))}
  36. </TabPanels>
  37. </Tabs>
  38. );
  39. // The full tabs list is rendered
  40. expect(screen.getByRole('tablist')).toHaveAttribute('aria-orientation', 'horizontal');
  41. expect(screen.getAllByRole('tab')).toHaveLength(TABS.length);
  42. TABS.forEach(tab => {
  43. expect(screen.getByRole('tab', {name: tab.label})).toBeInTheDocument();
  44. });
  45. // The first tab item is selected and its content visible
  46. expect(screen.getByRole('tab', {name: TABS[0].label})).toHaveAttribute(
  47. 'aria-selected',
  48. 'true'
  49. );
  50. expect(screen.getByText(TABS[0].content)).toBeInTheDocument();
  51. });
  52. it('renders tabs list when disabled', () => {
  53. render(
  54. <Tabs disabled>
  55. <TabList>
  56. {TABS.map(tab => (
  57. <TabList.Item key={tab.key}>{tab.label}</TabList.Item>
  58. ))}
  59. </TabList>
  60. <TabPanels>
  61. {TABS.map(tab => (
  62. <TabPanels.Item key={tab.key}>{tab.content}</TabPanels.Item>
  63. ))}
  64. </TabPanels>
  65. </Tabs>
  66. );
  67. // The first tab item is selected and its content visible
  68. expect(screen.getByRole('tab', {name: TABS[0].label})).toHaveAttribute(
  69. 'aria-selected',
  70. 'true'
  71. );
  72. expect(screen.getByText(TABS[0].content)).toBeInTheDocument();
  73. // All tabs are marked as disabled
  74. TABS.forEach(tab => {
  75. expect(screen.getByRole('tab', {name: tab.label})).toHaveAttribute(
  76. 'aria-disabled',
  77. 'true'
  78. );
  79. });
  80. });
  81. it('changes tabs on click', async () => {
  82. render(
  83. <Tabs>
  84. <TabList>
  85. {TABS.map(tab => (
  86. <TabList.Item key={tab.key}>{tab.label}</TabList.Item>
  87. ))}
  88. </TabList>
  89. <TabPanels>
  90. {TABS.map(tab => (
  91. <TabPanels.Item key={tab.key}>{tab.content}</TabPanels.Item>
  92. ))}
  93. </TabPanels>
  94. </Tabs>
  95. );
  96. // Click on the Activity tab
  97. await userEvent.click(screen.getByRole('tab', {name: 'Activity'}));
  98. // The Activity tab is selected and its content rendered
  99. expect(screen.getByRole('tab', {name: 'Activity'})).toHaveAttribute(
  100. 'aria-selected',
  101. 'true'
  102. );
  103. expect(screen.getByText(TABS[1].content)).toBeInTheDocument();
  104. });
  105. it('changes tabs using keyboard navigation', async () => {
  106. render(
  107. <Tabs>
  108. <TabList>
  109. {TABS.map(tab => (
  110. <TabList.Item key={tab.key}>{tab.label}</TabList.Item>
  111. ))}
  112. </TabList>
  113. <TabPanels>
  114. {TABS.map(tab => (
  115. <TabPanels.Item key={tab.key}>{tab.content}</TabPanels.Item>
  116. ))}
  117. </TabPanels>
  118. </Tabs>
  119. );
  120. // Focus on tab list
  121. await userEvent.tab();
  122. expect(screen.getByRole('tab', {name: 'Details'})).toHaveFocus();
  123. // Press Arrow Right to select the next tab to the right (Activity)
  124. await userEvent.keyboard('{arrowRight}{enter}');
  125. // The Activity tab is selected and its contents rendered
  126. expect(screen.getByRole('tab', {name: 'Activity'})).toHaveAttribute(
  127. 'aria-selected',
  128. 'true'
  129. );
  130. expect(screen.getByText(TABS[1].content)).toBeInTheDocument();
  131. });
  132. it('changes tabs on key press in vertical orientation', async () => {
  133. render(
  134. <Tabs orientation="vertical">
  135. <TabList>
  136. {TABS.map(tab => (
  137. <TabList.Item key={tab.key}>{tab.label}</TabList.Item>
  138. ))}
  139. </TabList>
  140. <TabPanels>
  141. {TABS.map(tab => (
  142. <TabPanels.Item key={tab.key}>{tab.content}</TabPanels.Item>
  143. ))}
  144. </TabPanels>
  145. </Tabs>
  146. );
  147. // Focus on tab list
  148. await userEvent.tab();
  149. expect(screen.getByRole('tab', {name: 'Details'})).toHaveFocus();
  150. // Press Arrow Right to select the next tab below (Activity)
  151. await userEvent.keyboard('{arrowDown}{enter}');
  152. // The Activity tab should now be selected and its contents rendered
  153. expect(screen.getByRole('tab', {name: 'Activity'})).toHaveAttribute(
  154. 'aria-selected',
  155. 'true'
  156. );
  157. expect(screen.getByText(TABS[1].content)).toBeInTheDocument();
  158. });
  159. it('renders disabled tabs', () => {
  160. render(
  161. <Tabs>
  162. <TabList>
  163. {TABS.map(tab => (
  164. <TabList.Item key={tab.key} disabled>
  165. {tab.label}
  166. </TabList.Item>
  167. ))}
  168. </TabList>
  169. <TabPanels>
  170. {TABS.map(tab => (
  171. <TabPanels.Item key={tab.key}>{tab.content}</TabPanels.Item>
  172. ))}
  173. </TabPanels>
  174. </Tabs>
  175. );
  176. TABS.forEach(tab => {
  177. expect(screen.getByRole('tab', {name: tab.label})).toHaveAttribute(
  178. 'aria-disabled',
  179. 'true'
  180. );
  181. });
  182. });
  183. it('renders tab links', async () => {
  184. const routerContext = RouterContextFixture();
  185. render(
  186. <Tabs>
  187. <TabList>
  188. {TABS.map(tab => (
  189. <TabList.Item key={tab.key} to="#some-link">
  190. {tab.label}
  191. </TabList.Item>
  192. ))}
  193. </TabList>
  194. <TabPanels>
  195. {TABS.map(tab => (
  196. <TabPanels.Item key={tab.key}>{tab.content}</TabPanels.Item>
  197. ))}
  198. </TabPanels>
  199. </Tabs>,
  200. {context: routerContext}
  201. );
  202. TABS.forEach(tab => {
  203. const tabEl = screen.getByRole('tab', {name: tab.label});
  204. expect(within(tabEl).getByRole('link', {hidden: true})).toHaveAttribute(
  205. 'href',
  206. '#some-link'
  207. );
  208. });
  209. // Command/ctrl/shift-clicking on a tab link doesn't change the tab selection.
  210. // The expected behavior is that clicking on a tab link will open a new browser
  211. // tab/window. The current view shouldn't update.
  212. const secondTabEl = screen.getByRole('tab', {name: TABS[1].label});
  213. const secondTabLink = within(secondTabEl).getByRole('link', {hidden: true});
  214. const user = userEvent.setup();
  215. await user.keyboard('[MetaLeft>]');
  216. await user.click(secondTabLink);
  217. await user.keyboard('[/MetaLeft]');
  218. await user.keyboard('[ControlLeft>]');
  219. await user.click(secondTabLink);
  220. await user.keyboard('[/ControlLeft]');
  221. await user.keyboard('[ShiftLeft>]');
  222. await user.click(secondTabLink);
  223. await user.keyboard('[/ShiftLeft]');
  224. expect(screen.getByRole('tab', {name: TABS[0].label})).toHaveAttribute(
  225. 'aria-selected',
  226. 'true'
  227. );
  228. });
  229. });