Skip to content

Commit 8bc3310

Browse files
authored
fix: reset UI selection after programmatic value change (#915)
1 parent f5049c4 commit 8bc3310

File tree

3 files changed

+23
-3
lines changed

3 files changed

+23
-3
lines changed

src/document/interceptor.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ export function prepareInterceptor<
2828
*/
2929
applyNative?: boolean
3030
realArgs?: ImplReturn<ElementType[PropName]>
31+
then?: () => void
3132
},
3233
) {
3334
const prototypeDescriptor = Object.getOwnPropertyDescriptor(
@@ -49,7 +50,11 @@ export function prepareInterceptor<
4950
this: ElementType,
5051
...args: Params<ElementType[PropName]>
5152
) {
52-
const {applyNative = true, realArgs} = interceptorImpl.call(this, ...args)
53+
const {
54+
applyNative = true,
55+
realArgs,
56+
then,
57+
} = interceptorImpl.call(this, ...args)
5358

5459
const realFunc = ((!applyNative && objectDescriptor) ||
5560
(prototypeDescriptor as PropertyDescriptor))[target] as (
@@ -62,6 +67,8 @@ export function prepareInterceptor<
6267
} else {
6368
realFunc.call(this, ...realArgs)
6469
}
70+
71+
then?.()
6572
}
6673
;(intercept as Interceptable)[Interceptor] = Interceptor
6774

src/document/value.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,12 @@ function valueInterceptor(
3232
if (isUI) {
3333
this[UIValue] = String(v)
3434
setPreviousValue(this, String(this.value))
35-
} else {
36-
trackOrSetValue(this, String(v))
3735
}
3836

3937
return {
4038
applyNative: !!isUI,
4139
realArgs: sanitizeValue(this, v),
40+
then: isUI ? undefined : () => trackOrSetValue(this, String(v)),
4241
}
4342
}
4443

tests/document/index.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,20 @@ test('maintain selection range on elements without support for selection range',
112112
expect(element.selectionStart).toBe(null)
113113
})
114114

115+
test('reset UI selection if value is programmatically set', async () => {
116+
const {element} = render<HTMLInputElement>(`<input/>`)
117+
118+
prepare(element)
119+
120+
setUIValue(element, 'abc')
121+
setUISelection(element, {anchorOffset: 1, focusOffset: 2})
122+
123+
element.value = 'abcdef'
124+
expect(element.selectionStart).toBe(6)
125+
expect(getUISelection(element)).toHaveProperty('focusOffset', 6)
126+
expect(getUISelection(element)).toHaveProperty('startOffset', 6)
127+
})
128+
115129
test('clear UI selection if selection is programmatically set', async () => {
116130
const {element} = render<HTMLInputElement>(`<input/>`)
117131

0 commit comments

Comments
 (0)