Skip to content
This repository was archived by the owner on Oct 11, 2022. It is now read-only.

Commit 721981e

Browse files
committed
merge master
2 parents de3a8d7 + 117ffd5 commit 721981e

File tree

11 files changed

+355
-78
lines changed

11 files changed

+355
-78
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "draft-js-markdown-plugin",
3-
"version": "1.2.0",
3+
"version": "1.3.0",
44
"description": "A DraftJS plugin for supporting Markdown syntax shortcuts, fork of draft-js-markdown-shortcuts-plugin",
55
"main": "lib/index.js",
66
"scripts": {

src/__test__/plugin.test.js

Lines changed: 151 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import sinon from "sinon";
21
import Draft, { EditorState, SelectionState, ContentBlock } from "draft-js";
32
import {
43
CheckableListItem,
@@ -99,10 +98,12 @@ describe("draft-js-markdown-plugin", () => {
9998
it("is loaded", () => {
10099
expect(typeof createMarkdownPlugin).toBe("function");
101100
});
101+
102102
it("initialize", () => {
103103
plugin.initialize(store);
104104
expect(plugin.store).toEqual(store);
105105
});
106+
106107
describe("handleReturn", () => {
107108
beforeEach(() => {
108109
subject = () =>
@@ -127,6 +128,78 @@ describe("draft-js-markdown-plugin", () => {
127128
expect(modifierSpy).not.toHaveBeenCalledTimes(1);
128129
expect(store.setEditorState).not.toHaveBeenCalled();
129130
});
131+
132+
it("does not handle if current entity is link", () => {
133+
currentRawContentState = {
134+
entityMap: {
135+
"0": {
136+
data: {
137+
href: "www.google.com",
138+
url: "http://www.google.com",
139+
},
140+
mutability: "MUTABLE",
141+
type: "LINK",
142+
},
143+
},
144+
blocks: [
145+
{
146+
key: "item1",
147+
text: "what **is** going on",
148+
type: "unstyled",
149+
depth: 0,
150+
inlineStyleRanges: [],
151+
entityRanges: [
152+
{
153+
offset: 0,
154+
key: 0,
155+
length: 20,
156+
},
157+
],
158+
data: {},
159+
},
160+
],
161+
};
162+
163+
currentSelectionState = currentEditorState.getSelection().merge({
164+
focusOffset: 19,
165+
anchorOffset: 19,
166+
});
167+
168+
currentEditorState = createEditorState(
169+
currentRawContentState,
170+
currentSelectionState
171+
);
172+
173+
expect(subject()).toBe("not-handled");
174+
});
175+
176+
it("resets curent inline style", () => {
177+
currentRawContentState = {
178+
entityMap: {},
179+
blocks: [
180+
{
181+
key: "item1",
182+
text: "item1",
183+
type: "unstyled",
184+
depth: 0,
185+
inlineStyleRanges: [{ offset: 0, length: 5, style: "BOLD" }],
186+
entityRanges: [],
187+
data: {},
188+
},
189+
],
190+
};
191+
192+
currentSelectionState = currentSelectionState.merge({
193+
focusOffset: 5,
194+
anchorOffset: 5,
195+
});
196+
197+
expect(subject()).toBe("handled");
198+
expect(store.setEditorState).toHaveBeenCalled();
199+
newEditorState = store.setEditorState.mock.calls[0][0];
200+
expect(newEditorState.getCurrentInlineStyle().size).toBe(0);
201+
});
202+
130203
it("leaves from list", () => {
131204
createMarkdownPlugin.__Rewire__("leaveList", modifierSpy); // eslint-disable-line no-underscore-dangle
132205
currentRawContentState = {
@@ -147,6 +220,38 @@ describe("draft-js-markdown-plugin", () => {
147220
expect(modifierSpy).toHaveBeenCalledTimes(1);
148221
expect(store.setEditorState).toHaveBeenCalledWith(newEditorState);
149222
});
223+
224+
it("adds list item and transforms markdown", () => {
225+
// createMarkdownPlugin.__Rewire__("leaveList", modifierSpy); // eslint-disable-line no-underscore-dangle
226+
currentRawContentState = {
227+
entityMap: {},
228+
blocks: [
229+
{
230+
key: "item1",
231+
text: "**some bold text**",
232+
type: "unordered-list-item",
233+
depth: 0,
234+
inlineStyleRanges: [],
235+
entityRanges: [],
236+
data: {},
237+
},
238+
],
239+
};
240+
currentSelectionState = currentSelectionState.merge({
241+
focusOffset: 18,
242+
anchorOffset: 18,
243+
});
244+
expect(subject()).toBe("handled");
245+
// expect(modifierSpy).toHaveBeenCalledTimes(1);
246+
expect(store.setEditorState).toHaveBeenCalledTimes(1);
247+
newEditorState = store.setEditorState.mock.calls[0][0];
248+
const newRawContentState = Draft.convertToRaw(
249+
newEditorState.getCurrentContent()
250+
);
251+
expect(newRawContentState.blocks.length).toBe(2);
252+
expect(newEditorState.getCurrentInlineStyle().size).toBe(0);
253+
});
254+
150255
const testInsertNewBlock = type => () => {
151256
createMarkdownPlugin.__Rewire__("insertEmptyBlock", modifierSpy); // eslint-disable-line no-underscore-dangle
152257
currentRawContentState = {
@@ -375,6 +480,51 @@ describe("draft-js-markdown-plugin", () => {
375480
expect(subject()).toBe("not-handled");
376481
});
377482
});
483+
describe("current entity is a link", () => {
484+
it("returns not-handled", () => {
485+
currentRawContentState = {
486+
entityMap: {
487+
"0": {
488+
data: {
489+
href: "www.google.com",
490+
url: "http://www.google.com",
491+
},
492+
mutability: "MUTABLE",
493+
type: "LINK",
494+
},
495+
},
496+
blocks: [
497+
{
498+
key: "item1",
499+
text: "what **is** going on",
500+
type: "unstyled",
501+
depth: 0,
502+
inlineStyleRanges: [],
503+
entityRanges: [
504+
{
505+
offset: 0,
506+
key: 0,
507+
length: 20,
508+
},
509+
],
510+
data: {},
511+
},
512+
],
513+
};
514+
515+
currentSelectionState = currentEditorState.getSelection().merge({
516+
focusOffset: 19,
517+
anchorOffset: 19,
518+
});
519+
520+
currentEditorState = createEditorState(
521+
currentRawContentState,
522+
currentSelectionState
523+
);
524+
525+
expect(subject()).toBe("not-handled");
526+
});
527+
});
378528
});
379529
describe("handlePastedText", () => {
380530
let pastedText;

src/constants.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
export const CODE_BLOCK_REGEX = /^```([\w-]+)?\s*$/;
22

33
export const inlineMatchers = {
4-
BOLD: [/\*\*([^(?**)]+)\*\*/g, /__([^(?:__)]+)__/g],
5-
ITALIC: [/\*([^*]+)\*/g, /_([^_]+)_/g],
6-
CODE: [/`([^`]+)`/g],
7-
STRIKETHROUGH: [/~~([^(?:~~)]+)~~/g],
4+
BOLD: [/\*\*([^(?**)]+)\*\*$/g, /__([^(?:__)]+)__$/g],
5+
ITALIC: [/\*([^*]+)\*$/g, /_([^_]+)_$/g],
6+
CODE: [/`([^`]+)`$/g],
7+
STRIKETHROUGH: [/~~([^(?:~~)]+)~~$/g],
88
};
99

1010
export const CODE_BLOCK_TYPE = "code-block";

src/index.js

Lines changed: 60 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@ import {
66
CHECKABLE_LIST_ITEM,
77
} from "draft-js-checkable-list-item";
88

9-
import { Map } from "immutable";
10-
9+
import { Map, OrderedSet, is } from "immutable";
1110
import CodeBlock from "./components/Code";
11+
import {
12+
getDefaultKeyBinding,
13+
Modifier,
14+
EditorState,
15+
RichUtils,
16+
DefaultDraftInlineStyle,
17+
} from "draft-js";
1218
import adjustBlockDepth from "./modifiers/adjustBlockDepth";
1319
import handleBlockType from "./modifiers/handleBlockType";
1420
import handleInlineStyle from "./modifiers/handleInlineStyle";
1521
import handleNewCodeBlock from "./modifiers/handleNewCodeBlock";
22+
import resetInlineStyle from "./modifiers/resetInlineStyle";
1623
import insertEmptyBlock from "./modifiers/insertEmptyBlock";
1724
import handleLink from "./modifiers/handleLink";
1825
import handleImage from "./modifiers/handleImage";
@@ -55,6 +62,16 @@ const defaultRenderSelect = ({ options, onChange, selectedValue }) => (
5562
</select>
5663
);
5764

65+
function inLink(editorState) {
66+
const selection = editorState.getSelection();
67+
const contentState = editorState.getCurrentContent();
68+
const block = contentState.getBlockForKey(selection.getAnchorKey());
69+
const entityKey = block.getEntityAt(selection.getFocusOffset());
70+
return (
71+
entityKey != null && contentState.getEntity(entityKey).getType() === "LINK"
72+
);
73+
}
74+
5875
function inCodeBlock(editorState) {
5976
const startKey = editorState.getSelection().getStartKey();
6077
if (startKey) {
@@ -90,9 +107,11 @@ function checkReturnForState(editorState, ev) {
90107
const currentBlock = contentState.getBlockForKey(key);
91108
const type = currentBlock.getType();
92109
const text = currentBlock.getText();
110+
93111
if (/-list-item$/.test(type) && text === "") {
94112
newEditorState = leaveList(editorState);
95113
}
114+
96115
if (
97116
newEditorState === editorState &&
98117
(ev.ctrlKey ||
@@ -102,7 +121,15 @@ function checkReturnForState(editorState, ev) {
102121
/^header-/.test(type) ||
103122
type === "blockquote")
104123
) {
105-
newEditorState = insertEmptyBlock(editorState);
124+
// transform markdown (if we aren't in a codeblock that is)
125+
if (!inCodeBlock(editorState)) {
126+
newEditorState = checkCharacterForState(newEditorState, "\n");
127+
}
128+
if (newEditorState === editorState) {
129+
newEditorState = insertEmptyBlock(newEditorState);
130+
} else {
131+
newEditorState = RichUtils.toggleBlockType(newEditorState, type);
132+
}
106133
}
107134
if (
108135
newEditorState === editorState &&
@@ -208,18 +235,38 @@ const createMarkdownPlugin = (_config = {}) => {
208235
return "not-handled";
209236
},
210237
handleReturn(ev, editorState, { setEditorState }) {
238+
if (inLink(editorState)) return "not-handled";
239+
211240
let newEditorState = checkReturnForState(editorState, ev);
212-
if (editorState !== newEditorState) {
241+
const selection = newEditorState.getSelection();
242+
243+
// exit code blocks
244+
if (
245+
inCodeBlock(editorState) &&
246+
!is(editorState.getImmutable(), newEditorState.getImmutable())
247+
) {
213248
setEditorState(newEditorState);
214249
return "handled";
215250
}
216251

217-
// If we're in a code block don't add markdown to it
218-
if (inCodeBlock(editorState)) return "not-handled";
252+
newEditorState = checkCharacterForState(newEditorState, "\n");
253+
let content = newEditorState.getCurrentContent();
219254

220-
newEditorState = checkCharacterForState(editorState, "\n");
221-
if (editorState !== newEditorState) {
222-
setEditorState(newEditorState);
255+
// if there are actually no changes but the editorState has a
256+
// current inline style we want to split the block
257+
if (
258+
is(editorState.getImmutable(), newEditorState.getImmutable()) &&
259+
editorState.getCurrentInlineStyle().size > 0
260+
) {
261+
content = Modifier.splitBlock(content, selection);
262+
}
263+
264+
newEditorState = resetInlineStyle(newEditorState);
265+
266+
if (!is(editorState.getImmutable(), newEditorState.getImmutable())) {
267+
setEditorState(
268+
EditorState.push(newEditorState, content, "split-block")
269+
);
223270
return "handled";
224271
}
225272

@@ -230,9 +277,12 @@ const createMarkdownPlugin = (_config = {}) => {
230277
return "not-handled";
231278
}
232279

233-
// If we're in a code block don't add markdown to it
280+
// If we're in a code block - don't transform markdown
234281
if (inCodeBlock(editorState)) return "not-handled";
235282

283+
// If we're in a link - don't transform markdown
284+
if (inLink(editorState)) return "not-handled";
285+
236286
const newEditorState = checkCharacterForState(editorState, character);
237287
if (editorState !== newEditorState) {
238288
setEditorState(newEditorState);

src/modifiers/__test__/changeCurrentInlineStyle-test.js

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -70,18 +70,12 @@ describe("changeCurrentInlineStyle", () => {
7070
"\n"
7171
);
7272
expect(newEditorState).not.toEqual(editorState);
73-
expect(Draft.convertToRaw(newEditorState.getCurrentContent())).toEqual(
74-
rawContentState(
75-
"foo bar\n baz",
76-
[
77-
{
78-
length: 3,
79-
offset: 4,
80-
style: "CODE",
81-
},
82-
],
83-
"CODE"
84-
)
85-
);
73+
const contentState = Draft.convertToRaw(newEditorState.getCurrentContent());
74+
expect(contentState.blocks.length).toBe(2);
75+
expect(contentState.blocks[0].text).toEqual("foo bar");
76+
expect(contentState.blocks[0].inlineStyleRanges).toEqual([
77+
{ offset: 4, length: 3, style: "CODE" },
78+
]);
79+
expect(contentState.blocks[1].text).toEqual(" baz");
8680
});
8781
});

0 commit comments

Comments
 (0)