diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0ed06e35..82774341 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,6 +12,8 @@
- Stop unnecessarily calling `onSave` in the editor’s `onBlur` ([#173](https://github.com/springload/draftail/issues/173)).
- Prevent crash when filtering pasted content whose last block is to be removed (e.g. unsupported image) ([#179](https://github.com/springload/draftail/issues/179)).
+- Prevent crash in `DraftUtils.getEntitySelection`, when the provided entity key isn't valid (undefined, missing) ([#168](https://github.com/springload/draftail/pull/168))
+- Fix entity removal and editing not doing anything when the selection is backwards (right to left) ([#168](https://github.com/springload/draftail/pull/168)).
### Changed
diff --git a/lib/api/DraftUtils.js b/lib/api/DraftUtils.js
index 22b2b9f6..d3a02c62 100644
--- a/lib/api/DraftUtils.js
+++ b/lib/api/DraftUtils.js
@@ -61,9 +61,15 @@ export default {
/**
* Creates a selection on a given entity in the currently selected block.
+ * Returns the current selection if no entity key is provided, or if the entity could not be found.
*/
getEntitySelection(editorState, entityKey) {
- const selectionState = editorState.getSelection();
+ const selection = editorState.getSelection();
+
+ if (!entityKey) {
+ return selection;
+ }
+
const block = this.getSelectedBlock(editorState);
let entityRange;
// https://github.com/jpuri/draftjs-utils/blob/e81c0ae19c3b0fdef7e0c1b70d924398956be126/js/inline.js#L111
@@ -77,9 +83,13 @@ export default {
},
);
- return selectionState.merge({
- anchorOffset: entityRange.start,
- focusOffset: entityRange.end,
+ if (!entityRange) {
+ return selection;
+ }
+
+ return selection.merge({
+ anchorOffset: selection.isBackward ? entityRange.end : entityRange.start,
+ focusOffset: selection.isBackward ? entityRange.start : entityRange.end,
});
},
diff --git a/lib/api/DraftUtils.test.js b/lib/api/DraftUtils.test.js
index 84b430d0..2350c13b 100644
--- a/lib/api/DraftUtils.test.js
+++ b/lib/api/DraftUtils.test.js
@@ -152,33 +152,71 @@ describe("DraftUtils", () => {
describe("#getEntitySelection", () => {
it("works", () => {
- const contentBlocks = convertFromHTML("
aaaaaaaaaa
");
- const contentState = ContentState.createFromBlockArray(contentBlocks);
- let editorState = EditorState.createWithContent(contentState);
- const updatedSelection = editorState.getSelection().merge({
+ const content = ContentState.createFromText("hello, world");
+ let editorState = EditorState.createWithContent(content);
+ const selection = editorState.getSelection().merge({
anchorOffset: 0,
- focusOffset: 5,
+ focusOffset: 4,
+ });
+
+ const contentStateWithEntity = content.createEntity("LINK", "MUTABLE", {
+ url: "www.testing.com",
});
- const contentStateWithEntity = contentState.createEntity(
- "LINK",
- "MUTABLE",
- { url: "www.testing.com" },
- );
const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
- editorState = RichUtils.toggleLink(
+ editorState = RichUtils.toggleLink(editorState, selection, entityKey);
+ editorState = EditorState.forceSelection(
editorState,
- updatedSelection,
- entityKey,
+ selection.merge({ focusOffset: 0 }),
);
- const entitySelection = DraftUtils.getEntitySelection(
+ expect(
+ DraftUtils.getEntitySelection(editorState, entityKey).toJS(),
+ ).toMatchObject({
+ anchorOffset: 0,
+ focusOffset: 4,
+ });
+ });
+
+ it("supports backwards selections (#168)", () => {
+ const content = ContentState.createFromText("hello, world");
+ let editorState = EditorState.createWithContent(content);
+ const selection = editorState.getSelection().merge({
+ anchorOffset: 5,
+ focusOffset: 0,
+ isBackward: true,
+ });
+ const contentStateWithEntity = content.createEntity("LINK", "MUTABLE", {
+ url: "www.testing.com",
+ });
+ const entityKey = contentStateWithEntity.getLastCreatedEntityKey();
+ editorState = RichUtils.toggleLink(editorState, selection, entityKey);
+ editorState = EditorState.forceSelection(
editorState,
- entityKey,
+ selection.merge({ anchorOffset: 0 }),
);
- expect(entitySelection.toJS()).toMatchObject({
- anchorOffset: 0,
- focusOffset: 5,
+ expect(
+ DraftUtils.getEntitySelection(editorState, entityKey).toJS(),
+ ).toMatchObject({
+ anchorOffset: 5,
+ focusOffset: 0,
+ isBackward: true,
});
});
+
+ it("entity not found should not change selection (#168)", () => {
+ const content = ContentState.createFromText("hello, world");
+ const editorState = EditorState.createWithContent(content);
+ expect(DraftUtils.getEntitySelection(editorState, "1")).toBe(
+ editorState.getSelection(),
+ );
+ });
+
+ it("missing entity key should not change selection (#168)", () => {
+ const content = ContentState.createFromText("hello, world");
+ const editorState = EditorState.createWithContent(content);
+ expect(DraftUtils.getEntitySelection(editorState, null)).toBe(
+ editorState.getSelection(),
+ );
+ });
});
describe("#updateBlockEntity", () => {