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

Commit 6322893

Browse files
authored
Merge pull request #43 from withspectrum/whitelist-features
Whitelist markdown features via `config.features`
2 parents 7002ed8 + 4b7a56d commit 6322893

File tree

9 files changed

+254
-68
lines changed

9 files changed

+254
-68
lines changed

README.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,64 @@ const languages = {
6969
const markdownPlugin = createMarkdownPlugin({ languages })
7070
```
7171

72+
### `features`
73+
A list of enabled features, by default all features are turned on.
74+
75+
```js
76+
features = {
77+
block: Array<string>,
78+
inline: Array<string>,
79+
}
80+
```
81+
82+
````
83+
#### Example
84+
85+
```
86+
// this will only enable BOLD for inline and CODE
87+
// as well as header-one for blocks
88+
const features = {
89+
inline: ['BOLD'],
90+
block: ['CODE', 'header-one'],
91+
}
92+
const plugin = createMarkdownPlugin({ features })
93+
```
94+
95+
*Available Inline features*:
96+
97+
```js
98+
[
99+
'BOLD',
100+
'ITALIC',
101+
'CODE',
102+
'STRIKETHROUGH',
103+
'LINK',
104+
'IMAGE'
105+
]
106+
```
107+
108+
*Available Block features*:
109+
110+
```js
111+
import { CHECKABLE_LIST_ITEM } from "draft-js-checkable-list-item"
112+
[
113+
'CODE',
114+
'header-one',
115+
'header-two',
116+
'header-three',
117+
'header-four',
118+
'header-five',
119+
'header-six',
120+
'ordered-list-item',
121+
'unordered-list-item',
122+
// CHECKABLE_LIST_ITEM is a constant from 'draft-js-checkable-list-item'
123+
// see import statementabove
124+
CHECKABLE_LIST_ITEM,
125+
'blockquote',
126+
]
127+
```
128+
129+
72130
## Usage
73131
74132
```js

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@
9595
"peerDependencies": {
9696
"draft-js-plugins-editor": "~2.0.0-rc.1 || 2.0.0-rc2 || 2.0.0-rc1 || 2.0.0-beta12",
9797
"react": "^15.0.0",
98-
"react-dom": "^15.0.0",
99-
"react-portal": "^4.1.4"
98+
"react-dom": "^15.0.0"
10099
},
101100
"contributors": [
102101
"Atsushi Nagase <a@ngs.io>"

src/__test__/plugin.test.js

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import {
44
CheckableListItemUtils,
55
} from "draft-js-checkable-list-item";
66

7+
import { defaultInlineWhitelist, defaultBlockWhitelist } from "../constants";
8+
79
import { Map, List } from "immutable";
810
import createMarkdownPlugin from "../";
911

@@ -451,12 +453,7 @@ describe("draft-js-markdown-plugin", () => {
451453
],
452454
};
453455
});
454-
[
455-
"handleBlockType",
456-
"handleImage",
457-
"handleLink",
458-
"handleInlineStyle",
459-
].forEach(modifier => {
456+
["handleImage", "handleLink"].forEach(modifier => {
460457
describe(modifier, () => {
461458
beforeEach(() => {
462459
createMarkdownPlugin.__Rewire__(modifier, modifierSpy); // eslint-disable-line no-underscore-dangle
@@ -467,6 +464,36 @@ describe("draft-js-markdown-plugin", () => {
467464
});
468465
});
469466
});
467+
["handleBlockType"].forEach(modifier => {
468+
describe(modifier, () => {
469+
beforeEach(() => {
470+
createMarkdownPlugin.__Rewire__(modifier, modifierSpy); // eslint-disable-line no-underscore-dangle
471+
});
472+
it("returns handled", () => {
473+
expect(subject()).toBe("handled");
474+
expect(modifierSpy).toHaveBeenCalledWith(
475+
defaultBlockWhitelist,
476+
currentEditorState,
477+
" "
478+
);
479+
});
480+
});
481+
});
482+
["handleInlineStyle"].forEach(modifier => {
483+
describe(modifier, () => {
484+
beforeEach(() => {
485+
createMarkdownPlugin.__Rewire__(modifier, modifierSpy); // eslint-disable-line no-underscore-dangle
486+
});
487+
it("returns handled", () => {
488+
expect(subject()).toBe("handled");
489+
expect(modifierSpy).toHaveBeenCalledWith(
490+
defaultInlineWhitelist,
491+
currentEditorState,
492+
" "
493+
);
494+
});
495+
});
496+
});
470497
describe("character is not a space", () => {
471498
beforeEach(() => {
472499
character = "x";

src/constants.js

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { CHECKABLE_LIST_ITEM } from "draft-js-checkable-list-item";
2+
13
export const CODE_BLOCK_REGEX = /^```([\w-]+)?\s*$/;
24

35
export const inlineMatchers = {
@@ -8,3 +10,26 @@ export const inlineMatchers = {
810
};
911

1012
export const CODE_BLOCK_TYPE = "code-block";
13+
14+
export const defaultInlineWhitelist = [
15+
"BOLD",
16+
"ITALIC",
17+
"CODE",
18+
"STRIKETHROUGH",
19+
"LINK",
20+
"IMAGE",
21+
];
22+
23+
export const defaultBlockWhitelist = [
24+
"CODE",
25+
"header-one",
26+
"header-two",
27+
"header-three",
28+
"header-four",
29+
"header-five",
30+
"header-six",
31+
"ordered-list-item",
32+
"unordered-list-item",
33+
CHECKABLE_LIST_ITEM,
34+
"blockquote",
35+
];

src/index.js

Lines changed: 53 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,12 @@ import changeCurrentBlockType from "./modifiers/changeCurrentBlockType";
2929
import createLinkDecorator from "./decorators/link";
3030
import createImageDecorator from "./decorators/image";
3131
import { replaceText } from "./utils";
32-
import { CODE_BLOCK_REGEX, CODE_BLOCK_TYPE } from "./constants";
32+
import {
33+
CODE_BLOCK_REGEX,
34+
CODE_BLOCK_TYPE,
35+
defaultInlineWhitelist,
36+
defaultBlockWhitelist,
37+
} from "./constants";
3338

3439
const defaultLanguages = {
3540
bash: "Bash",
@@ -85,21 +90,35 @@ function inCodeBlock(editorState) {
8590
return false;
8691
}
8792

88-
function checkCharacterForState(editorState, character) {
89-
let newEditorState = handleBlockType(editorState, character);
90-
if (editorState === newEditorState) {
93+
function checkCharacterForState(config, editorState, character) {
94+
let newEditorState = handleBlockType(
95+
config.features.block,
96+
editorState,
97+
character
98+
);
99+
if (
100+
editorState === newEditorState &&
101+
config.features.inline.includes("IMAGE")
102+
) {
91103
newEditorState = handleImage(editorState, character);
92104
}
93-
if (editorState === newEditorState) {
105+
if (
106+
editorState === newEditorState &&
107+
config.features.inline.includes("LINK")
108+
) {
94109
newEditorState = handleLink(editorState, character);
95110
}
96111
if (editorState === newEditorState) {
97-
newEditorState = handleInlineStyle(editorState, character);
112+
newEditorState = handleInlineStyle(
113+
config.features.inline,
114+
editorState,
115+
character
116+
);
98117
}
99118
return newEditorState;
100119
}
101120

102-
function checkReturnForState(editorState, ev) {
121+
function checkReturnForState(config, editorState, ev) {
103122
let newEditorState = editorState;
104123
const contentState = editorState.getCurrentContent();
105124
const selection = editorState.getSelection();
@@ -123,7 +142,7 @@ function checkReturnForState(editorState, ev) {
123142
) {
124143
// transform markdown (if we aren't in a codeblock that is)
125144
if (!inCodeBlock(editorState)) {
126-
newEditorState = checkCharacterForState(newEditorState, "\n");
145+
newEditorState = checkCharacterForState(config, newEditorState, "\n");
127146
}
128147
if (newEditorState === editorState) {
129148
newEditorState = insertEmptyBlock(newEditorState);
@@ -134,6 +153,7 @@ function checkReturnForState(editorState, ev) {
134153
if (
135154
newEditorState === editorState &&
136155
type !== "code-block" &&
156+
config.features.block.includes("CODE") &&
137157
CODE_BLOCK_REGEX.test(text)
138158
) {
139159
newEditorState = handleNewCodeBlock(editorState);
@@ -157,6 +177,10 @@ function checkReturnForState(editorState, ev) {
157177
const defaultConfig = {
158178
renderLanguageSelect: defaultRenderSelect,
159179
languages: defaultLanguages,
180+
features: {
181+
inline: defaultInlineWhitelist,
182+
block: defaultBlockWhitelist,
183+
},
160184
};
161185

162186
const createMarkdownPlugin = (_config = {}) => {
@@ -165,6 +189,10 @@ const createMarkdownPlugin = (_config = {}) => {
165189
const config = {
166190
...defaultConfig,
167191
..._config,
192+
features: {
193+
...defaultConfig.features,
194+
..._config.features,
195+
},
168196
};
169197

170198
return {
@@ -237,7 +265,7 @@ const createMarkdownPlugin = (_config = {}) => {
237265
handleReturn(ev, editorState, { setEditorState }) {
238266
if (inLink(editorState)) return "not-handled";
239267

240-
let newEditorState = checkReturnForState(editorState, ev);
268+
let newEditorState = checkReturnForState(config, editorState, ev);
241269
const selection = newEditorState.getSelection();
242270

243271
// exit code blocks
@@ -249,7 +277,7 @@ const createMarkdownPlugin = (_config = {}) => {
249277
return "handled";
250278
}
251279

252-
newEditorState = checkCharacterForState(newEditorState, "\n");
280+
newEditorState = checkCharacterForState(config, newEditorState, "\n");
253281
let content = newEditorState.getCurrentContent();
254282

255283
// if there are actually no changes but the editorState has a
@@ -283,7 +311,11 @@ const createMarkdownPlugin = (_config = {}) => {
283311
// If we're in a link - don't transform markdown
284312
if (inLink(editorState)) return "not-handled";
285313

286-
const newEditorState = checkCharacterForState(editorState, character);
314+
const newEditorState = checkCharacterForState(
315+
config,
316+
editorState,
317+
character
318+
);
287319
if (editorState !== newEditorState) {
288320
setEditorState(newEditorState);
289321
return "handled";
@@ -334,11 +366,19 @@ const createMarkdownPlugin = (_config = {}) => {
334366
newEditorState,
335367
buffer.join("") + text[i]
336368
);
337-
newEditorState = checkCharacterForState(newEditorState, text[i]);
369+
newEditorState = checkCharacterForState(
370+
config,
371+
newEditorState,
372+
text[i]
373+
);
338374
buffer = [];
339375
} else if (text[i].charCodeAt(0) === 10) {
340376
newEditorState = replaceText(newEditorState, buffer.join(""));
341-
const tmpEditorState = checkReturnForState(newEditorState, {});
377+
const tmpEditorState = checkReturnForState(
378+
config,
379+
newEditorState,
380+
{}
381+
);
342382
if (newEditorState === tmpEditorState) {
343383
newEditorState = insertEmptyBlock(tmpEditorState);
344384
} else {

src/modifiers/__test__/handleBlockType-test.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import Draft, { EditorState, SelectionState } from "draft-js";
22
import handleBlockType from "../handleBlockType";
3+
import { defaultBlockWhitelist } from "../../constants";
34

45
describe("handleBlockType", () => {
56
describe("no markup", () => {
@@ -31,7 +32,11 @@ describe("handleBlockType", () => {
3132
selection
3233
);
3334
it("does not convert block type", () => {
34-
const newEditorState = handleBlockType(editorState, " ");
35+
const newEditorState = handleBlockType(
36+
defaultBlockWhitelist,
37+
editorState,
38+
" "
39+
);
3540
expect(newEditorState).toEqual(editorState);
3641
expect(Draft.convertToRaw(newEditorState.getCurrentContent())).toEqual(
3742
rawContentState
@@ -69,7 +74,11 @@ describe("handleBlockType", () => {
6974
);
7075

7176
it("does not convert block type", () => {
72-
const newEditorState = handleBlockType(editorState, " ");
77+
const newEditorState = handleBlockType(
78+
defaultBlockWhitelist,
79+
editorState,
80+
" "
81+
);
7382
expect(newEditorState).toEqual(editorState);
7483
expect(Draft.convertToRaw(newEditorState.getCurrentContent())).toEqual(
7584
rawContentState
@@ -549,7 +558,11 @@ describe("handleBlockType", () => {
549558
selection
550559
);
551560
it("converts block type", () => {
552-
const newEditorState = handleBlockType(editorState, character);
561+
const newEditorState = handleBlockType(
562+
defaultBlockWhitelist,
563+
editorState,
564+
character
565+
);
553566
expect(newEditorState).not.toEqual(editorState);
554567
expect(Draft.convertToRaw(newEditorState.getCurrentContent())).toEqual(
555568
after

0 commit comments

Comments
 (0)