|
@@ -1,20 +1,29 @@
|
|
|
// Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
|
|
|
|
|
|
import { flushPromises } from '@vue/test-utils'
|
|
|
-import { afterAll, beforeAll } from 'vitest'
|
|
|
+import { afterAll, beforeAll, expect } from 'vitest'
|
|
|
|
|
|
import { renderComponent } from '#tests/support/components/index.ts'
|
|
|
+import { waitForNextTick } from '#tests/support/utils.ts'
|
|
|
|
|
|
import CommonDialog from '../CommonDialog.vue'
|
|
|
-import { getDialogMeta, openDialog } from '../useDialog.ts'
|
|
|
+import { getDialogMeta, useDialog } from '../useDialog.ts'
|
|
|
|
|
|
const html = String.raw
|
|
|
|
|
|
describe('visuals for common dialog', () => {
|
|
|
+ let mainElement: HTMLElement
|
|
|
+ let app: HTMLDivElement
|
|
|
+
|
|
|
beforeAll(() => {
|
|
|
- const app = document.createElement('div')
|
|
|
+ app = document.createElement('div')
|
|
|
app.id = 'app'
|
|
|
document.body.appendChild(app)
|
|
|
+
|
|
|
+ mainElement = document.createElement('main')
|
|
|
+ mainElement.id = 'main-content'
|
|
|
+
|
|
|
+ app.insertAdjacentElement('beforeend', mainElement)
|
|
|
})
|
|
|
|
|
|
beforeEach(() => {
|
|
@@ -30,7 +39,7 @@ describe('visuals for common dialog', () => {
|
|
|
})
|
|
|
|
|
|
it('rendering with header title and content', () => {
|
|
|
- const view = renderComponent(CommonDialog, {
|
|
|
+ const wrapper = renderComponent(CommonDialog, {
|
|
|
props: {
|
|
|
name: 'dialog',
|
|
|
headerTitle: 'Some Title',
|
|
@@ -38,28 +47,30 @@ describe('visuals for common dialog', () => {
|
|
|
slots: {
|
|
|
default: 'Content Slot',
|
|
|
},
|
|
|
+ router: true,
|
|
|
})
|
|
|
|
|
|
- expect(view.getByText('Some Title')).toBeInTheDocument()
|
|
|
- expect(view.getByText('Content Slot')).toBeInTheDocument()
|
|
|
- expect(view.getByLabelText('Close dialog')).toBeInTheDocument()
|
|
|
+ expect(wrapper.getByText('Some Title')).toBeInTheDocument()
|
|
|
+ expect(wrapper.getByText('Content Slot')).toBeInTheDocument()
|
|
|
+ expect(wrapper.getByLabelText('Close dialog')).toBeInTheDocument()
|
|
|
})
|
|
|
|
|
|
it('can render header title as slot', () => {
|
|
|
- const view = renderComponent(CommonDialog, {
|
|
|
+ const wrapper = renderComponent(CommonDialog, {
|
|
|
props: {
|
|
|
name: 'dialog',
|
|
|
},
|
|
|
slots: {
|
|
|
header: 'Some Title',
|
|
|
},
|
|
|
+ router: true,
|
|
|
})
|
|
|
|
|
|
- expect(view.getByText('Some Title')).toBeInTheDocument()
|
|
|
+ expect(wrapper.getByText('Some Title')).toBeInTheDocument()
|
|
|
})
|
|
|
|
|
|
it('can close dialog with keyboard and clicks', async () => {
|
|
|
- const view = renderComponent(CommonDialog, {
|
|
|
+ const wrapper = renderComponent(CommonDialog, {
|
|
|
props: {
|
|
|
name: 'dialog',
|
|
|
},
|
|
@@ -68,27 +79,28 @@ describe('visuals for common dialog', () => {
|
|
|
teleport: true,
|
|
|
},
|
|
|
},
|
|
|
+ router: true,
|
|
|
})
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
- await view.events.keyboard('{Escape}')
|
|
|
+ await wrapper.events.keyboard('{Escape}')
|
|
|
|
|
|
- const emitted = view.emitted()
|
|
|
+ const emitted = wrapper.emitted()
|
|
|
|
|
|
expect(emitted.close).toHaveLength(1)
|
|
|
expect(emitted.close[0]).toEqual([undefined])
|
|
|
|
|
|
- await view.events.click(view.getByLabelText('Close dialog'))
|
|
|
+ await wrapper.events.click(wrapper.getByLabelText('Close dialog'))
|
|
|
expect(emitted.close).toHaveLength(2)
|
|
|
|
|
|
- await view.events.click(view.getByRole('button', { name: 'OK' }))
|
|
|
+ await wrapper.events.click(wrapper.getByRole('button', { name: 'OK' }))
|
|
|
expect(emitted.close).toHaveLength(3)
|
|
|
expect(emitted.close[2]).toEqual([false])
|
|
|
})
|
|
|
|
|
|
it('rendering different footer button content', () => {
|
|
|
- const view = renderComponent(CommonDialog, {
|
|
|
+ const wrapper = renderComponent(CommonDialog, {
|
|
|
props: {
|
|
|
name: 'dialog',
|
|
|
headerTitle: 'Some Title',
|
|
@@ -99,22 +111,24 @@ describe('visuals for common dialog', () => {
|
|
|
slots: {
|
|
|
default: 'Content Slot',
|
|
|
},
|
|
|
+ router: true,
|
|
|
})
|
|
|
|
|
|
expect(
|
|
|
- view.getByRole('button', { name: 'Yes, continue' }),
|
|
|
+ wrapper.getByRole('button', { name: 'Yes, continue' }),
|
|
|
).toBeInTheDocument()
|
|
|
})
|
|
|
|
|
|
it('has an accessible name', async () => {
|
|
|
- const view = renderComponent(CommonDialog, {
|
|
|
+ const wrapper = renderComponent(CommonDialog, {
|
|
|
props: {
|
|
|
headerTitle: 'foobar',
|
|
|
name: 'dialog',
|
|
|
},
|
|
|
+ router: true,
|
|
|
})
|
|
|
|
|
|
- expect(view.getByRole('dialog')).toHaveAccessibleName('foobar')
|
|
|
+ expect(wrapper.getByRole('dialog')).toHaveAccessibleName('foobar')
|
|
|
})
|
|
|
|
|
|
it('traps focus inside the dialog', async () => {
|
|
@@ -125,7 +139,7 @@ describe('visuals for common dialog', () => {
|
|
|
`
|
|
|
document.body.appendChild(externalForm)
|
|
|
|
|
|
- const view = renderComponent(CommonDialog, {
|
|
|
+ const wrapper = renderComponent(CommonDialog, {
|
|
|
props: {
|
|
|
name: 'dialog',
|
|
|
},
|
|
@@ -138,32 +152,35 @@ describe('visuals for common dialog', () => {
|
|
|
</select>
|
|
|
`,
|
|
|
},
|
|
|
+ router: true,
|
|
|
})
|
|
|
|
|
|
- view.getByTestId('input').focus()
|
|
|
- expect(view.getByTestId('input')).toHaveFocus()
|
|
|
+ wrapper.getByTestId('input').focus()
|
|
|
+ expect(wrapper.getByTestId('input')).toHaveFocus()
|
|
|
|
|
|
- await view.events.keyboard('{Tab}')
|
|
|
- expect(view.getByTestId('div')).toHaveFocus()
|
|
|
+ await wrapper.events.keyboard('{Tab}')
|
|
|
+ expect(wrapper.getByTestId('div')).toHaveFocus()
|
|
|
|
|
|
- await view.events.keyboard('{Tab}')
|
|
|
- expect(view.getByTestId('select')).toHaveFocus()
|
|
|
+ await wrapper.events.keyboard('{Tab}')
|
|
|
+ expect(wrapper.getByTestId('select')).toHaveFocus()
|
|
|
|
|
|
- await view.events.keyboard('{Tab}')
|
|
|
- expect(view.getByRole('button', { name: 'Cancel & Go Back' })).toHaveFocus()
|
|
|
+ await wrapper.events.keyboard('{Tab}')
|
|
|
+ expect(
|
|
|
+ wrapper.getByRole('button', { name: 'Cancel & Go Back' }),
|
|
|
+ ).toHaveFocus()
|
|
|
|
|
|
- await view.events.keyboard('{Tab}')
|
|
|
- expect(view.getByRole('button', { name: 'OK' })).toHaveFocus()
|
|
|
+ await wrapper.events.keyboard('{Tab}')
|
|
|
+ expect(wrapper.getByRole('button', { name: 'OK' })).toHaveFocus()
|
|
|
|
|
|
- await view.events.keyboard('{Tab}')
|
|
|
- expect(view.getByLabelText('Close dialog')).toHaveFocus()
|
|
|
+ await wrapper.events.keyboard('{Tab}')
|
|
|
+ expect(wrapper.getByLabelText('Close dialog')).toHaveFocus()
|
|
|
|
|
|
- await view.events.keyboard('{Tab}')
|
|
|
- expect(view.getByTestId('input')).toHaveFocus()
|
|
|
+ await wrapper.events.keyboard('{Tab}')
|
|
|
+ expect(wrapper.getByTestId('input')).toHaveFocus()
|
|
|
})
|
|
|
|
|
|
it('autofocuses the first focusable element', async () => {
|
|
|
- const view = renderComponent(CommonDialog, {
|
|
|
+ const wrapper = renderComponent(CommonDialog, {
|
|
|
props: {
|
|
|
name: 'dialog',
|
|
|
},
|
|
@@ -175,15 +192,16 @@ describe('visuals for common dialog', () => {
|
|
|
</select>
|
|
|
`,
|
|
|
},
|
|
|
+ router: true,
|
|
|
})
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
- expect(view.getByTestId('div')).toHaveFocus()
|
|
|
+ expect(wrapper.getByTestId('div')).toHaveFocus()
|
|
|
})
|
|
|
|
|
|
it('focuses close, if there is nothing focusable in dialog', async () => {
|
|
|
- const view = renderComponent(CommonDialog, {
|
|
|
+ const wrapper = renderComponent(CommonDialog, {
|
|
|
props: {
|
|
|
name: 'dialog',
|
|
|
hideFooter: true,
|
|
@@ -192,35 +210,60 @@ describe('visuals for common dialog', () => {
|
|
|
|
|
|
await flushPromises()
|
|
|
|
|
|
- expect(view.getByLabelText('Close dialog')).toHaveFocus()
|
|
|
+ expect(wrapper.getByLabelText('Close dialog')).toHaveFocus()
|
|
|
})
|
|
|
|
|
|
it('refocuses element that opened dialog', async () => {
|
|
|
- const button = document.createElement('button')
|
|
|
- button.setAttribute('aria-haspopup', 'dialog')
|
|
|
- button.setAttribute('aria-controls', 'dialog-dialog')
|
|
|
- button.setAttribute('data-test-id', 'button')
|
|
|
- document.body.appendChild(button)
|
|
|
+ const wrapper = renderComponent(
|
|
|
+ {
|
|
|
+ template: `
|
|
|
+ <button aria-haspopup="dialog" aria-controls="dialog" data-test-id="button"/>`,
|
|
|
+ setup() {
|
|
|
+ const dialog = useDialog({
|
|
|
+ name: 'dialog',
|
|
|
+ component: () =>
|
|
|
+ import('#desktop/components/CommonDialog/CommonDialog.vue'),
|
|
|
+ })
|
|
|
+ dialog.open({ name: 'dialog', hideFooter: true })
|
|
|
+ },
|
|
|
+ },
|
|
|
+ {
|
|
|
+ dialog: true,
|
|
|
+ router: true,
|
|
|
+ },
|
|
|
+ )
|
|
|
+
|
|
|
+ await wrapper.events.click(wrapper.getByTestId('button'))
|
|
|
+
|
|
|
+ expect(wrapper.getByTestId('button')).toHaveFocus()
|
|
|
|
|
|
- button.focus()
|
|
|
+ await wrapper.events.keyboard('{Escape}')
|
|
|
|
|
|
- expect(button).toHaveFocus()
|
|
|
+ await waitForNextTick()
|
|
|
|
|
|
- await openDialog('dialog', {})
|
|
|
+ expect(wrapper.getByTestId('button')).toHaveFocus()
|
|
|
+ })
|
|
|
|
|
|
- const view = renderComponent(CommonDialog, {
|
|
|
+ it('displays by default over the main content', () => {
|
|
|
+ const wrapper = renderComponent(CommonDialog, {
|
|
|
props: {
|
|
|
name: 'dialog',
|
|
|
- hideFooter: true,
|
|
|
+ fullscreen: false,
|
|
|
},
|
|
|
})
|
|
|
|
|
|
- await flushPromises()
|
|
|
-
|
|
|
- expect(view.getByLabelText('Close dialog')).toHaveFocus()
|
|
|
+ expect(mainElement.children).not.include(wrapper.baseElement)
|
|
|
+ expect(app.children).include(wrapper.baseElement)
|
|
|
+ })
|
|
|
|
|
|
- await view.events.keyboard('{Escape}')
|
|
|
+ it('supports displaying over entire viewport', () => {
|
|
|
+ const wrapper = renderComponent(CommonDialog, {
|
|
|
+ props: {
|
|
|
+ name: 'dialog',
|
|
|
+ fullscreen: true,
|
|
|
+ },
|
|
|
+ })
|
|
|
|
|
|
- expect(button).toHaveFocus()
|
|
|
+ expect(mainElement.children).include(wrapper.baseElement)
|
|
|
})
|
|
|
})
|