Skip to content

Commit

Permalink
fix: reset UI selection on setter (#770)
Browse files Browse the repository at this point in the history
  • Loading branch information
ph-fritsche committed Nov 28, 2021
1 parent 55e194a commit 2733d10
Show file tree
Hide file tree
Showing 2 changed files with 68 additions and 21 deletions.
63 changes: 42 additions & 21 deletions src/document/selection.ts
Expand Up @@ -12,31 +12,52 @@ declare global {
}
}

function setSelectionInterceptor(
this: HTMLInputElement | HTMLTextAreaElement,
start: number | Value | null,
end: number | null,
direction: 'forward' | 'backward' | 'none' = 'none',
export function prepareSelectionInterceptor(
element: HTMLInputElement | HTMLTextAreaElement,
) {
const isUI = start && typeof start === 'object' && start[UISelection]
prepareInterceptor(
element,
'setSelectionRange',
function interceptorImpl(
this: HTMLInputElement | HTMLTextAreaElement,
start: number | Value | null,
end: number | null,
direction: 'forward' | 'backward' | 'none' = 'none',
) {
const isUI = start && typeof start === 'object' && start[UISelection]

this[UISelection] = isUI
? {start: start.valueOf(), end: Number(end)}
: undefined
this[UISelection] = isUI
? {start: start.valueOf(), end: Number(end)}
: undefined

return {
realArgs: [Number(start), end, direction] as [
number,
number,
typeof direction,
],
}
}
return {
realArgs: [Number(start), end, direction] as [
number,
number,
typeof direction,
],
}
},
)

export function prepareSelectionInterceptor(
element: HTMLInputElement | HTMLTextAreaElement,
) {
prepareInterceptor(element, 'setSelectionRange', setSelectionInterceptor)
prepareInterceptor(
element,
'selectionStart',
function interceptorImpl(this, v) {
this[UISelection] = undefined

return {realArgs: v}
},
)
prepareInterceptor(
element,
'selectionEnd',
function interceptorImpl(this, v) {
this[UISelection] = undefined

return {realArgs: v}
},
)
}

export function setUISelection(
Expand Down
26 changes: 26 additions & 0 deletions tests/document/index.ts
Expand Up @@ -92,3 +92,29 @@ test('maintain selection range on elements without support for selection range',
})
expect(element.selectionStart).toBe(null)
})

test('clear UI selection if selection is programmatically set', () => {
const {element} = setup<HTMLInputElement>(`<input/>`)

prepare(element)

element.focus()
setUIValue(element, 'abc')
setUISelection(element, 1, 2)
expect(element.selectionStart).toBe(1)

element.setSelectionRange(0, 1)
expect(getUISelection(element)).toEqual({selectionStart: 0, selectionEnd: 1})

setUISelection(element, 2, 3)
expect(getUISelection(element)).toEqual({selectionStart: 2, selectionEnd: 3})

element.selectionEnd = 1
expect(getUISelection(element)).toEqual({selectionStart: 1, selectionEnd: 1})

setUISelection(element, 0, 1)
expect(getUISelection(element)).toEqual({selectionStart: 0, selectionEnd: 1})

element.selectionStart = 2
expect(getUISelection(element)).toEqual({selectionStart: 2, selectionEnd: 2})
})

0 comments on commit 2733d10

Please sign in to comment.