fireInputEvent.js
3.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.fireInputEvent = fireInputEvent;
var _dom = require("@testing-library/dom");
var _utils = require("../../utils");
function fireInputEvent(element, {
newValue,
newSelectionStart,
eventOverrides
}) {
// apply the changes before firing the input event, so that input handlers can access the altered dom and selection
if ((0, _utils.isContentEditable)(element)) {
applyNative(element, 'textContent', newValue);
} else
/* istanbul ignore else */
if ((0, _utils.isElementType)(element, ['input', 'textarea'])) {
applyNative(element, 'value', newValue);
} else {
// TODO: properly type guard
throw new Error('Invalid Element');
}
setSelectionRangeAfterInput(element, newSelectionStart);
_dom.fireEvent.input(element, { ...eventOverrides
});
setSelectionRangeAfterInputHandler(element, newValue, newSelectionStart);
}
function setSelectionRangeAfterInput(element, newSelectionStart) {
(0, _utils.setSelectionRange)(element, newSelectionStart, newSelectionStart);
}
function setSelectionRangeAfterInputHandler(element, newValue, newSelectionStart) {
const value = (0, _utils.getValue)(element); // don't apply this workaround on elements that don't necessarily report the visible value - e.g. number
// TODO: this could probably be only applied when there is keyboardState.carryValue
const isUnreliableValue = value === '' && (0, _utils.hasUnreliableEmptyValue)(element);
if (!isUnreliableValue && value === newValue) {
const {
selectionStart
} = (0, _utils.getSelectionRange)(element);
if (selectionStart === value.length) {
// The value was changed as expected, but the cursor was moved to the end
// TODO: this could probably be only applied when we work around a framework setter on the element in applyNative
(0, _utils.setSelectionRange)(element, newSelectionStart, newSelectionStart);
}
}
}
const initial = Symbol('initial input value/textContent');
const onBlur = Symbol('onBlur');
/**
* React tracks the changes on element properties.
* This workaround tries to alter the DOM element without React noticing,
* so that it later picks up the change.
*
* @see https://github.com/facebook/react/blob/148f8e497c7d37a3c7ab99f01dec2692427272b1/packages/react-dom/src/client/inputValueTracking.js#L51-L104
*/
function applyNative(element, propName, propValue) {
const descriptor = Object.getOwnPropertyDescriptor(element, propName);
const nativeDescriptor = Object.getOwnPropertyDescriptor(element.constructor.prototype, propName);
if (descriptor && nativeDescriptor) {
Object.defineProperty(element, propName, nativeDescriptor);
} // Keep track of the initial value to determine if a change event should be dispatched.
// CONSTRAINT: We can not determine what happened between focus event and our first API call.
if (element[initial] === undefined) {
element[initial] = String(element[propName]);
}
element[propName] = propValue; // Add an event listener for the blur event to the capture phase on the window.
// CONSTRAINT: Currently there is no cross-platform solution to unshift the event handler stack.
// Our change event might occur after other event handlers on the blur event have been processed.
if (!element[onBlur]) {
var _element$ownerDocumen;
(_element$ownerDocumen = element.ownerDocument.defaultView) == null ? void 0 : _element$ownerDocumen.addEventListener('blur', element[onBlur] = () => {
const initV = element[initial]; // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete element[onBlur]; // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
delete element[initial];
if (String(element[propName]) !== initV) {
_dom.fireEvent.change(element);
}
}, {
capture: true,
once: true
});
}
if (descriptor) {
Object.defineProperty(element, propName, descriptor);
}
}