roving-focus-item.test.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // @ts-nocheck
  2. import { defineComponent, inject, nextTick, ref } from 'vue'
  3. import { mount } from '@vue/test-utils'
  4. import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
  5. import { composeRefs } from '@element-plus/utils'
  6. import { EVENT_CODE } from '@element-plus/constants'
  7. import {
  8. ROVING_FOCUS_COLLECTION_INJECTION_KEY,
  9. ROVING_FOCUS_ITEM_COLLECTION_INJECTION_KEY,
  10. } from '../src/roving-focus-group'
  11. import {
  12. ROVING_FOCUS_GROUP_INJECTION_KEY,
  13. ROVING_FOCUS_GROUP_ITEM_INJECTION_KEY,
  14. } from '../src/tokens'
  15. import ElRovingFocusItem from '../src/roving-focus-item.vue'
  16. const AXIOM = 'rem is the best girl'
  17. const focusItemKls = 'item-kls'
  18. let counter = 0
  19. const FocusItem = defineComponent({
  20. setup() {
  21. const { rovingFocusGroupItemRef, ...itemInjection } = inject(
  22. ROVING_FOCUS_GROUP_ITEM_INJECTION_KEY,
  23. undefined
  24. )
  25. const collectionItemInjection = inject(
  26. ROVING_FOCUS_ITEM_COLLECTION_INJECTION_KEY,
  27. undefined
  28. )!
  29. const itemRef = composeRefs(
  30. rovingFocusGroupItemRef,
  31. collectionItemInjection.collectionItemRef
  32. )
  33. return {
  34. itemRef,
  35. ...itemInjection,
  36. id: ++counter,
  37. }
  38. },
  39. template: `<div
  40. :ref="itemRef"
  41. :tabindex="tabIndex"
  42. class="${focusItemKls}"
  43. @keydown="handleKeydown"
  44. @focus="handleFocus"
  45. @mousedown="handleMousedown"
  46. >
  47. ${AXIOM} {{ id }}
  48. </div>`,
  49. })
  50. describe('<ElRovingFocusItem />', () => {
  51. const currentTabbedId = ref('test_id')
  52. const loop = ref(false)
  53. const onItemFocus = vi.fn()
  54. const onItemShiftTab = vi.fn()
  55. const itemMap = new Map()
  56. const getItems = () => [...itemMap.values()]
  57. const defaultProvides = {
  58. [ROVING_FOCUS_GROUP_INJECTION_KEY as symbol]: {
  59. currentTabbedId,
  60. loop,
  61. onItemFocus,
  62. onItemShiftTab,
  63. },
  64. [ROVING_FOCUS_COLLECTION_INJECTION_KEY as symbol]: {
  65. getItems,
  66. itemMap,
  67. },
  68. }
  69. const createComponent = (props = {}) =>
  70. mount(
  71. {
  72. template: `<div>
  73. <el-roving-focus-item v-bind="$attrs">
  74. <focus-item />
  75. </el-roving-focus-item>
  76. <el-roving-focus-item v-bind="$attrs">
  77. <focus-item />
  78. </el-roving-focus-item>
  79. <el-roving-focus-item v-bind="$attrs">
  80. <focus-item />
  81. </el-roving-focus-item>
  82. </div>`,
  83. components: {
  84. ElRovingFocusItem,
  85. FocusItem,
  86. },
  87. },
  88. {
  89. props,
  90. global: {
  91. provide: defaultProvides,
  92. },
  93. attachTo: document.body,
  94. }
  95. )
  96. let wrapper: ReturnType<typeof createComponent>
  97. const findItems = () => wrapper.findAllComponents(ElRovingFocusItem)
  98. const findDOMItems = () => wrapper.findAll(`.${focusItemKls}`)
  99. beforeEach(async () => {
  100. wrapper = createComponent()
  101. await nextTick()
  102. })
  103. afterEach(() => {
  104. wrapper.unmount()
  105. })
  106. describe('rendering test', () => {
  107. it('should be able to render content', () => {
  108. expect(wrapper.html()).toContain(AXIOM)
  109. expect(findItems()).toHaveLength(3)
  110. })
  111. })
  112. describe('roving focus', () => {
  113. it('should be able to handle mousedown event', async () => {
  114. const DOMItems = findDOMItems()
  115. const items = findItems()
  116. const firstDOMItem = DOMItems.at(0)
  117. await firstDOMItem.trigger('mousedown')
  118. await nextTick()
  119. const firstItem = items.at(0)
  120. expect(firstItem.emitted()).toHaveProperty('mousedown')
  121. await wrapper.setProps({
  122. focusable: false,
  123. })
  124. await firstDOMItem.trigger('mousedown')
  125. // when the item is not focusable, the focus event should be prevented by default
  126. const emittedEvents = firstItem.emitted().mousedown
  127. expect(emittedEvents.at(0)[0].defaultPrevented).toBe(false)
  128. expect(emittedEvents.at(1)[0].defaultPrevented).toBe(true)
  129. })
  130. it('should be able to handle focus event', async () => {
  131. expect(onItemFocus).not.toHaveBeenCalled()
  132. const DOMItems = findDOMItems()
  133. const firstDOMItem = DOMItems.at(0)
  134. await firstDOMItem.trigger('focus')
  135. expect(onItemFocus).toHaveBeenCalled()
  136. expect(findItems().at(0).emitted()).toHaveProperty('focus')
  137. })
  138. it('should be able to handle keyboard navigation', async () => {
  139. const DOMItems = findDOMItems()
  140. const items = findItems()
  141. const firstDOMItem = DOMItems.at(0)
  142. expect(onItemShiftTab).not.toHaveBeenCalled()
  143. await firstDOMItem.trigger('keydown.shift', {
  144. key: EVENT_CODE.tab,
  145. })
  146. expect(items.at(0).emitted()).toHaveProperty('keydown')
  147. expect(onItemShiftTab).toHaveBeenCalled()
  148. // navigating clockwise
  149. expect(document.activeElement).toBe(document.body)
  150. await DOMItems.at(1).trigger('keydown', {
  151. key: EVENT_CODE.down,
  152. })
  153. await nextTick()
  154. expect(document.activeElement).toStrictEqual(DOMItems.at(2).element)
  155. // navigate anticlockwise
  156. await DOMItems.at(1).trigger('keydown', {
  157. key: EVENT_CODE.up,
  158. })
  159. await nextTick()
  160. expect(document.activeElement).toStrictEqual(DOMItems.at(0).element)
  161. // should be able to focus on the last element when press End
  162. await DOMItems.at(0).trigger('keydown', {
  163. key: EVENT_CODE.end,
  164. })
  165. await nextTick()
  166. expect(document.activeElement).toStrictEqual(DOMItems.at(2).element)
  167. await DOMItems.at(0).trigger('keydown', {
  168. key: EVENT_CODE.home,
  169. })
  170. await nextTick()
  171. expect(document.activeElement).toStrictEqual(DOMItems.at(0).element)
  172. })
  173. })
  174. })