Repository: withspectrum/draft-js-markdown-plugin
Pull request #71 (2018-05-06 04:43:41)

custom entity
This PR adds the ability to change the entity type of `LINK` and `IMAGES` so that they can be configured to work with other plugins and libraries.

Related: #69 


Patch:
diff --git a/README.md b/README.md
index e6b0f3e..f95e8ff 100644
--- a/README.md
+++ b/README.md
@@ -57,6 +57,7 @@ const markdownPlugin = createMarkdownPlugin({ renderLanguageSelect })
 ```
 
 ### `languages`
+
 Dictionary for languages available to code block switcher
 
 #### Example:
@@ -70,6 +71,7 @@ const markdownPlugin = createMarkdownPlugin({ languages })
 ```
 
 ### `features`
+
 A list of enabled features, by default all features are turned on.
 
 ```js
@@ -79,7 +81,7 @@ features = {
 }
 ```
 
-#### Example
+#### Example:
 
 ```
 // this will only enable BOLD for inline and CODE
@@ -125,6 +127,30 @@ import { CHECKABLE_LIST_ITEM } from "draft-js-checkable-list-item"
 ]
 ```
 
+### `entityType`
+
+To interoperate this plugin with other DraftJS plugins, i.e. [`draft-js-plugins`](https://github.com/draft-js-plugins/draft-js-plugins), you might need to customize the `LINK` and `IMAGE` entity type created by `draft-js-markdown-plugin`.
+
+#### Example:
+
+```js
+import createMarkdownPlugin from "draft-js-markdown-plugin";
+import createFocusPlugin from "draft-js-focus-plugin";
+import createImagePlugin from "draft-js-image-plugin";
+
+const entityType = {
+  IMAGE: "IMAGE",
+};
+
+const focusPlugin = createFocusPlugin();
+const imagePlugin = createImagePlugin({
+  decorator: focusPlugin.decorator,
+});
+// For `draft-js-image-plugin` to work, the entity type of an image must be `IMAGE`.
+const markdownPlugin = createMarkdownPlugin({ entityType });
+
+const editorPlugins = [focusPlugin, imagePlugin, markdownPlugin];
+```
 
 ## Usage
 
diff --git a/src/constants.js b/src/constants.js
index b5991af..5c82a97 100644
--- a/src/constants.js
+++ b/src/constants.js
@@ -11,6 +11,11 @@ export const inlineMatchers = {
 
 export const CODE_BLOCK_TYPE = "code-block";
 
+export const ENTITY_TYPE = {
+  IMAGE: "IMG",
+  LINK: "LINK",
+};
+
 export const defaultInlineWhitelist = [
   "BOLD",
   "ITALIC",
diff --git a/src/decorators/image/imageStrategy.js b/src/decorators/image/imageStrategy.js
index 3c67551..d7115ac 100644
--- a/src/decorators/image/imageStrategy.js
+++ b/src/decorators/image/imageStrategy.js
@@ -1,10 +1,10 @@
-const createImageStrategy = () => {
+const createImageStrategy = ({ entityType }) => {
   const findImageEntities = (contentBlock, callback, contentState) => {
     contentBlock.findEntityRanges(character => {
       const entityKey = character.getEntity();
       return (
         entityKey !== null &&
-        contentState.getEntity(entityKey).getType() === "IMG"
+        contentState.getEntity(entityKey).getType() === entityType
       );
     }, callback);
   };
diff --git a/src/decorators/image/index.js b/src/decorators/image/index.js
index a8cf63a..6de06d3 100644
--- a/src/decorators/image/index.js
+++ b/src/decorators/image/index.js
@@ -1,8 +1,8 @@
 import createImageStrategy from "./imageStrategy";
 import Image from "../../components/Image";
 
-const createImageDecorator = () => ({
-  strategy: createImageStrategy(),
+const createImageDecorator = ({ entityType }) => ({
+  strategy: createImageStrategy({ entityType }),
   component: Image,
 });
 
diff --git a/src/decorators/link/index.js b/src/decorators/link/index.js
index dd18ceb..d10083c 100644
--- a/src/decorators/link/index.js
+++ b/src/decorators/link/index.js
@@ -1,8 +1,8 @@
 import createLinkStrategy from "./linkStrategy";
 import Link from "../../components/Link";
 
-const createLinkDecorator = () => ({
-  strategy: createLinkStrategy(),
+const createLinkDecorator = ({ entityType }) => ({
+  strategy: createLinkStrategy({ entityType }),
   component: Link,
 });
 
diff --git a/src/decorators/link/linkStrategy.js b/src/decorators/link/linkStrategy.js
index a5f6475..503f8ba 100644
--- a/src/decorators/link/linkStrategy.js
+++ b/src/decorators/link/linkStrategy.js
@@ -1,10 +1,10 @@
-const createLinkStrategy = () => {
+const createLinkStrategy = ({ entityType }) => {
   const findLinkEntities = (contentBlock, callback, contentState) => {
     contentBlock.findEntityRanges(character => {
       const entityKey = character.getEntity();
       return (
         entityKey !== null &&
-        contentState.getEntity(entityKey).getType() === "LINK"
+        contentState.getEntity(entityKey).getType() === entityType
       );
     }, callback);
   };
diff --git a/src/index.js b/src/index.js
index f76c09a..bf54751 100755
--- a/src/index.js
+++ b/src/index.js
@@ -33,6 +33,7 @@ import { replaceText } from "./utils";
 import {
   CODE_BLOCK_REGEX,
   CODE_BLOCK_TYPE,
+  ENTITY_TYPE,
   defaultInlineWhitelist,
   defaultBlockWhitelist,
 } from "./constants";
@@ -101,13 +102,17 @@ function checkCharacterForState(config, editorState, character) {
     editorState === newEditorState &&
     config.features.inline.includes("IMAGE")
   ) {
-    newEditorState = handleImage(editorState, character);
+    newEditorState = handleImage(
+      editorState,
+      character,
+      config.entityType.IMAGE
+    );
   }
   if (
     editorState === newEditorState &&
     config.features.inline.includes("LINK")
   ) {
-    newEditorState = handleLink(editorState, character);
+    newEditorState = handleLink(editorState, character, config.entityType.LINK);
   }
   if (
     newEditorState === editorState &&
@@ -204,6 +209,7 @@ const defaultConfig = {
     inline: defaultInlineWhitelist,
     block: defaultBlockWhitelist,
   },
+  entityType: ENTITY_TYPE,
 };
 
 const createMarkdownPlugin = (_config = {}) => {
@@ -216,6 +222,10 @@ const createMarkdownPlugin = (_config = {}) => {
       ...defaultConfig.features,
       ..._config.features,
     },
+    entityType: {
+      ...defaultConfig.entityType,
+      ..._config.entityType,
+    },
   };
 
   return {
@@ -226,7 +236,14 @@ const createMarkdownPlugin = (_config = {}) => {
         wrapper: <pre spellCheck="false" />,
       },
     }).merge(checkboxBlockRenderMap),
-    decorators: [createLinkDecorator(), createImageDecorator()],
+    decorators: [
+      createLinkDecorator({
+        entityType: config.entityType.LINK,
+      }),
+      createImageDecorator({
+        entityType: config.entityType.IMAGE,
+      }),
+    ],
     initialize({ setEditorState, getEditorState }) {
       store.setEditorState = setEditorState;
       store.getEditorState = getEditorState;
diff --git a/src/modifiers/handleImage.js b/src/modifiers/handleImage.js
index 3de2bce..4dcc77d 100644
--- a/src/modifiers/handleImage.js
+++ b/src/modifiers/handleImage.js
@@ -1,6 +1,6 @@
 import insertImage from "./insertImage";
 
-const handleImage = (editorState, character) => {
+const handleImage = (editorState, character, entityType) => {
   const re = /!\[([^\]]*)]\(([^)"]+)(?: "([^"]+)")?\)/g;
   const key = editorState.getSelection().getStartKey();
   const text = editorState
@@ -13,7 +13,7 @@ const handleImage = (editorState, character) => {
   do {
     matchArr = re.exec(line);
     if (matchArr) {
-      newEditorState = insertImage(newEditorState, matchArr);
+      newEditorState = insertImage(newEditorState, matchArr, entityType);
     }
   } while (matchArr);
   return newEditorState;
diff --git a/src/modifiers/handleLink.js b/src/modifiers/handleLink.js
index 09cd748..f43ea9d 100644
--- a/src/modifiers/handleLink.js
+++ b/src/modifiers/handleLink.js
@@ -1,6 +1,6 @@
 import insertLink from "./insertLink";
 
-const handleLink = (editorState, character) => {
+const handleLink = (editorState, character, entityType) => {
   const re = /\[([^\]]+)]\(([^)"]+)(?: "([^"]+)")?\)/g;
   const key = editorState.getSelection().getStartKey();
   const text = editorState
@@ -13,7 +13,7 @@ const handleLink = (editorState, character) => {
   do {
     matchArr = re.exec(line);
     if (matchArr) {
-      newEditorState = insertLink(newEditorState, matchArr);
+      newEditorState = insertLink(newEditorState, matchArr, entityType);
     }
   } while (matchArr);
   return newEditorState;
diff --git a/src/modifiers/insertImage.js b/src/modifiers/insertImage.js
index 65f76b3..5c2f95c 100644
--- a/src/modifiers/insertImage.js
+++ b/src/modifiers/insertImage.js
@@ -1,6 +1,6 @@
 import { EditorState, RichUtils, SelectionState, Modifier } from "draft-js";
 
-const insertImage = (editorState, matchArr) => {
+const insertImage = (editorState, matchArr, entityType) => {
   const currentContent = editorState.getCurrentContent();
   const selection = editorState.getSelection();
   const key = selection.getStartKey();
@@ -11,7 +11,7 @@ const insertImage = (editorState, matchArr) => {
     anchorOffset: index,
     focusOffset,
   });
-  const nextContent = currentContent.createEntity("IMG", "IMMUTABLE", {
+  const nextContent = currentContent.createEntity(entityType, "IMMUTABLE", {
     alt,
     src,
     title,
diff --git a/src/modifiers/insertLink.js b/src/modifiers/insertLink.js
index a45fdb4..23faee0 100644
--- a/src/modifiers/insertLink.js
+++ b/src/modifiers/insertLink.js
@@ -1,6 +1,6 @@
 import { EditorState, RichUtils, SelectionState, Modifier } from "draft-js";
 
-const insertLink = (editorState, matchArr) => {
+const insertLink = (editorState, matchArr, entityType) => {
   const currentContent = editorState.getCurrentContent();
   const selection = editorState.getSelection();
   const key = selection.getStartKey();
@@ -11,7 +11,7 @@ const insertLink = (editorState, matchArr) => {
     anchorOffset: index,
     focusOffset,
   });
-  const nextContent = currentContent.createEntity("LINK", "MUTABLE", {
+  const nextContent = currentContent.createEntity(entityType, "MUTABLE", {
     href,
     title,
     url: href,


Test patch:
diff --git a/src/__test__/plugin.test.js b/src/__test__/plugin.test.js
index 309e1fb..a8a8055 100755
--- a/src/__test__/plugin.test.js
+++ b/src/__test__/plugin.test.js
@@ -4,7 +4,11 @@ import {
   CheckableListItemUtils,
 } from "draft-js-checkable-list-item";
 
-import { defaultInlineWhitelist, defaultBlockWhitelist } from "../constants";
+import {
+  defaultInlineWhitelist,
+  defaultBlockWhitelist,
+  ENTITY_TYPE,
+} from "../constants";
 
 import { Map, List } from "immutable";
 import createMarkdownPlugin from "../";
@@ -514,14 +518,33 @@ describe("draft-js-markdown-plugin", () => {
             ],
           };
         });
-        ["handleImage", "handleLink"].forEach(modifier => {
+        ["handleImage"].forEach(modifier => {
+          describe(modifier, () => {
+            beforeEach(() => {
+              createMarkdownPlugin.__Rewire__(modifier, modifierSpy); // eslint-disable-line no-underscore-dangle
+            });
+            it("returns handled", () => {
+              expect(subject()).toBe("handled");
+              expect(modifierSpy).toHaveBeenCalledWith(
+                currentEditorState,
+                " ",
+                ENTITY_TYPE.IMAGE
+              );
+            });
+          });
+        });
+        ["handleLink"].forEach(modifier => {
           describe(modifier, () => {
             beforeEach(() => {
               createMarkdownPlugin.__Rewire__(modifier, modifierSpy); // eslint-disable-line no-underscore-dangle
             });
             it("returns handled", () => {
               expect(subject()).toBe("handled");
-              expect(modifierSpy).toHaveBeenCalledWith(currentEditorState, " ");
+              expect(modifierSpy).toHaveBeenCalledWith(
+                currentEditorState,
+                " ",
+                ENTITY_TYPE.LINK
+              );
             });
           });
         });
diff --git a/src/decorators/image/__test__/imageStrategy-test.js b/src/decorators/image/__test__/imageStrategy-test.js
index d30aa9a..7587593 100755
--- a/src/decorators/image/__test__/imageStrategy-test.js
+++ b/src/decorators/image/__test__/imageStrategy-test.js
@@ -1,4 +1,5 @@
 import Draft from "draft-js";
+import { ENTITY_TYPE } from "../../../constants";
 import createImageStrategy from "../imageStrategy";
 
 describe("imageStrategy", () => {
@@ -34,7 +35,9 @@ describe("imageStrategy", () => {
   });
   it("callbacks range", () => {
     const block = contentState.getBlockForKey("dtehj");
-    const strategy = createImageStrategy();
+    const strategy = createImageStrategy({
+      entityType: ENTITY_TYPE.IMAGE,
+    });
     const cb = jest.fn();
     expect(typeof block).toBe("object");
     strategy(block, cb, contentState);
diff --git a/src/decorators/link/__test__/linkStrategy-test.js b/src/decorators/link/__test__/linkStrategy-test.js
index 2de2c60..a27d8a7 100755
--- a/src/decorators/link/__test__/linkStrategy-test.js
+++ b/src/decorators/link/__test__/linkStrategy-test.js
@@ -1,4 +1,5 @@
 import Draft from "draft-js";
+import { ENTITY_TYPE } from "../../../constants";
 import createLinkStrategy from "../linkStrategy";
 
 describe("linkStrategy", () => {
@@ -33,7 +34,9 @@ describe("linkStrategy", () => {
   });
   it("callbacks range", () => {
     const block = contentState.getBlockForKey("dtehj");
-    const strategy = createLinkStrategy();
+    const strategy = createLinkStrategy({
+      entityType: ENTITY_TYPE.LINK,
+    });
     const cb = jest.fn();
     expect(typeof block).toBe("object");
     strategy(block, cb, contentState);
diff --git a/src/modifiers/__test__/handleImage-test.js b/src/modifiers/__test__/handleImage-test.js
index 3b8cdb1..a9d44e2 100644
--- a/src/modifiers/__test__/handleImage-test.js
+++ b/src/modifiers/__test__/handleImage-test.js
@@ -1,4 +1,5 @@
 import Draft, { EditorState, SelectionState } from "draft-js";
+import { ENTITY_TYPE } from "../../constants";
 import handleImage from "../handleImage";
 
 describe("handleImage", () => {
@@ -87,7 +88,7 @@ describe("handleImage", () => {
     describe(condition, () => {
       it("returns new editor state", () => {
         const editorState = createEditorState(text);
-        const newEditorState = handleImage(editorState, " ");
+        const newEditorState = handleImage(editorState, " ", ENTITY_TYPE.IMAGE);
         expect(newEditorState).not.toEqual(editorState);
         expect(Draft.convertToRaw(newEditorState.getCurrentContent())).toEqual(
           afterRawContentState
@@ -99,7 +100,7 @@ describe("handleImage", () => {
   describe("if does not match", () => {
     it("returns old editor state", () => {
       const editorState = createEditorState("yo");
-      const newEditorState = handleImage(editorState, " ");
+      const newEditorState = handleImage(editorState, " ", ENTITY_TYPE.LINK);
       expect(newEditorState).toEqual(editorState);
       expect(fakeInsertImage).not.toHaveBeenCalled();
     });
diff --git a/src/modifiers/__test__/handleLink-test.js b/src/modifiers/__test__/handleLink-test.js
index d758492..7f988ca 100644
--- a/src/modifiers/__test__/handleLink-test.js
+++ b/src/modifiers/__test__/handleLink-test.js
@@ -1,5 +1,6 @@
 import sinon from "sinon";
 import Draft, { EditorState, SelectionState } from "draft-js";
+import { ENTITY_TYPE } from "../../constants";
 import handleLink from "../handleLink";
 
 describe("handleLink", () => {
@@ -78,7 +79,7 @@ describe("handleLink", () => {
     describe(condition, () => {
       it("returns new editor state", () => {
         const editorState = createEditorState(text);
-        const newEditorState = handleLink(editorState, " ");
+        const newEditorState = handleLink(editorState, " ", ENTITY_TYPE.LINK);
         expect(newEditorState).not.toEqual(editorState);
         expect(Draft.convertToRaw(newEditorState.getCurrentContent())).toEqual(
           afterRawContentState
@@ -90,7 +91,7 @@ describe("handleLink", () => {
   describe("if does not match", () => {
     it("returns old editor state", () => {
       const editorState = createEditorState("yo");
-      const newEditorState = handleLink(editorState, " ");
+      const newEditorState = handleLink(editorState, " ", ENTITY_TYPE.LINK);
       expect(newEditorState).toEqual(editorState);
       expect(fakeInsertLink).not.toHaveBeenCalled();
     });
diff --git a/src/modifiers/__test__/insertImage-test.js b/src/modifiers/__test__/insertImage-test.js
index 801e679..c2967a0 100644
--- a/src/modifiers/__test__/insertImage-test.js
+++ b/src/modifiers/__test__/insertImage-test.js
@@ -1,4 +1,5 @@
 import Draft, { EditorState, SelectionState } from "draft-js";
+import { ENTITY_TYPE } from "../../constants";
 import insertImage from "../insertImage";
 
 describe("insertImage", () => {
@@ -71,7 +72,11 @@ describe("insertImage", () => {
     ];
     matchArr.index = 4;
     matchArr.input = text;
-    const newEditorState = insertImage(editorState, matchArr);
+    const newEditorState = insertImage(
+      editorState,
+      matchArr,
+      ENTITY_TYPE.IMAGE
+    );
     expect(newEditorState).not.toEqual(editorState);
     expect(Draft.convertToRaw(newEditorState.getCurrentContent())).toEqual(
       afterRawContentState
diff --git a/src/modifiers/__test__/insertLink-test.js b/src/modifiers/__test__/insertLink-test.js
index 2466184..1c991e1 100644
--- a/src/modifiers/__test__/insertLink-test.js
+++ b/src/modifiers/__test__/insertLink-test.js
@@ -1,4 +1,5 @@
 import Draft, { EditorState, SelectionState } from "draft-js";
+import { ENTITY_TYPE } from "../../constants";
 import insertLink from "../insertLink";
 
 describe("insertLink", () => {
@@ -70,7 +71,7 @@ describe("insertLink", () => {
     ];
     matchArr.index = 4;
     matchArr.input = text;
-    const newEditorState = insertLink(editorState, matchArr);
+    const newEditorState = insertLink(editorState, matchArr, ENTITY_TYPE.LINK);
     expect(newEditorState).not.toEqual(editorState);
     expect(Draft.convertToRaw(newEditorState.getCurrentContent())).toEqual(
       afterRawContentState