CommonFlyout.spec.ts 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. // Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. import { beforeAll, describe, expect } from 'vitest'
  3. import { nextTick } from 'vue'
  4. import renderComponent from '#tests/support/components/renderComponent.ts'
  5. import CommonFlyout from '#desktop/components/CommonFlyout/CommonFlyout.vue'
  6. const html = String.raw
  7. describe('CommonFlyout', () => {
  8. describe('standalone component', () => {
  9. let flyout: ReturnType<typeof renderComponent>
  10. let mainElement: HTMLElement
  11. let app: HTMLDivElement
  12. beforeAll(() => {
  13. app = document.createElement('div')
  14. app.id = 'app'
  15. document.body.appendChild(app)
  16. mainElement = document.createElement('main')
  17. mainElement.id = 'main-content'
  18. app.insertAdjacentElement('beforeend', mainElement)
  19. })
  20. beforeEach(() => {
  21. flyout = renderComponent(CommonFlyout, {
  22. props: {
  23. name: 'test-identifier',
  24. headerTitle: 'Test Title',
  25. headerIcon: 'buildings',
  26. showBackdrop: false,
  27. },
  28. router: true,
  29. })
  30. })
  31. it('renders the correct title', async () => {
  32. expect(flyout.getByText('Test Title')).toBeInTheDocument()
  33. })
  34. it('renders the correct icon', async () => {
  35. expect(flyout.queryByIconName('buildings')).toBeInTheDocument()
  36. })
  37. it('renders a default submit label', async () => {
  38. expect(flyout.getByText('Update')).toBeInTheDocument()
  39. })
  40. it('renders a custom submit label', async () => {
  41. await flyout.rerender({
  42. footerActionOptions: {
  43. actionLabel: 'Submit',
  44. },
  45. })
  46. expect(flyout.getByText('Submit')).toBeInTheDocument()
  47. })
  48. it('renders a default cancel label', () => {
  49. expect(flyout.getByText('Cancel & Go Back')).toBeInTheDocument()
  50. })
  51. it('renders a custom cancel label', async () => {
  52. await flyout.rerender({
  53. footerActionOptions: {
  54. cancelLabel: 'Exit',
  55. },
  56. })
  57. expect(flyout.getByText('Exit')).toBeInTheDocument()
  58. })
  59. it('renders the resize handle as a default', () => {
  60. expect(flyout.queryByLabelText('Resize side panel')).toBeInTheDocument()
  61. })
  62. it('does not render the resize handle when allowResizing is false', async () => {
  63. await flyout.rerender({
  64. resizable: false,
  65. })
  66. expect(
  67. flyout.queryByLabelText('Resize side panel'),
  68. ).not.toBeInTheDocument()
  69. })
  70. it('renders slot content', async () => {
  71. const flyout = renderComponent(CommonFlyout, {
  72. props: {
  73. name: 'test-identifier',
  74. label: 'Test',
  75. headerTitle: 'Test Title',
  76. titleIcon: 'buildings',
  77. showBackdrop: false,
  78. },
  79. slots: {
  80. header: 'Foo header',
  81. default: 'Hello world!',
  82. footer: 'Foo submit',
  83. },
  84. })
  85. expect(flyout.getByText('Hello world!')).toBeInTheDocument()
  86. expect(flyout.getByText('Foo header')).toBeInTheDocument()
  87. expect(flyout.getByText('Foo submit')).toBeInTheDocument()
  88. })
  89. it('focuses the first focusable element when opened', async () => {
  90. const flyout = renderComponent(CommonFlyout, {
  91. props: {
  92. headerTitle: 'Test Title',
  93. name: 'test-identifier',
  94. showBackdrop: false,
  95. },
  96. slots: {
  97. default: html`<input
  98. type="text"
  99. placeholder="test"
  100. name="test-input"
  101. />`,
  102. },
  103. })
  104. await nextTick()
  105. expect(flyout.getByPlaceholderText('test')).toHaveFocus()
  106. })
  107. it('has a default container width of 500px', async () => {
  108. expect(flyout.getByRole('complementary')).toHaveStyle({
  109. width: '500px',
  110. })
  111. })
  112. it('displays by default over the main content', async () => {
  113. await flyout.rerender({ fullscreen: false })
  114. expect(mainElement.children).not.include(flyout.baseElement)
  115. expect(app.children).include(flyout.baseElement)
  116. })
  117. it('supports displaying over entire viewport', async () => {
  118. await flyout.rerender({ fullscreen: true })
  119. expect(mainElement.children).include(flyout.baseElement)
  120. })
  121. describe('events', () => {
  122. it('emits close event when cancel button is clicked', async () => {
  123. await flyout.events.click(flyout.getByText('Cancel & Go Back'))
  124. expect(flyout.emitted('close')).toHaveLength(1)
  125. })
  126. it('emits close event when x button is clicked', async () => {
  127. await flyout.events.click(
  128. flyout.getAllByLabelText('Close side panel').at(-1) as HTMLElement,
  129. )
  130. expect(flyout.emitted('close')).toHaveLength(1)
  131. })
  132. it('emits close event when escape key is pressed, by default', async () => {
  133. await flyout.events.keyboard('{Escape}')
  134. expect(flyout.emitted('close')).toHaveLength(1)
  135. })
  136. it('emits close event when escape key is pressed, if specified', async () => {
  137. await flyout.rerender({ noCloseOnEscape: true })
  138. await flyout.events.keyboard('{Escape}')
  139. expect(flyout.emitted('close')).toBeUndefined()
  140. })
  141. it('emits event when action button is clicked', async () => {
  142. await flyout.events.click(flyout.getByText('Update'))
  143. expect(flyout.emitted('action')).toHaveLength(1)
  144. })
  145. })
  146. })
  147. })