CollapseButton.spec.ts 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { renderComponent } from '#tests/support/components/index.ts'
  3. import { EnumTextDirection } from '#shared/graphql/types.ts'
  4. import { useLocaleStore } from '#shared/stores/locale.ts'
  5. import CollapseButton from '#desktop/components/CollapseButton/CollapseButton.vue'
  6. describe('CollapseButton', () => {
  7. it.each([
  8. {
  9. collapsed: true,
  10. orientation: 'horizontal',
  11. inverse: false,
  12. icon: 'arrow-bar-right',
  13. },
  14. {
  15. collapsed: false,
  16. orientation: 'horizontal',
  17. inverse: false,
  18. icon: 'arrow-bar-left',
  19. },
  20. {
  21. collapsed: true,
  22. orientation: 'vertical',
  23. inverse: false,
  24. icon: 'arrows-expand',
  25. },
  26. {
  27. collapsed: false,
  28. orientation: 'vertical',
  29. inverse: false,
  30. icon: 'arrows-collapse',
  31. },
  32. {
  33. collapsed: true,
  34. orientation: 'horizontal',
  35. inverse: true,
  36. icon: 'arrow-bar-left',
  37. },
  38. {
  39. collapsed: false,
  40. orientation: 'horizontal',
  41. inverse: true,
  42. icon: 'arrow-bar-right',
  43. },
  44. {
  45. collapsed: true,
  46. orientation: 'vertical',
  47. inverse: true,
  48. icon: 'arrows-expand',
  49. },
  50. {
  51. collapsed: false,
  52. orientation: 'vertical',
  53. inverse: true,
  54. icon: 'arrows-collapse',
  55. },
  56. ])(
  57. 'displays correct LTR icon (collapsed: $collapsed, orientation: $orientation, inverse: $inverse)',
  58. async ({ collapsed, orientation, inverse, icon }) => {
  59. const wrapper = renderComponent(CollapseButton, {
  60. props: {
  61. ownerId: 'test',
  62. collapsed,
  63. inverse,
  64. orientation,
  65. },
  66. })
  67. expect(wrapper.getByIconName(icon)).toBeInTheDocument()
  68. },
  69. )
  70. it.each([
  71. {
  72. collapsed: true,
  73. orientation: 'horizontal',
  74. inverse: false,
  75. icon: 'arrow-bar-left',
  76. },
  77. {
  78. collapsed: false,
  79. orientation: 'horizontal',
  80. inverse: false,
  81. icon: 'arrow-bar-right',
  82. },
  83. {
  84. collapsed: true,
  85. orientation: 'vertical',
  86. inverse: false,
  87. icon: 'arrows-expand',
  88. },
  89. {
  90. collapsed: false,
  91. orientation: 'vertical',
  92. inverse: false,
  93. icon: 'arrows-collapse',
  94. },
  95. {
  96. collapsed: true,
  97. orientation: 'horizontal',
  98. inverse: true,
  99. icon: 'arrow-bar-right',
  100. },
  101. {
  102. collapsed: false,
  103. orientation: 'horizontal',
  104. inverse: true,
  105. icon: 'arrow-bar-left',
  106. },
  107. {
  108. collapsed: true,
  109. orientation: 'vertical',
  110. inverse: true,
  111. icon: 'arrows-expand',
  112. },
  113. {
  114. collapsed: false,
  115. orientation: 'vertical',
  116. inverse: true,
  117. icon: 'arrows-collapse',
  118. },
  119. ])(
  120. 'displays correct RTL icon (collapsed: $collapsed, orientation: $orientation, inverse: $inverse)',
  121. async ({ collapsed, orientation, inverse, icon }) => {
  122. const locale = useLocaleStore()
  123. locale.localeData = {
  124. dir: EnumTextDirection.Rtl,
  125. } as any
  126. const wrapper = renderComponent(CollapseButton, {
  127. props: {
  128. ownerId: 'test',
  129. collapsed,
  130. inverse,
  131. orientation,
  132. },
  133. })
  134. expect(wrapper.getByIconName(icon)).toBeInTheDocument()
  135. },
  136. )
  137. it('emits toggle-collapse event on click', async () => {
  138. const wrapper = renderComponent(CollapseButton, {
  139. props: {
  140. ownerId: 'test',
  141. collapsed: true,
  142. },
  143. })
  144. await wrapper.events.click(wrapper.getByRole('button'))
  145. expect(wrapper.emitted('toggle-collapse')).toBeTruthy()
  146. })
  147. it('renders the button by default', () => {
  148. const wrapper = renderComponent(CollapseButton, {
  149. props: {
  150. ownerId: 'test',
  151. },
  152. })
  153. expect(wrapper.getByRole('button')).toBeInTheDocument()
  154. })
  155. it('shows only on hover for non-touch devices', () => {
  156. const wrapper = renderComponent(CollapseButton, {
  157. props: {
  158. ownerId: 'test',
  159. },
  160. })
  161. expect(wrapper.getByRole('button').parentElement).toHaveClasses([
  162. 'opacity-0',
  163. ])
  164. })
  165. it.each(['tertiary-gray', 'none'])(
  166. 'renders variant %s correctly',
  167. (variant) => {
  168. const wrapper = renderComponent(CollapseButton, {
  169. props: {
  170. variant,
  171. ownerId: 'test',
  172. },
  173. })
  174. if (variant === 'tertiary-gray') {
  175. expect(wrapper.getByRole('button')).toHaveClasses([
  176. 'focus-visible:bg-blue-800',
  177. 'active:dark:bg-blue-800',
  178. 'focus:dark:bg-blue-800',
  179. 'active:bg-blue-800',
  180. 'focus:bg-blue-800',
  181. 'hover:bg-blue-600',
  182. 'hover:dark:bg-blue-900',
  183. 'text-black',
  184. 'dark:bg-gray-200',
  185. 'dark:text-white',
  186. ])
  187. }
  188. expect(wrapper.getByRole('button')).toHaveClasses([])
  189. },
  190. )
  191. it('shows always for touch devices', () => {
  192. // Impersonate a touch device by mocking the corresponding media query.
  193. Object.defineProperty(window, 'matchMedia', {
  194. value: vi.fn().mockImplementation(() => ({
  195. matches: true,
  196. addEventListener: vi.fn(),
  197. removeEventListener: vi.fn(),
  198. })),
  199. })
  200. const wrapper = renderComponent(CollapseButton, {
  201. props: {
  202. ownerId: 'test',
  203. },
  204. })
  205. expect(wrapper.getByRole('button')).not.toHaveClasses([
  206. 'transition-opacity',
  207. 'opacity-0',
  208. 'group-hover/test:opacity-100',
  209. ])
  210. })
  211. it('supports custom labels for expand and collapse', async () => {
  212. const wrapper = renderComponent(CollapseButton, {
  213. props: {
  214. ownerId: 'test',
  215. collapsed: true,
  216. expandLabel: 'expand foo',
  217. collapseLabel: 'collapse foo',
  218. },
  219. })
  220. expect(wrapper.getByLabelText('expand foo')).toBeInTheDocument()
  221. await wrapper.rerender({
  222. collapsed: false,
  223. })
  224. expect(wrapper.getByLabelText('collapse foo')).toBeInTheDocument()
  225. })
  226. })