2020-09-09 21:18:08 +08:00
|
|
|
import { nextTick } from 'vue'
|
2020-09-02 10:26:32 +08:00
|
|
|
import { mount } from '@vue/test-utils'
|
|
|
|
import * as Aria from '@element-plus/utils/aria'
|
|
|
|
|
2021-09-17 15:27:31 +08:00
|
|
|
import TrapFocus, { FOCUSABLE_CHILDREN } from '../trap-focus'
|
|
|
|
import type { ITrapFocusElement } from '../trap-focus'
|
|
|
|
|
2020-09-02 10:26:32 +08:00
|
|
|
const isVisibleMock = jest
|
|
|
|
.spyOn(Aria, 'isVisible')
|
|
|
|
.mockImplementation(() => true)
|
|
|
|
|
|
|
|
let wrapper
|
|
|
|
const _mount = (template: string) =>
|
|
|
|
mount(
|
|
|
|
{
|
|
|
|
template,
|
|
|
|
props: {},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
global: {
|
|
|
|
directives: { TrapFocus },
|
|
|
|
},
|
|
|
|
attachTo: document.body,
|
2021-09-04 19:29:28 +08:00
|
|
|
}
|
2020-09-02 10:26:32 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
afterAll(() => {
|
|
|
|
isVisibleMock.mockRestore()
|
|
|
|
})
|
|
|
|
|
|
|
|
afterEach(() => {
|
|
|
|
wrapper.unmount()
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('v-trap-focus', () => {
|
|
|
|
test('should fetch all focusable element', () => {
|
|
|
|
wrapper = _mount(`
|
|
|
|
<div v-trap-focus>
|
|
|
|
<button />
|
|
|
|
</div>
|
|
|
|
`)
|
|
|
|
expect(
|
2021-09-04 19:29:28 +08:00
|
|
|
(wrapper.element as ITrapFocusElement)[FOCUSABLE_CHILDREN].length
|
2020-09-02 10:26:32 +08:00
|
|
|
).toBe(1)
|
|
|
|
})
|
|
|
|
|
|
|
|
test('should not fetch disabled element', () => {
|
|
|
|
wrapper = _mount(`
|
|
|
|
<div v-trap-focus>
|
|
|
|
<button />
|
|
|
|
<button disabled />
|
|
|
|
<a href="test" />
|
|
|
|
<a />
|
|
|
|
<input />
|
|
|
|
<input disabled />
|
|
|
|
<input type="hidden" />
|
|
|
|
<input type="file" />
|
|
|
|
<div tabindex="-1" />
|
|
|
|
<select />
|
|
|
|
<select disabled />
|
|
|
|
<textarea />
|
|
|
|
<textarea disabled />
|
|
|
|
</div>
|
|
|
|
`)
|
|
|
|
expect(
|
2021-09-04 19:29:28 +08:00
|
|
|
(wrapper.element as ITrapFocusElement)[FOCUSABLE_CHILDREN].length
|
2020-09-02 10:26:32 +08:00
|
|
|
).toBe(5)
|
|
|
|
})
|
|
|
|
|
|
|
|
test('should trap keyboard.tab event', async () => {
|
|
|
|
wrapper = _mount(`
|
|
|
|
<div v-trap-focus>
|
|
|
|
<button class="button-1" />
|
|
|
|
<button class="button-2" />
|
|
|
|
<button class="button-3" />
|
|
|
|
</div>
|
|
|
|
`)
|
|
|
|
|
|
|
|
expect(document.activeElement).toBe(document.body)
|
|
|
|
await wrapper.find('.button-1').trigger('keydown', {
|
|
|
|
code: 'Tab',
|
|
|
|
shiftKey: true,
|
|
|
|
})
|
|
|
|
|
|
|
|
expect(document.activeElement).toBe(wrapper.find('.button-3').element)
|
|
|
|
await wrapper.find('.button-3').trigger('keydown', {
|
|
|
|
code: 'Tab',
|
|
|
|
})
|
|
|
|
|
|
|
|
expect(document.activeElement).toBe(wrapper.find('.button-1').element)
|
|
|
|
// the current active element is .button-1
|
|
|
|
await wrapper.find('.button-1').trigger('keydown', {
|
|
|
|
code: 'Tab',
|
|
|
|
})
|
|
|
|
|
|
|
|
expect(document.activeElement).toBe(wrapper.find('.button-2').element)
|
|
|
|
|
|
|
|
// now the active element became .button-2, this time we navigate back
|
|
|
|
await wrapper.find('.button-2').trigger('keydown', {
|
|
|
|
code: 'Tab',
|
|
|
|
shiftKey: true,
|
|
|
|
})
|
|
|
|
expect(document.activeElement).toBe(wrapper.find('.button-1').element)
|
|
|
|
})
|
|
|
|
|
2020-09-09 21:18:08 +08:00
|
|
|
test('should focus on the only focusable element', async () => {
|
2020-09-02 10:26:32 +08:00
|
|
|
wrapper = _mount(`
|
|
|
|
<div v-trap-focus>
|
|
|
|
<button />
|
|
|
|
</div>
|
|
|
|
`)
|
|
|
|
expect(document.activeElement).toBe(document.body)
|
|
|
|
await wrapper.find('button').trigger('keydown', {
|
|
|
|
code: 'Tab',
|
|
|
|
})
|
2020-09-09 21:18:08 +08:00
|
|
|
expect(document.activeElement).toBe(wrapper.find('button').element)
|
2020-09-02 10:26:32 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
test('should update focusable list when children changes', async () => {
|
|
|
|
wrapper = mount(
|
|
|
|
{
|
|
|
|
props: {
|
|
|
|
show: {
|
|
|
|
type: Boolean,
|
|
|
|
default: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
template: `
|
|
|
|
<div v-trap-focus>
|
|
|
|
<button />
|
|
|
|
<button v-if="show" />
|
|
|
|
</div>
|
|
|
|
`,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
global: {
|
|
|
|
directives: {
|
|
|
|
TrapFocus,
|
|
|
|
},
|
|
|
|
},
|
2021-09-04 19:29:28 +08:00
|
|
|
}
|
2020-09-02 10:26:32 +08:00
|
|
|
)
|
|
|
|
const initialElements = wrapper.element[FOCUSABLE_CHILDREN]
|
|
|
|
expect(initialElements.length).toBe(1)
|
|
|
|
|
|
|
|
await wrapper.setProps({
|
|
|
|
show: true,
|
|
|
|
})
|
|
|
|
|
2020-09-09 21:18:08 +08:00
|
|
|
await nextTick()
|
|
|
|
|
2020-09-02 10:26:32 +08:00
|
|
|
expect(wrapper.element[FOCUSABLE_CHILDREN].length).toBe(2)
|
|
|
|
})
|
|
|
|
})
|