Skip to content

Commit d698a45

Browse files
marker-daomarker dao ®
andauthored
Caret Utils: Remove body contains input check (T1309092) (#31375)
Co-authored-by: marker dao ® <[email protected]>
1 parent 904778f commit d698a45

File tree

2 files changed

+59
-40
lines changed

2 files changed

+59
-40
lines changed
Lines changed: 38 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,33 @@
11
import devices from '@js/core/devices';
22
import domAdapter from '@js/core/dom_adapter';
3+
import type { dxElementWrapper } from '@js/core/renderer';
34
import $ from '@js/core/renderer';
45
import { isDefined } from '@js/core/utils/type';
56

6-
export interface CaretRange { start: number; end: number }
7+
export interface CaretRange {
8+
start: number;
9+
end: number;
10+
}
711

812
const {
913
ios,
10-
// @ts-expect-error
14+
// @ts-expect-error Device type doesn't contain mac
1115
mac,
1216
} = devices.real();
17+
1318
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
1419
const isFocusingOnCaretChange = ios || mac;
1520

1621
const getCaret = (input: HTMLInputElement): CaretRange => {
17-
let range;
22+
let range: CaretRange = {
23+
start: 0,
24+
end: 0,
25+
};
1826

1927
try {
2028
range = {
21-
start: input.selectionStart,
22-
end: input.selectionEnd,
29+
start: input.selectionStart ?? 0,
30+
end: input.selectionEnd ?? 0,
2331
};
2432
} catch (e) {
2533
range = {
@@ -31,32 +39,39 @@ const getCaret = (input: HTMLInputElement): CaretRange => {
3139
return range;
3240
};
3341

34-
const setCaret = (input, position) => {
35-
const body = domAdapter.getBody();
36-
if (!body.contains(input) && !body.contains(input.getRootNode().host)) {
37-
return;
38-
}
39-
42+
export const setCaret = (
43+
input: HTMLInputElement,
44+
selection: CaretRange,
45+
): void => {
4046
try {
41-
input.selectionStart = position.start;
42-
input.selectionEnd = position.end;
43-
} catch (e) { /* empty */ }
47+
input.selectionStart = selection.start;
48+
input.selectionEnd = selection.end;
49+
} catch { /** empty */ }
4450
};
45-
// @ts-expect-error
46-
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
47-
const caret = (input, position?: any, force = false): CaretRange | undefined => {
48-
input = $(input).get(0);
4951

50-
if (!isDefined(position)) {
51-
return getCaret(input);
52+
const caret = (
53+
input: HTMLInputElement | dxElementWrapper,
54+
selection?: CaretRange,
55+
force = false,
56+
): CaretRange | undefined => {
57+
const inputElement = $(input).get(0) as HTMLInputElement;
58+
59+
if (!isDefined(selection)) {
60+
return getCaret(inputElement);
5261
}
5362

5463
// NOTE: AppleWebKit-based browsers focuses element input after caret position has changed
55-
if (!force && isFocusingOnCaretChange && domAdapter.getActiveElement(input) !== input) {
56-
return;
64+
if (
65+
!force
66+
&& isFocusingOnCaretChange
67+
&& domAdapter.getActiveElement(inputElement) !== inputElement
68+
) {
69+
return undefined;
5770
}
5871

59-
setCaret(input, position);
72+
setCaret(inputElement, selection);
73+
74+
return undefined;
6075
};
6176

6277
export default caret;

packages/devextreme/testing/tests/DevExpress.utils/utils.caret.tests.js

Lines changed: 21 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,28 @@ testModule('caret', () => {
2828
assert.deepEqual(caret($input), caretPosition, 'caret position set correctly');
2929
});
3030

31-
test('T341277 - an exception if element is not in document', function(assert) {
32-
const caretPosition = { start: 1, end: 2 };
31+
test('exception should not be thrown if the input is not attached to the DOM (T341277)', function(assert) {
32+
const setterStub = sinon.stub();
3333
const input = document.createElement('input');
3434

35+
Object.defineProperty(input, 'selectionStart', {
36+
set() {
37+
setterStub();
38+
throw new Error('Cannot set selection');
39+
},
40+
});
41+
42+
const getActiveElementStub = sinon.stub(domAdapter, 'getActiveElement').returns(input);
43+
3544
try {
36-
caret(input, caretPosition);
45+
caret(input, { start: 1, end: 2 });
46+
47+
assert.strictEqual(setterStub.callCount, 1, 'setter has been called');
3748
assert.ok(true, 'exception is not thrown');
3849
} catch(e) {
3950
assert.ok(false, 'exception is thrown');
51+
} finally {
52+
getActiveElementStub.restore();
4053
}
4154
});
4255

@@ -61,28 +74,19 @@ testModule('caret', () => {
6174

6275
test('\'setCaret\' does not raise an error when it is impossible to set a range', function(assert) {
6376
const caretPosition = { start: 1, end: 2 };
64-
const initialDescriptor = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'selectionStart');
65-
const getterSetterConfig = {
66-
get: function() {
67-
throw 'You can not get a selection';
68-
},
69-
set: function(value) {
70-
throw 'You can not set a selection';
71-
}
72-
};
73-
74-
Object.defineProperty(HTMLInputElement.prototype, 'selectionStart', $.extend({}, initialDescriptor, getterSetterConfig));
75-
7677
const input = $('<input>').appendTo('#qunit-fixture').get(0);
7778

79+
Object.defineProperty(input, 'selectionStart', {
80+
set() { throw new Error('Cannot set selection'); },
81+
get() { throw new Error('Cannot set selection'); },
82+
});
83+
7884
try {
7985
caret(input, caretPosition);
8086
assert.ok(true, 'exception is not thrown');
8187
} catch(e) {
8288
assert.ok(false, 'exception is thrown');
8389
}
84-
85-
Object.defineProperty(HTMLInputElement.prototype, 'selectionStart', initialDescriptor);
8690
});
8791

8892
[false, true].forEach((forceSetCaret) => {

0 commit comments

Comments
 (0)