CommonPopoverMenu.spec.ts 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { h } from 'vue'
  3. import { renderComponent } from '#tests/support/components/index.ts'
  4. import CommonPopover from '#shared/components/CommonPopover/CommonPopover.vue'
  5. import { usePopover } from '#shared/components/CommonPopover/usePopover.ts'
  6. import CommonPopoverMenu from '../CommonPopoverMenu.vue'
  7. import type { MenuItem } from '../types.ts'
  8. const html = String.raw
  9. const fn = vi.fn()
  10. describe('rendering section', () => {
  11. it('no output without default slot and items', () => {
  12. const view = renderComponent(CommonPopoverMenu, {
  13. props: {
  14. popover: null,
  15. headerLabel: 'Test Header',
  16. },
  17. router: true,
  18. })
  19. expect(view.queryByText('Test Header')).not.toBeInTheDocument()
  20. })
  21. it('if have header prop, renders header', () => {
  22. const view = renderComponent(CommonPopoverMenu, {
  23. props: {
  24. popover: null,
  25. headerLabel: 'Test Header',
  26. items: [
  27. {
  28. label: 'Example',
  29. },
  30. ],
  31. },
  32. router: true,
  33. store: true,
  34. })
  35. expect(view.getByText('Test Header')).toBeInTheDocument()
  36. })
  37. it('if have header slot, renders header', () => {
  38. const view = renderComponent(CommonPopoverMenu, {
  39. props: {
  40. popover: null,
  41. },
  42. slots: {
  43. header: '<div>Test Header</div>',
  44. default: 'Example',
  45. },
  46. router: true,
  47. })
  48. expect(view.getByText('Test Header')).toBeInTheDocument()
  49. })
  50. it('rendering items', () => {
  51. const items: MenuItem[] = [
  52. { key: 'login', link: '/login', label: 'Login' },
  53. { key: 'dashboard', link: '/', label: 'Link' },
  54. ]
  55. const view = renderComponent(CommonPopoverMenu, {
  56. shallow: false,
  57. props: {
  58. popover: null,
  59. items,
  60. },
  61. router: true,
  62. })
  63. expect(view.getByText('Login')).toBeInTheDocument()
  64. expect(view.getByText('Link')).toBeInTheDocument()
  65. })
  66. it('rendering only items with permission', () => {
  67. const items: MenuItem[] = [
  68. { key: 'login', link: '/login', label: 'Login' },
  69. { key: 'dashboard', link: '/', label: 'Link', permission: ['example'] },
  70. ]
  71. const view = renderComponent(CommonPopoverMenu, {
  72. shallow: false,
  73. props: {
  74. popover: null,
  75. items,
  76. },
  77. router: true,
  78. })
  79. expect(view.getByText('Login')).toBeInTheDocument()
  80. expect(view.queryByText('Link')).not.toBeInTheDocument()
  81. })
  82. it('support click handler on item', async () => {
  83. const clickHandler = vi.fn()
  84. const items: MenuItem[] = [
  85. { key: 'example', onClick: clickHandler, label: 'Example' },
  86. ]
  87. const view = renderComponent(CommonPopoverMenu, {
  88. shallow: false,
  89. props: {
  90. popover: null,
  91. items,
  92. },
  93. router: true,
  94. })
  95. await view.events.click(view.getByText('Example'))
  96. expect(clickHandler).toHaveBeenCalledOnce()
  97. })
  98. it('close popover on click on item or avoid closing', async () => {
  99. const clickHandlerExample = vi.fn()
  100. const clickHandlerOther = vi.fn()
  101. const view = renderComponent({
  102. components: { CommonPopover, CommonPopoverMenu },
  103. template: html`
  104. <CommonPopover ref="popover" :owner="popoverTarget"
  105. ><CommonPopoverMenu :popover="popover" :items="items"
  106. /></CommonPopover>
  107. <button ref="popoverTarget" @click="toggle">Click me</button>
  108. `,
  109. setup() {
  110. const { popover, popoverTarget, toggle } = usePopover()
  111. const items: MenuItem[] = [
  112. { key: 'example', onClick: clickHandlerExample, label: 'Example' },
  113. {
  114. key: 'other',
  115. onClick: clickHandlerOther,
  116. label: 'Other',
  117. noCloseOnClick: true,
  118. },
  119. ]
  120. return {
  121. items,
  122. toggle,
  123. popover,
  124. popoverTarget,
  125. }
  126. },
  127. })
  128. await view.events.click(view.getByText('Click me'))
  129. expect(view.queryByText('Example')).toBeInTheDocument()
  130. await view.events.click(view.getByText('Example'))
  131. expect(clickHandlerExample).toHaveBeenCalledOnce()
  132. expect(view.queryByText('Example')).not.toBeInTheDocument()
  133. await view.events.click(view.getByText('Click me'))
  134. await view.events.click(view.getByText('Other'))
  135. expect(clickHandlerOther).toHaveBeenCalledOnce()
  136. expect(view.queryByText('Other')).toBeInTheDocument()
  137. })
  138. it('can use an own component for item rendering', async () => {
  139. const CustomComponent = (props: any) => {
  140. return h('div', `Example ${props.label}`)
  141. }
  142. CustomComponent.props = ['label']
  143. const items: MenuItem[] = [
  144. { key: 'menu-item', component: CustomComponent, label: 'Menu item' },
  145. ]
  146. const view = renderComponent(CommonPopoverMenu, {
  147. shallow: false,
  148. props: {
  149. popover: null,
  150. items,
  151. },
  152. router: true,
  153. })
  154. expect(view.getByText('Example Menu item')).toBeInTheDocument()
  155. })
  156. it('yields entity data on show if prop is passed', async () => {
  157. renderComponent(CommonPopoverMenu, {
  158. props: {
  159. popover: null,
  160. headerLabel: 'Test Header',
  161. entity: {
  162. id: 'example',
  163. name: 'vitest',
  164. },
  165. items: [
  166. {
  167. label: 'Example',
  168. show: (event: { id: string; name: string }) => {
  169. fn(event)
  170. return true
  171. },
  172. },
  173. ],
  174. },
  175. router: true,
  176. store: true,
  177. })
  178. expect(fn).toBeCalledWith({ id: 'example', name: 'vitest' })
  179. })
  180. it('supports display of groups', () => {
  181. const items = [
  182. { key: 'group-test', label: 'group test', groupLabel: 'test group' },
  183. {
  184. key: 'group-test-2',
  185. label: 'group test 2',
  186. groupLabel: 'test group',
  187. },
  188. {
  189. key: 'group-test-2',
  190. label: 'group test 3',
  191. groupLabel: 'test group',
  192. },
  193. {
  194. key: 'single-group-test-2',
  195. label: 'single-group test 3',
  196. groupLabel: 'single group',
  197. },
  198. { key: 'single-test', label: 'single test' },
  199. ]
  200. const view = renderComponent(CommonPopoverMenu, {
  201. shallow: false,
  202. props: {
  203. popover: null,
  204. items,
  205. },
  206. router: true,
  207. })
  208. expect(view.getByRole('button', { name: 'group test' })).toBeInTheDocument()
  209. expect(
  210. view.getByRole('button', { name: 'group test 2' }),
  211. ).toBeInTheDocument()
  212. expect(
  213. view.getByRole('button', { name: 'group test 3' }),
  214. ).toBeInTheDocument()
  215. expect(
  216. view.getByRole('button', { name: 'single-group test 3' }),
  217. ).toBeInTheDocument()
  218. expect(
  219. view.getByRole('button', { name: 'single test' }),
  220. ).toBeInTheDocument()
  221. expect(
  222. view.getByRole('heading', { name: 'test group', level: 3 }),
  223. ).toBeInTheDocument()
  224. expect(
  225. view.getByRole('heading', { name: 'single group', level: 3 }),
  226. ).toBeInTheDocument()
  227. })
  228. })