Skip to content

Commit 134ab03

Browse files
LaurenWyatt610mmissey
authored andcommitted
Added tests and fixed IE IndexSizeError trying to get a range from a selection when there is not one (facebookarchive#2271)
Summary: **Summary** Various browsers will throw errors if `selection.getRangeAt(0)` is called when there is no range. To fix this, a check was added to make sure a range exists. This change was originally suggested [here](facebookarchive#1571) a few years ago. I opted for opening a new PR and adding tests here since that PR seemed stale. Selection and Range did not work in the test environment, so I had to stub them. I only added functionality for what was needed in the tests I added, but more can be added in the future very easily. Pull Request resolved: facebookarchive#2271 Differential Revision: D18807105 Pulled By: mrkev fbshipit-source-id: 0e3b833b8a3267b9a5f17b262b6a0442b6ae5e3d
1 parent 2745433 commit 134ab03

File tree

4 files changed

+140
-5
lines changed

4 files changed

+140
-5
lines changed

src/component/contents/DraftEditorLeaf.react.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ const React = require('React');
2020

2121
const invariant = require('invariant');
2222
const isHTMLBRElement = require('isHTMLBRElement');
23-
const setDraftEditorSelection = require('setDraftEditorSelection');
23+
const setDraftEditorSelection = require('setDraftEditorSelection')
24+
.setDraftEditorSelection;
2425

2526
type Props = {
2627
// The block that contains this leaf.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`addFocusToSelection sets a new focus on the selection if selection.extend is unsupported 1`] = `
4+
Selection {
5+
"focusNode": Washington,
6+
"focusOffset": 0,
7+
"range": Range {
8+
"endOffset": 3,
9+
"node": Washington,
10+
"startOffset": 0,
11+
},
12+
"rangeCount": 1,
13+
}
14+
`;
15+
16+
exports[`addFocusToSelection the range is not updated if rangeCount is 0 1`] = `
17+
Selection {
18+
"focusNode": null,
19+
"focusOffset": 0,
20+
"range": null,
21+
"rangeCount": 0,
22+
}
23+
`;
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @emails oncall+draft_js
8+
* @format
9+
*/
10+
11+
'use strict';
12+
13+
jest.disableAutomock();
14+
15+
const addFocusToSelection = require('setDraftEditorSelection')
16+
.addFocusToSelection;
17+
const getSampleSelectionMocksForTesting = require('getSampleSelectionMocksForTesting');
18+
19+
// Based on https://w3c.github.io/selection-api/#selection-interface
20+
class Selection {
21+
constructor({range}) {
22+
this.rangeCount = range ? 1 : 0;
23+
this.focusNode = (range && range.node) || null;
24+
this.focusOffset = (range && range.startOffset) || 0;
25+
this.range = range || null;
26+
}
27+
28+
getRangeAt(idx) {
29+
if (idx !== 0 || this.rangeCount <= 0) {
30+
throw new Error('IndexSizeError');
31+
}
32+
return this.range;
33+
}
34+
35+
addRange(range) {
36+
this.range = range;
37+
this.rangeCount = 1;
38+
}
39+
}
40+
41+
// Based on https://dom.spec.whatwg.org/#concept-range
42+
class Range {
43+
constructor({startOffset, endOffset, node}) {
44+
this.startOffset = startOffset;
45+
this.endOffset = endOffset;
46+
this.node = node;
47+
}
48+
49+
setEnd(node, offset) {
50+
this.endOffset = offset;
51+
this.node = node;
52+
}
53+
54+
cloneRange() {
55+
return new Range({
56+
startOffset: this.startOffset,
57+
endOffset: this.endOffset,
58+
node: this.node,
59+
});
60+
}
61+
}
62+
63+
let editorState = null;
64+
let textNodes = null;
65+
66+
const resetRootNodeMocks = () => {
67+
({editorState, textNodes} = getSampleSelectionMocksForTesting());
68+
};
69+
70+
beforeEach(() => {
71+
resetRootNodeMocks();
72+
});
73+
74+
describe('addFocusToSelection', () => {
75+
test('sets a new focus on the selection if selection.extend is unsupported', () => {
76+
const range = new Range({
77+
startOffset: 0,
78+
endOffset: 0,
79+
node: textNodes[0],
80+
});
81+
const selection = new Selection({range});
82+
const storedFocusNode = selection.focusNode;
83+
const storedFocusOffset = 3;
84+
addFocusToSelection(
85+
selection,
86+
storedFocusNode,
87+
storedFocusOffset,
88+
editorState.getSelection(),
89+
);
90+
expect(selection).toMatchSnapshot();
91+
});
92+
93+
// If rangeCount is 0, selection.getRangeAt() will throw on various browsers
94+
test('the range is not updated if rangeCount is 0', () => {
95+
const selection = new Selection({});
96+
const storedFocusNode = selection.focusNode;
97+
const storedFocusOffset = 3;
98+
addFocusToSelection(
99+
selection,
100+
storedFocusNode,
101+
storedFocusOffset,
102+
editorState.getSelection(),
103+
);
104+
expect(selection).toMatchSnapshot();
105+
});
106+
});

src/component/selection/setDraftEditorSelection.js

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -306,9 +306,11 @@ function addFocusToSelection(
306306
// Additionally, clone the selection range. IE11 throws an
307307
// InvalidStateError when attempting to access selection properties
308308
// after the range is detached.
309-
const range = selection.getRangeAt(0);
310-
range.setEnd(node, offset);
311-
selection.addRange(range.cloneRange());
309+
if (selection.rangeCount > 0) {
310+
const range = selection.getRangeAt(0);
311+
range.setEnd(node, offset);
312+
selection.addRange(range.cloneRange());
313+
}
312314
}
313315
}
314316

@@ -333,4 +335,7 @@ function addPointToSelection(
333335
selection.addRange(range);
334336
}
335337

336-
module.exports = setDraftEditorSelection;
338+
module.exports = {
339+
setDraftEditorSelection,
340+
addFocusToSelection,
341+
};

0 commit comments

Comments
 (0)