fix(page-controller): address Copilot review feedback
## Changes 1. **Check beforeinput cancellation** - dispatchEvent returns false if canceled - Check defaultPrevented as well - Abort mutation if event was canceled by any listener 2. **Fix event order to match real user typing** - Before: beforeinput -> mutation -> input -> keydown -> keyup - After: keydown -> beforeinput -> mutation -> input -> keyup - This matches typical browser event sequence 3. **Fix blur event semantics** - blur doesn't bubble; focusout does - Call editableElement.blur() to actually change focus - Dispatch focusout with bubbles:true for listeners - Then refocus 4. **Keep single-character keyboard events** - Already fixed in previous commit - Maintained here with correct order All changes follow Copilot's suggested fixes.
This commit is contained in:
@@ -125,23 +125,8 @@ export async function inputTextElement(element: HTMLElement, text: string) {
|
|||||||
// Clear existing content
|
// Clear existing content
|
||||||
editableElement.innerText = ''
|
editableElement.innerText = ''
|
||||||
|
|
||||||
// Dispatch beforeinput event (important for React apps)
|
// Dispatch keydown first (typical event order: keydown -> beforeinput -> mutation -> input -> keyup)
|
||||||
const beforeInputEvent = new InputEvent('beforeinput', {
|
// Only for single-character input to maintain semantic consistency
|
||||||
bubbles: true,
|
|
||||||
cancelable: true,
|
|
||||||
inputType: 'insertText',
|
|
||||||
data: text,
|
|
||||||
})
|
|
||||||
editableElement.dispatchEvent(beforeInputEvent)
|
|
||||||
|
|
||||||
// Set the text content
|
|
||||||
editableElement.innerText = text
|
|
||||||
|
|
||||||
// Dispatch input event (standard)
|
|
||||||
editableElement.dispatchEvent(new Event('input', { bubbles: true }))
|
|
||||||
|
|
||||||
// Dispatch keydown/keyup events for frameworks that listen to keyboard.
|
|
||||||
// To avoid inconsistent semantics, only do this for single-character input.
|
|
||||||
if (text.length === 1) {
|
if (text.length === 1) {
|
||||||
const keydownEvent = new KeyboardEvent('keydown', {
|
const keydownEvent = new KeyboardEvent('keydown', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
@@ -149,7 +134,30 @@ export async function inputTextElement(element: HTMLElement, text: string) {
|
|||||||
key: text,
|
key: text,
|
||||||
})
|
})
|
||||||
editableElement.dispatchEvent(keydownEvent)
|
editableElement.dispatchEvent(keydownEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch beforeinput event (important for React apps)
|
||||||
|
// Check if canceled - if so, abort the mutation
|
||||||
|
const beforeInputEvent = new InputEvent('beforeinput', {
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true,
|
||||||
|
inputType: 'insertText',
|
||||||
|
data: text,
|
||||||
|
})
|
||||||
|
const notCanceled = editableElement.dispatchEvent(beforeInputEvent)
|
||||||
|
if (!notCanceled || beforeInputEvent.defaultPrevented) {
|
||||||
|
// Listener canceled the input, abort
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the text content (DOM mutation)
|
||||||
|
editableElement.innerText = text
|
||||||
|
|
||||||
|
// Dispatch input event (standard)
|
||||||
|
editableElement.dispatchEvent(new Event('input', { bubbles: true }))
|
||||||
|
|
||||||
|
// Dispatch keyup after input (completing the typical event sequence)
|
||||||
|
if (text.length === 1) {
|
||||||
const keyupEvent = new KeyboardEvent('keyup', {
|
const keyupEvent = new KeyboardEvent('keyup', {
|
||||||
bubbles: true,
|
bubbles: true,
|
||||||
cancelable: true,
|
cancelable: true,
|
||||||
@@ -161,8 +169,10 @@ export async function inputTextElement(element: HTMLElement, text: string) {
|
|||||||
// Dispatch change event (for good measure)
|
// Dispatch change event (for good measure)
|
||||||
editableElement.dispatchEvent(new Event('change', { bubbles: true }))
|
editableElement.dispatchEvent(new Event('change', { bubbles: true }))
|
||||||
|
|
||||||
// Dispatch blur and refocus to trigger any validation
|
// Trigger a real blur and a bubbling focusout to run any validation, then refocus
|
||||||
editableElement.dispatchEvent(new FocusEvent('blur', { bubbles: true }))
|
// Note: blur doesn't bubble, focusout does
|
||||||
|
editableElement.blur()
|
||||||
|
editableElement.dispatchEvent(new FocusEvent('focusout', { bubbles: true }))
|
||||||
editableElement.focus()
|
editableElement.focus()
|
||||||
} else if (element instanceof HTMLTextAreaElement) {
|
} else if (element instanceof HTMLTextAreaElement) {
|
||||||
nativeTextAreaValueSetter.call(element, text)
|
nativeTextAreaValueSetter.call(element, text)
|
||||||
|
|||||||
Reference in New Issue
Block a user