fix(components): [tree] keyboard nav broken after filter/collapse (#21377)

* fix(components): [tree] keyboard nav broken after filter/collapse

Fix issues #21376

* fix(components): [tree] keyboard nav broken after filter/collapse

* fix(components): [tree] keyboard nav broken after filter/collapse

* Update packages/components/tree/src/model/node.ts

Co-authored-by: Noblet Ouways <91417411+Dsaquel@users.noreply.github.com>

* Update packages/components/tree/src/model/node.ts

Co-authored-by: Noblet Ouways <91417411+Dsaquel@users.noreply.github.com>

* fix(components): [tree] keyboard nav broken after filter/collapse

* fix(components): [tree] add test cases

* fix(components): [tree] keyboard nav broken after filter/collapse

* fix(components): [tree] keyboard nav broken after filter/collapse

---------

Co-authored-by: Noblet Ouways <91417411+Dsaquel@users.noreply.github.com>
This commit is contained in:
Spaceman
2025-07-21 10:17:32 +08:00
committed by GitHub
parent a36792abc5
commit e34cd311ef
3 changed files with 117 additions and 8 deletions

View File

@@ -1481,6 +1481,102 @@ describe('Tree.vue', () => {
expect(flag).toBe(true)
})
test('collapse and navigate down and up', async () => {
const { wrapper } = getTreeVm(``, {
template: `
<div>
<el-tree ref="tree1" :data="data" node-key="id" :props="defaultProps" default-expand-all></el-tree>
</div>
`,
})
await nextTick()
let flag = false
function handleFocus() {
return () => (flag = true)
}
const tree = wrapper.findComponent({ name: 'ElTree' })
const targetElement = wrapper.find('div[data-key="2"]').element
const fromElement = wrapper.find('div[data-key="1"]')
await fromElement.trigger('click')
expect(fromElement.classes('is-expanded')).toBe(false) // 判断是否已折叠
await nextTick()
defineGetter(targetElement, 'focus', handleFocus)
;(tree.vm as InstanceType<typeof Tree>).setCurrentKey(1)
// 模拟按下下箭头键
fromElement.element.dispatchEvent(
new KeyboardEvent('keydown', {
code: 'ArrowDown',
bubbles: true,
cancelable: false,
})
)
expect(flag).toBe(true)
await nextTick()
flag = false
defineGetter(fromElement.element, 'focus', handleFocus)
// 模拟按下上箭头键
targetElement.dispatchEvent(
new KeyboardEvent('keydown', {
code: 'ArrowUp',
bubbles: true,
cancelable: false,
})
)
expect(flag).toBe(true)
})
test('filter-node-method and navigate down', async () => {
const { wrapper } = getTreeVm(``, {
template: `
<div>
<el-tree ref="tree1" :data="data" node-key="id" :props="defaultProps" :filter-node-method="filterNode"></el-tree>
</div>
`,
methods: {
filterNode(value, data) {
if (!value) return true
return data.label.includes(value)
},
},
})
let flag = false
function handleFocus() {
return () => (flag = true)
}
const treeWrapper = wrapper.findComponent(Tree)
;(treeWrapper.vm as InstanceType<typeof Tree>).filter('-1')
await sleep()
const tree = wrapper.findComponent({ name: 'ElTree' })
;(tree.vm as InstanceType<typeof Tree>).setCurrentKey(1)
const allNodes = treeWrapper.findAll('.el-tree-node')
const visibleNodes = allNodes.filter(
(node) => !node.classes().includes('is-hidden')
)
const len = visibleNodes.length
for (let i = 0; i < len; i++) {
if (visibleNodes[i + 1]) {
defineGetter(visibleNodes[i + 1].element, 'focus', handleFocus)
// 模拟按下下箭头键
visibleNodes[i].element.dispatchEvent(
new KeyboardEvent('keydown', {
code: 'ArrowDown',
bubbles: true,
cancelable: false,
})
)
expect(flag).toBe(true)
flag = false
}
}
})
test('navigate with disabled', async () => {
const wrapper = mount({
template: `

View File

@@ -79,6 +79,13 @@ const getPropertyFromData = function (node: Node, prop: string): any {
}
}
const setCanFocus = function (childNodes: Node[], focus: boolean): void {
childNodes.forEach((item) => {
item.canFocus = focus
setCanFocus(item.childNodes, focus)
})
}
let nodeIdSeed = 0
class Node {
@@ -359,9 +366,7 @@ class Node {
}
this.expanded = true
if (callback) callback()
this.childNodes.forEach((item) => {
item.canFocus = true
})
setCanFocus(this.childNodes, true)
}
if (this.shouldLoadData()) {
@@ -395,9 +400,7 @@ class Node {
collapse(): void {
this.expanded = false
this.childNodes.forEach((item) => {
item.canFocus = false
})
setCanFocus(this.childNodes, false)
}
shouldLoadData() {

View File

@@ -46,8 +46,13 @@ export function useKeydown({ el$ }: UseKeydownOption, store: Ref<TreeStore>) {
: treeItems.length - 1
const startIndex = nextIndex
while (true) {
if (store.value.getNode(treeItems[nextIndex].dataset.key!).canFocus)
const currentNode = store.value.getNode(
treeItems[nextIndex].dataset.key!
)
if (currentNode.canFocus && currentNode.visible) {
break
}
nextIndex--
if (nextIndex === startIndex) {
nextIndex = -1
@@ -66,8 +71,13 @@ export function useKeydown({ el$ }: UseKeydownOption, store: Ref<TreeStore>) {
: 0
const startIndex = nextIndex
while (true) {
if (store.value.getNode(treeItems[nextIndex].dataset.key!).canFocus)
const currentNode = store.value.getNode(
treeItems[nextIndex].dataset.key!
)
if (currentNode.canFocus && currentNode.visible) {
break
}
nextIndex++
if (nextIndex === startIndex) {
nextIndex = -1