diff --git a/src/actions/editorialWorkflow.js b/src/actions/editorialWorkflow.js
index 7c1afdd3..98e48532 100644
--- a/src/actions/editorialWorkflow.js
+++ b/src/actions/editorialWorkflow.js
@@ -1,5 +1,6 @@
import uuid from 'uuid';
import { actions as notifActions } from 'redux-notifications';
+import { serializeValues } from '../lib/serializeEntryValues';
import { closeEntry } from './editor';
import { BEGIN, COMMIT, REVERT } from 'redux-optimist';
import { currentBackend } from '../backends/backend';
diff --git a/src/components/Widgets/Markdown/MarkdownControl/RawEditor/index.js b/src/components/Widgets/Markdown/MarkdownControl/RawEditor/index.js
index 1ef4ae0f..d19164b6 100644
--- a/src/components/Widgets/Markdown/MarkdownControl/RawEditor/index.js
+++ b/src/components/Widgets/Markdown/MarkdownControl/RawEditor/index.js
@@ -1,6 +1,6 @@
import React, { PropTypes } from 'react';
import { Editor as Slate, Plain } from 'slate';
-import { markdownToRemark, remarkToMarkdown } from '../../serializers';
+import { debounce } from 'lodash';
import Toolbar from '../Toolbar/Toolbar';
import { Sticky } from '../../../../UI/Sticky/Sticky';
import styles from './index.css';
@@ -8,14 +8,8 @@ import styles from './index.css';
export default class RawEditor extends React.Component {
constructor(props) {
super(props);
- /**
- * The value received is a Remark AST (MDAST), and must be stringified
- * to plain text before Slate's Plain serializer can convert it to the
- * Slate AST.
- */
- const value = remarkToMarkdown(this.props.value);
this.state = {
- editorState: Plain.deserialize(value || ''),
+ editorState: Plain.deserialize(this.props.value || ''),
};
}
@@ -27,15 +21,15 @@ export default class RawEditor extends React.Component {
this.setState({ editorState });
}
+ onChange = debounce(this.props.onChange, 250);
+
/**
* When the document value changes, serialize from Slate's AST back to plain
- * text (which is Markdown), and then deserialize from that to a Remark MDAST,
- * before passing up as the new value.
+ * text (which is Markdown) and pass that up as the new value.
*/
handleDocumentChange = (doc, editorState) => {
const value = Plain.serialize(editorState);
- const mdast = markdownToRemark(value);
- this.props.onChange(mdast);
+ this.onChange(value);
};
/**
@@ -79,5 +73,5 @@ export default class RawEditor extends React.Component {
RawEditor.propTypes = {
onChange: PropTypes.func.isRequired,
onMode: PropTypes.func.isRequired,
- value: PropTypes.object,
+ value: PropTypes.string,
};
diff --git a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/__tests__/parser.spec.js b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/__tests__/parser.spec.js
index 6700eacf..f549b366 100644
--- a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/__tests__/parser.spec.js
+++ b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/__tests__/parser.spec.js
@@ -1,8 +1,9 @@
import { fromJS } from 'immutable';
-import { markdownToRemark, remarkToSlate } from '../../../serializers';
+import { markdownToSlate } from '../../../serializers';
-// Temporary plugins test, uses preloaded plugins from ../parser
-// TODO: make the parser more testable
+const parser = markdownToSlate;
+
+// Temporary plugins test
const testPlugins = fromJS([
{
label: 'Image',
@@ -44,8 +45,6 @@ const testPlugins = fromJS([
},
]);
-const parser = markdown => remarkToSlate(markdownToRemark(markdown));
-
describe("Compile markdown to Slate Raw AST", () => {
it("should compile simple markdown", () => {
const value = `
diff --git a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js
index 33fbcd26..2fa9323a 100644
--- a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js
+++ b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/index.js
@@ -1,7 +1,7 @@
import React, { Component, PropTypes } from 'react';
-import { get, isEmpty } from 'lodash';
+import { get, isEmpty, debounce } from 'lodash';
import { Editor as Slate, Raw, Block, Text } from 'slate';
-import { slateToRemark, remarkToSlate, htmlToSlate } from '../../serializers';
+import { slateToMarkdown, markdownToSlate, htmlToSlate } from '../../serializers';
import registry from '../../../../../lib/registry';
import Toolbar from '../Toolbar/Toolbar';
import { Sticky } from '../../../../UI/Sticky/Sticky';
@@ -15,10 +15,10 @@ export default class Editor extends Component {
constructor(props) {
super(props);
const emptyBlock = Block.create({ kind: 'block', type: 'paragraph'});
- const emptyRaw = { nodes: [emptyBlock] };
- const mdast = this.props.value && remarkToSlate(this.props.value);
- const mdastHasNodes = !isEmpty(get(mdast, 'nodes'))
- const editorState = Raw.deserialize(mdastHasNodes ? mdast : emptyRaw, { terse: true });
+ const emptyRawDoc = { nodes: [emptyBlock] };
+ const rawDoc = this.props.value && markdownToSlate(this.props.value);
+ const rawDocHasNodes = !isEmpty(get(rawDoc, 'nodes'))
+ const editorState = Raw.deserialize(rawDocHasNodes ? rawDoc : emptyRawDoc, { terse: true });
this.state = {
editorState,
schema: {
@@ -43,11 +43,13 @@ export default class Editor extends Component {
return state.transform().insertFragment(doc).apply();
}
+ onChange = debounce(this.props.onChange, 250);
+
handleDocumentChange = (doc, editorState) => {
const raw = Raw.serialize(editorState, { terse: true });
const plugins = this.state.shortcodePlugins;
- const mdast = slateToRemark(raw, plugins);
- this.props.onChange(mdast);
+ const markdown = slateToMarkdown(raw, plugins);
+ this.onChange(markdown);
};
hasMark = type => this.state.editorState.marks.some(mark => mark.type === type);
@@ -211,5 +213,5 @@ Editor.propTypes = {
getAsset: PropTypes.func.isRequired,
onChange: PropTypes.func.isRequired,
onMode: PropTypes.func.isRequired,
- value: PropTypes.object,
+ value: PropTypes.string,
};
diff --git a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/plugins.js b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/plugins.js
index 569c06ca..efb7e029 100644
--- a/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/plugins.js
+++ b/src/components/Widgets/Markdown/MarkdownControl/VisualEditor/plugins.js
@@ -34,6 +34,21 @@ export const SoftBreakConfigured = SoftBreak(SoftBreakOpts);
export const ParagraphSoftBreakConfigured = SlateSoftBreak({ onlyIn: ['paragraph'], shift: true });
+const BreakToDefaultBlock = ({ onlyIn = [], defaultBlock = 'paragraph' }) => ({
+ onKeyDown(e, data, state) {
+ if (data.key != 'enter' || e.shiftKey == true || state.isExpanded) return;
+ if (onlyIn.includes(state.startBlock.type)) {
+ return state.transform().insertBlock(defaultBlock).apply();
+ }
+ }
+});
+
+const BreakToDefaultBlockOpts = {
+ onlyIn: ['heading-one', 'heading-two', 'heading-three', 'heading-four', 'heading-five', 'heading-six'],
+};
+
+export const BreakToDefaultBlockConfigured = BreakToDefaultBlock(BreakToDefaultBlockOpts);
+
const BackspaceCloseBlock = (options = {}) => ({
onKeyDown(e, data, state) {
if (data.key != 'backspace') return;
@@ -87,6 +102,7 @@ const plugins = [
SoftBreakConfigured,
ParagraphSoftBreakConfigured,
BackspaceCloseBlockConfigured,
+ BreakToDefaultBlockConfigured,
EditListConfigured,
EditTableConfigured,
];
diff --git a/src/components/Widgets/Markdown/MarkdownControl/index.js b/src/components/Widgets/Markdown/MarkdownControl/index.js
index 423bafd8..79b7e866 100644
--- a/src/components/Widgets/Markdown/MarkdownControl/index.js
+++ b/src/components/Widgets/Markdown/MarkdownControl/index.js
@@ -7,24 +7,13 @@ import { StickyContainer } from '../../../UI/Sticky/Sticky';
const MODE_STORAGE_KEY = 'cms.md-mode';
-/**
- * The markdown field value is persisted as a markdown string, but stringifying
- * on every keystroke is a big perf hit, so we'll register functions to perform
- * those actions only when necessary, such as after loading and before
- * persisting.
- */
-registry.registerWidgetValueSerializer('markdown', {
- serialize: remarkToMarkdown,
- deserialize: markdownToRemark,
-});
-
export default class MarkdownControl extends React.Component {
static propTypes = {
onChange: PropTypes.func.isRequired,
onAddAsset: PropTypes.func.isRequired,
onRemoveAsset: PropTypes.func.isRequired,
getAsset: PropTypes.func.isRequired,
- value: PropTypes.object,
+ value: PropTypes.string,
};
constructor(props) {
diff --git a/src/components/Widgets/Markdown/MarkdownPreview/__tests__/renderer.spec.js b/src/components/Widgets/Markdown/MarkdownPreview/__tests__/renderer.spec.js
index 02bb94c2..b4d7c0bf 100644
--- a/src/components/Widgets/Markdown/MarkdownPreview/__tests__/renderer.spec.js
+++ b/src/components/Widgets/Markdown/MarkdownPreview/__tests__/renderer.spec.js
@@ -4,7 +4,9 @@ import React from 'react';
import { shallow } from 'enzyme';
import { padStart } from 'lodash';
import MarkdownPreview from '../index';
-import { markdownToRemark } from '../../serializers';
+import { markdownToHtml } from '../../serializers';
+
+const parser = markdownToHtml;
describe('Markdown Preview renderer', () => {
describe('Markdown rendering', () => {
@@ -36,7 +38,7 @@ Text with **bold** & _em_ elements
###### H6
`;
- const component = shallow(
Paragraph with inline element
'; - const component = shallow(