LayoutSidebar.spec.ts 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { fireEvent } from '@testing-library/vue'
  3. import { beforeEach, describe, expect } from 'vitest'
  4. import renderComponent, {
  5. type ExtendedRenderResult,
  6. } from '#tests/support/components/renderComponent.ts'
  7. import LayoutSidebar from '#desktop/components/layout/LayoutSidebar.vue'
  8. import { SidebarPosition } from '../types.ts'
  9. describe('LayoutSidebar', () => {
  10. describe('Feature: Collapsible', () => {
  11. let view: ExtendedRenderResult
  12. beforeEach(() => {
  13. view = renderComponent(LayoutSidebar, {
  14. props: {
  15. name: 'testBar-collapsible',
  16. id: 'sidebar-test',
  17. collapsible: true,
  18. resizable: false,
  19. },
  20. })
  21. })
  22. it('shows CollapseButton in expanded state when collapsible is true', async () => {
  23. expect(view.queryByLabelText('Expand sidebar')).not.toBeInTheDocument()
  24. // By default, is expanded and aria label shows collapse action
  25. expect(view.queryByLabelText('Collapse sidebar')).toBeInTheDocument()
  26. })
  27. it('does not show CollapseButton when collapsible is false', async () => {
  28. await view.rerender({ collapsible: false })
  29. expect(
  30. view.queryByLabelText('collapse this item'),
  31. ).not.toBeInTheDocument()
  32. expect(view.queryByLabelText('expand this item')).not.toBeInTheDocument()
  33. })
  34. it('hides action Button when no iconCollapsed is provided', async () => {
  35. expect(view.queryByTestId('action-button')).not.toBeInTheDocument()
  36. })
  37. it('shows an action Button when iconCollapsed is provided and sidebar is collapsed', async () => {
  38. await view.rerender({ iconCollapsed: 'person-gear' })
  39. const collapseButton = await view.findByLabelText('Collapse sidebar')
  40. await view.events.click(collapseButton)
  41. expect(view.queryByTestId('action-button')).toBeInTheDocument()
  42. })
  43. })
  44. describe('Feature: Resizable', () => {
  45. let view: ExtendedRenderResult
  46. beforeEach(() => {
  47. view = renderComponent(LayoutSidebar, {
  48. props: {
  49. name: 'testBar-resize',
  50. id: 'sidebar-test',
  51. collapsible: false,
  52. resizable: true,
  53. },
  54. })
  55. })
  56. it('shows ResizeLine when resizable is true', async () => {
  57. expect(view.queryByLabelText('Resize sidebar')).toBeInTheDocument()
  58. })
  59. it('does not show ResizeLine when resizable is false', async () => {
  60. await view.rerender({ resizable: false })
  61. expect(view.queryByLabelText('Resize sidebar')).not.toBeInTheDocument()
  62. })
  63. it('resizes sidebar when ResizeLine is clicked and dragged', async () => {
  64. const resizeLine = await view.findByLabelText('Resize sidebar')
  65. await view.events.click(resizeLine)
  66. // Emulate mouse down on resize handle and mouse move on document
  67. await fireEvent.mouseDown(resizeLine, { clientX: 0 })
  68. await fireEvent.mouseMove(document, { clientX: 100 })
  69. await fireEvent.mouseUp(document, { clientX: 100 })
  70. expect(view.emitted('resize-horizontal')).toEqual([[100]])
  71. })
  72. it('resizes sidebar when ResizeLine is touched and dragged', async () => {
  73. const resizeLine = await view.findByLabelText('Resize sidebar')
  74. await view.events.click(resizeLine)
  75. // Touch device
  76. await fireEvent.touchStart(resizeLine, { pageX: 0 })
  77. await fireEvent.touchMove(document, { pageX: 100 })
  78. await fireEvent.touchEnd(document, { pageX: 100 })
  79. // :TODO check why we can not use toEqual([[100]])
  80. expect(view.emitted('resize-horizontal')).toBeTruthy()
  81. })
  82. it('resets width when ResizeLine is double clicked', async () => {
  83. const resizeLine = await view.findByLabelText('Resize sidebar')
  84. await fireEvent.dblClick(resizeLine)
  85. expect(view.emitted('reset-width')).toBeTruthy()
  86. })
  87. })
  88. describe('Feature: Position', () => {
  89. let view: ExtendedRenderResult
  90. beforeEach(() => {
  91. view = renderComponent(LayoutSidebar, {
  92. props: {
  93. name: 'testBar-position',
  94. id: 'sidebar-test',
  95. collapsible: true,
  96. resizable: true,
  97. },
  98. })
  99. })
  100. it('defaults to start position (left)', async () => {
  101. const aside = view.getByRole('complementary')
  102. expect(aside).toHaveClass('border-e')
  103. const collapseButton = view.getByLabelText('Collapse sidebar')
  104. expect(collapseButton.parentElement).toHaveClasses([
  105. 'focus-within:opacity-100',
  106. 'hover:opacity-100',
  107. ])
  108. const resizeLine = view.getByLabelText('Resize sidebar')
  109. expect(resizeLine.parentElement).toHaveClasses([
  110. 'ltr:right-0',
  111. 'ltr:translate-x-1/2',
  112. 'rtl:left-0',
  113. 'rtl:-translate-x-1/2',
  114. ])
  115. })
  116. it('supports end position (right)', async () => {
  117. await view.rerender({ position: SidebarPosition.End })
  118. const aside = view.getByRole('complementary')
  119. expect(aside).toHaveClass('border-s')
  120. const resizeLine = view.getByLabelText('Resize sidebar')
  121. expect(resizeLine.parentElement).toHaveClasses([
  122. 'ltr:left-0',
  123. 'rtl:right-0',
  124. 'ltr:-translate-x-1/2',
  125. 'rtl:right-0',
  126. 'rtl:translate-x-1/2',
  127. ])
  128. })
  129. })
  130. })