mirror of
https://github.com/zhw2590582/ArtPlayer.git
synced 2026-10-24 06:31:20 -08:00
refactor(core): [CORE-23] complete keyboard controls and focus lifecycle
This commit is contained in:
1 parent
faaa3e8c2d
commit
8ed0a5228c
190 files changed
+53981
-489
No files matched your search
@@ -0,0 +1,110 @@
|
||||
import assert from 'node:assert/strict'
|
||||
// eslint-disable-next-line test/no-import-node-test -- Verify focus modality ownership and document rebinding.
|
||||
import { test } from 'node:test'
|
||||
import { loadModules } from './helpers/load.js'
|
||||
|
||||
const { focusVisibility, captureMovedFocus, ResourceScope } = await loadModules({
|
||||
focusVisibility: { file: 'packages/artplayer/src/accessibility/focus', name: 'focusVisibility' },
|
||||
captureMovedFocus: { file: 'packages/artplayer/src/accessibility/moved-focus', name: 'captureMovedFocus' },
|
||||
ResourceScope: 'packages/artplayer/src/lifecycle/scope',
|
||||
})
|
||||
|
||||
test('DOM moves restore the same connected descendant and preserve externally moved focus', () => {
|
||||
const body = {}
|
||||
const outside = {}
|
||||
const doc = { body, activeElement: null }
|
||||
let count = 0
|
||||
const child = { ownerDocument: doc, isConnected: true, focus(options) {
|
||||
assert.deepEqual(options, { preventScroll: true })
|
||||
count++
|
||||
doc.activeElement = this
|
||||
} }
|
||||
const root = { ownerDocument: doc, contains: target => target === child }
|
||||
doc.activeElement = child
|
||||
const restore = captureMovedFocus(root)
|
||||
doc.activeElement = body
|
||||
restore()
|
||||
assert.equal(doc.activeElement, child)
|
||||
restore()
|
||||
assert.equal(count, 1)
|
||||
doc.activeElement = outside
|
||||
restore()
|
||||
assert.equal(doc.activeElement, outside)
|
||||
doc.activeElement = body
|
||||
child.isConnected = false
|
||||
restore()
|
||||
assert.equal(count, 1)
|
||||
child.isConnected = true
|
||||
doc.activeElement = outside
|
||||
const unrelated = captureMovedFocus(root)
|
||||
doc.activeElement = body
|
||||
unrelated()
|
||||
assert.equal(count, 1)
|
||||
})
|
||||
|
||||
function fixture() {
|
||||
const scope = new ResourceScope()
|
||||
const doc = Object.assign(new EventTarget(), { activeElement: null })
|
||||
const classes = new Set()
|
||||
const child = {}
|
||||
const root = Object.assign(new EventTarget(), {
|
||||
ownerDocument: doc,
|
||||
contains: value => value === child,
|
||||
classList: {
|
||||
contains: name => classes.has(name),
|
||||
remove: name => classes.delete(name),
|
||||
toggle: (name, active) => active ? classes.add(name) : classes.delete(name),
|
||||
},
|
||||
})
|
||||
let shows = 0
|
||||
const focused = focusVisibility(scope, root, () => shows++)
|
||||
const focus = () => {
|
||||
root.ownerDocument.activeElement = child
|
||||
root.dispatchEvent(new Event('focusin'))
|
||||
}
|
||||
return { scope, doc, root, child, classes, focused, focus, shows: () => shows }
|
||||
}
|
||||
|
||||
test('keyboard visibility follows actual focus without making pointer focus sticky', () => {
|
||||
const f = fixture()
|
||||
assert.equal(f.focused(), false)
|
||||
f.focus()
|
||||
assert.equal(f.focused(), true)
|
||||
assert.equal(f.shows(), 1)
|
||||
f.doc.dispatchEvent(new Event('mousedown'))
|
||||
assert.equal(f.focused(), false)
|
||||
assert.equal(f.classes.size, 0)
|
||||
f.doc.dispatchEvent(new Event('keydown'))
|
||||
assert.equal(f.focused(), true)
|
||||
assert.equal(f.shows(), 2)
|
||||
f.doc.dispatchEvent(new Event('keydown'))
|
||||
assert.equal(f.shows(), 2)
|
||||
f.doc.activeElement = null
|
||||
f.root.dispatchEvent(new Event('focusout'))
|
||||
assert.equal(f.focused(), false)
|
||||
assert.equal(f.classes.size, 0)
|
||||
f.scope.dispose()
|
||||
})
|
||||
|
||||
test('moving a focused player rebinds modality listeners and disposal releases both documents', () => {
|
||||
const f = fixture()
|
||||
f.focus()
|
||||
const next = Object.assign(new EventTarget(), { activeElement: null })
|
||||
f.root.ownerDocument = next
|
||||
f.focus()
|
||||
f.doc.dispatchEvent(new Event('mousedown'))
|
||||
assert.equal(f.focused(), true)
|
||||
next.dispatchEvent(new Event('touchstart'))
|
||||
assert.equal(f.focused(), false)
|
||||
next.dispatchEvent(new Event('keydown'))
|
||||
assert.equal(f.focused(), true)
|
||||
const shows = f.shows()
|
||||
f.scope.dispose()
|
||||
assert.equal(f.focused(), false)
|
||||
assert.equal(f.classes.size, 0)
|
||||
for (const target of [f.doc, next, f.root])
|
||||
target.dispatchEvent(new Event('keydown'))
|
||||
f.focus()
|
||||
assert.equal(f.shows(), shows)
|
||||
assert.equal(f.classes.size, 0)
|
||||
})
|
||||
Reference in new issue
Block a user