Skip to content

Commit 73443ec

Browse files
authored
fix(document): track HTMLInputElement.setRangeText() (#984)
1 parent 0a3fe4f commit 73443ec

File tree

5 files changed

+56
-0
lines changed

5 files changed

+56
-0
lines changed

src/document/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import {dispatchUIEvent} from '../event'
22
import {Config} from '../setup'
33
import {prepareSelectionInterceptor} from './selection'
4+
import {prepareRangeTextInterceptor} from './setRangeText'
45
import {
56
clearInitialValue,
67
getInitialValue,
@@ -69,6 +70,7 @@ function prepareElement(el: Node | HTMLInputElement) {
6970
if ('value' in el) {
7071
prepareValueInterceptor(el)
7172
prepareSelectionInterceptor(el)
73+
prepareRangeTextInterceptor(el)
7274
}
7375

7476
el[isPrepared] = isPrepared

src/document/selection.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,3 +144,10 @@ export function getUISelection(
144144
endOffset: Math.max(sel.anchorOffset, sel.focusOffset),
145145
}
146146
}
147+
148+
/** Flag the IDL selection as clean. This does not change the selection. */
149+
export function setUISelectionClean(
150+
element: HTMLInputElement | HTMLTextAreaElement,
151+
) {
152+
element[UISelection] = undefined
153+
}

src/document/setRangeText.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import {prepareInterceptor} from './interceptor'
2+
import {setUISelectionClean} from './selection'
3+
import {setUIValueClean} from './value'
4+
5+
export function prepareRangeTextInterceptor(
6+
element: HTMLInputElement | HTMLTextAreaElement,
7+
) {
8+
prepareInterceptor(
9+
element,
10+
'setRangeText',
11+
function interceptorImpl(...realArgs) {
12+
return {
13+
realArgs,
14+
then: () => {
15+
setUIValueClean(element)
16+
setUISelectionClean(element)
17+
},
18+
}
19+
},
20+
)
21+
}

src/document/value.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,13 @@ export function getUIValue(element: HTMLInputElement | HTMLTextAreaElement) {
8181
: String(element[UIValue])
8282
}
8383

84+
/** Flag the IDL value as clean. This does not change the value.*/
85+
export function setUIValueClean(
86+
element: HTMLInputElement | HTMLTextAreaElement,
87+
) {
88+
element[UIValue] = undefined
89+
}
90+
8491
export function clearInitialValue(
8592
element: HTMLInputElement | HTMLTextAreaElement,
8693
) {

tests/document/index.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,22 @@ test('select input without selectionRange support', () => {
183183
expect(getUISelection(element)).toHaveProperty('startOffset', 0)
184184
expect(getUISelection(element)).toHaveProperty('endOffset', 3)
185185
})
186+
187+
test('track changes to value and selection per setRangeText', () => {
188+
const {element} = render<HTMLInputElement>(`<input/>`)
189+
prepare(element)
190+
setUIValue(element, 'abcd')
191+
setUISelection(element, {focusOffset: 3})
192+
193+
element.setRangeText('X', 1, 2)
194+
expect(element).toHaveValue('aXcd')
195+
expect(element).toHaveProperty('selectionStart', 3)
196+
expect(getUIValue(element)).toBe('aXcd')
197+
expect(getUISelection(element)).toHaveProperty('focusOffset', 3)
198+
199+
element.setRangeText('Y', 1, 2, 'start')
200+
expect(element).toHaveValue('aYcd')
201+
expect(element).toHaveProperty('selectionEnd', 1)
202+
expect(getUIValue(element)).toBe('aYcd')
203+
expect(getUISelection(element)).toHaveProperty('focusOffset', 1)
204+
})

0 commit comments

Comments
 (0)