Skip to content
This repository has been archived by the owner on Feb 6, 2023. It is now read-only.

Commit

Permalink
Added tests and fixed IE IndexSizeError trying to get a range from a …
Browse files Browse the repository at this point in the history
…selection when there is not one (#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](#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: #2271

Differential Revision: D18807105

Pulled By: mrkev

fbshipit-source-id: 0e3b833b8a3267b9a5f17b262b6a0442b6ae5e3d
  • Loading branch information
LaurenWyatt610 authored and facebook-github-bot committed Dec 17, 2019
1 parent e20f79f commit aa55de2
Show file tree
Hide file tree
Showing 4 changed files with 140 additions and 5 deletions.
3 changes: 2 additions & 1 deletion src/component/contents/DraftEditorLeaf.react.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ const React = require('React');

const invariant = require('invariant');
const isHTMLBRElement = require('isHTMLBRElement');
const setDraftEditorSelection = require('setDraftEditorSelection');
const setDraftEditorSelection = require('setDraftEditorSelection')
.setDraftEditorSelection;

type Props = {
// The block that contains this leaf.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`addFocusToSelection sets a new focus on the selection if selection.extend is unsupported 1`] = `
Selection {
"focusNode": Washington,
"focusOffset": 0,
"range": Range {
"endOffset": 3,
"node": Washington,
"startOffset": 0,
},
"rangeCount": 1,
}
`;

exports[`addFocusToSelection the range is not updated if rangeCount is 0 1`] = `
Selection {
"focusNode": null,
"focusOffset": 0,
"range": null,
"rangeCount": 0,
}
`;
106 changes: 106 additions & 0 deletions src/component/selection/__tests__/setDraftEditorSelection-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @emails oncall+draft_js
* @format
*/

'use strict';

jest.disableAutomock();

const addFocusToSelection = require('setDraftEditorSelection')
.addFocusToSelection;
const getSampleSelectionMocksForTesting = require('getSampleSelectionMocksForTesting');

// Based on https://w3c.github.io/selection-api/#selection-interface
class Selection {
constructor({range}) {
this.rangeCount = range ? 1 : 0;
this.focusNode = (range && range.node) || null;
this.focusOffset = (range && range.startOffset) || 0;
this.range = range || null;
}

getRangeAt(idx) {
if (idx !== 0 || this.rangeCount <= 0) {
throw new Error('IndexSizeError');
}
return this.range;
}

addRange(range) {
this.range = range;
this.rangeCount = 1;
}
}

// Based on https://dom.spec.whatwg.org/#concept-range
class Range {
constructor({startOffset, endOffset, node}) {
this.startOffset = startOffset;
this.endOffset = endOffset;
this.node = node;
}

setEnd(node, offset) {
this.endOffset = offset;
this.node = node;
}

cloneRange() {
return new Range({
startOffset: this.startOffset,
endOffset: this.endOffset,
node: this.node,
});
}
}

let editorState = null;
let textNodes = null;

const resetRootNodeMocks = () => {
({editorState, textNodes} = getSampleSelectionMocksForTesting());
};

beforeEach(() => {
resetRootNodeMocks();
});

describe('addFocusToSelection', () => {
test('sets a new focus on the selection if selection.extend is unsupported', () => {
const range = new Range({
startOffset: 0,
endOffset: 0,
node: textNodes[0],
});
const selection = new Selection({range});
const storedFocusNode = selection.focusNode;
const storedFocusOffset = 3;
addFocusToSelection(
selection,
storedFocusNode,
storedFocusOffset,
editorState.getSelection(),
);
expect(selection).toMatchSnapshot();
});

// If rangeCount is 0, selection.getRangeAt() will throw on various browsers
test('the range is not updated if rangeCount is 0', () => {
const selection = new Selection({});
const storedFocusNode = selection.focusNode;
const storedFocusOffset = 3;
addFocusToSelection(
selection,
storedFocusNode,
storedFocusOffset,
editorState.getSelection(),
);
expect(selection).toMatchSnapshot();
});
});
13 changes: 9 additions & 4 deletions src/component/selection/setDraftEditorSelection.js
Original file line number Diff line number Diff line change
Expand Up @@ -306,9 +306,11 @@ function addFocusToSelection(
// Additionally, clone the selection range. IE11 throws an
// InvalidStateError when attempting to access selection properties
// after the range is detached.
const range = selection.getRangeAt(0);
range.setEnd(node, offset);
selection.addRange(range.cloneRange());
if (selection.rangeCount > 0) {
const range = selection.getRangeAt(0);
range.setEnd(node, offset);
selection.addRange(range.cloneRange());
}
}
}

Expand All @@ -333,4 +335,7 @@ function addPointToSelection(
selection.addRange(range);
}

module.exports = setDraftEditorSelection;
module.exports = {
setDraftEditorSelection,
addFocusToSelection,
};

0 comments on commit aa55de2

Please sign in to comment.