diff --git a/cypress/integration/markdown_widget_list_spec.js b/cypress/integration/markdown_widget_list_spec.js
index c88cbeae..a1378ac2 100644
--- a/cypress/integration/markdown_widget_list_spec.js
+++ b/cypress/integration/markdown_widget_list_spec.js
@@ -35,7 +35,7 @@ describe('Markdown widget', () => {
`);
});
- it('creates nested list when selection is collapsed in non-first block of list item', () => {
+ it('converts a list item to a paragraph block which is a sibling of the parent list', () => {
cy.clickUnorderedListButton()
.type('foo')
.enter()
@@ -44,44 +44,20 @@ describe('Markdown widget', () => {
+
`)
- .type('bar')
- .enter()
- .clickUnorderedListButton()
- .confirmMarkdownEditorContent(`
-
- `);
});
- it('converts empty nested list item to empty block in parent list item', () => {
+ it('converts empty nested list item to empty paragraph block in parent list item', () => {
cy.clickUnorderedListButton()
.type('foo')
.enter()
- .clickUnorderedListButton()
+ .tabkey()
.type('bar')
.enter()
- .clickUnorderedListButton()
+ .tabkey()
.confirmMarkdownEditorContent(`
-
@@ -129,10 +105,10 @@ describe('Markdown widget', () => {
cy.clickUnorderedListButton()
.type('foo')
.enter()
- .clickUnorderedListButton()
+ .tabkey()
.type('bar')
.enter()
- .clickUnorderedListButton()
+ .tabkey()
.type('baz')
.clickUnorderedListButton()
.confirmMarkdownEditorContent(`
@@ -228,7 +204,7 @@ describe('Markdown widget', () => {
.up()
.enter()
.type('qux')
- .clickUnorderedListButton()
+ .tabkey()
.confirmMarkdownEditorContent(`
-
@@ -250,7 +226,6 @@ describe('Markdown widget', () => {
.up()
.enter()
.type('quux')
- .clickUnorderedListButton()
.confirmMarkdownEditorContent(`
-
@@ -307,9 +282,9 @@ describe('Markdown widget', () => {
it('affects only selected list items', () => {
cy.clickUnorderedListButton()
.type('foo')
- .enter({ times: 2 })
+ .enter()
.type('bar')
- .enter({ times: 2 })
+ .enter()
.type('baz')
.setSelection('bar')
.clickUnorderedListButton()
@@ -352,14 +327,69 @@ describe('Markdown widget', () => {
`)
.setSelection('baz')
.clickUnorderedListButton()
+ .confirmMarkdownEditorContent(`
+
+ `)
.setCursorAfter('baz')
.enter()
- .clickUnorderedListButton()
+ .tabkey()
.type('qux')
+ .confirmMarkdownEditorContent(`
+
+ `)
.setSelection('baz')
.clickOrderedListButton()
+ .confirmMarkdownEditorContent(`
+
+ -
+
foo
+
+ -
+
bar
+
+ -
+
baz
+
+
+
+
+
+ `)
.setCursorAfter('qux')
- .enter({ times: 4 })
+ .enter({ times: 2 })
.clickUnorderedListButton()
.confirmMarkdownEditorContent(`
@@ -398,7 +428,7 @@ describe('Markdown widget', () => {
`);
});
- it('creates a new paragraph in a non-empty paragraph within a list item', () => {
+ it('creates a new list item in a non-empty list', () => {
cy.clickUnorderedListButton()
.type('foo')
.enter()
@@ -406,6 +436,8 @@ describe('Markdown widget', () => {
@@ -416,46 +448,21 @@ describe('Markdown widget', () => {
`);
});
- it('creates a new list item in an empty paragraph within a non-empty list item', () => {
+ it('creates a new default block below a list when hitting Enter twice on an empty list item of the list', () => {
cy.clickUnorderedListButton()
.type('foo')
.enter({ times: 2 })
- .confirmMarkdownEditorContent(`
-
- `)
- .type('bar')
- .enter()
- .confirmMarkdownEditorContent(`
-
- `);
- });
-
- it('creates a new block below list', () => {
- cy.clickUnorderedListButton()
- .type('foo')
- .enter({ times: 3 })
.confirmMarkdownEditorContent(`
-
@@ -476,24 +483,10 @@ describe('Markdown widget', () => {
`);
});
- it('removes empty block in non-empty list item', () => {
- cy.clickUnorderedListButton()
- .type('foo')
- .enter()
- .backspace()
- .confirmMarkdownEditorContent(`
-
- `);
- });
-
it('removes the list item if list not empty', () => {
cy.clickUnorderedListButton()
.type('foo')
- .enter({ times: 2 })
+ .enter()
.backspace()
.confirmMarkdownEditorContent(`
@@ -544,7 +537,7 @@ describe('Markdown widget', () => {
it('indents nested list items', () => {
cy.clickUnorderedListButton()
.type('foo')
- .enter({ times: 2 })
+ .enter()
.type('bar')
.tabkey()
.confirmMarkdownEditorContent(`
@@ -559,7 +552,7 @@ describe('Markdown widget', () => {
`)
- .enter({ times: 2 })
+ .enter()
.tabkey()
.confirmMarkdownEditorContent(`
@@ -583,8 +576,8 @@ describe('Markdown widget', () => {
it('only nests up to one level down from the parent list', () => {
cy.clickUnorderedListButton()
.type('foo')
- .enter({ times: 2 })
- .tabkey({ times: 5 })
+ .enter()
+ .tabkey()
.confirmMarkdownEditorContent(`
-
@@ -602,7 +595,7 @@ describe('Markdown widget', () => {
it('unindents nested list items with shift', () => {
cy.clickUnorderedListButton()
.type('foo')
- .enter({ times: 2 })
+ .enter()
.tabkey()
.tabkey({ shift: true })
.confirmMarkdownEditorContent(`
@@ -620,10 +613,10 @@ describe('Markdown widget', () => {
it('indents and unindents from one level below parent back to document root', () => {
cy.clickUnorderedListButton()
.type('foo')
- .enter({ times: 2 })
+ .enter()
.tabkey()
.type('bar')
- .enter({ times: 2 })
+ .enter()
.tabkey()
.type('baz')
.confirmMarkdownEditorContent(`
diff --git a/cypress/integration/markdown_widget_quote_spec.js b/cypress/integration/markdown_widget_quote_spec.js
index 9c6d90d1..503c672f 100644
--- a/cypress/integration/markdown_widget_quote_spec.js
+++ b/cypress/integration/markdown_widget_quote_spec.js
@@ -59,8 +59,8 @@ describe('Markdown widget', () => {
.clickUnorderedListButton()
.clickHeadingOneButton()
.type('foo')
- .enter({ times: 3 })
- .clickQuoteButton()
+ .enter({ times: 2 }) // First Enter creates new list item. Second Enter turns that list item into a default block.
+ .clickQuoteButton() // Unwrap the quote block.
.confirmMarkdownEditorContent(`
-
@@ -126,7 +126,7 @@ describe('Markdown widget', () => {
cy.focused()
.clickUnorderedListButton()
.type('foo')
- .enter({ times: 2 })
+ .enter()
.type('bar')
.setSelection('foo', 'bar')
.clickQuoteButton()
@@ -154,7 +154,7 @@ describe('Markdown widget', () => {
`)
.setCursorAfter('bar')
- .enter({ times: 2 })
+ .enter()
.type('baz')
.setSelection('bar', 'baz')
.clickQuoteButton()
@@ -185,6 +185,38 @@ describe('Markdown widget', () => {
.clickUnorderedListButton()
.clickQuoteButton()
.type('foo')
+ // Content should contains 4 tags and 3 tags
+ .confirmMarkdownEditorContent(`
+
+
+
+ `)
+ /*
+ * First Enter creates new paragraph within the innermost block quote.
+ * Second Enter moves that paragraph one level up to become sibling of the previous quote block and direct child of a list item.
+ * Third Enter to turn that paragraph into a list item and move it one level up.
+ * Repeat the circle for three more times to reach the second list item of the outermost list block.
+ * Then Enter again to turn that list item into a paragraph and move it one level up to become sibling of the outermost list and
+ * direct child of the outermost block quote.
+ */
.enter({ times: 10 })
.type('bar')
.confirmMarkdownEditorContent(`
@@ -211,7 +243,16 @@ describe('Markdown widget', () => {
bar
`)
- .backspace({ times: 12 })
+ /* The GOAL is to delete all the text content inside this deeply nested block quote and turn it into a default paragraph block on top level.
+ * We need:
+ * 3 Backspace to delete the word “bar”.
+ * 1 Backspace to remove the paragraph that contains bar and bring cursor to the end of the unordered list which is direct child of the outermost block quote.
+ * 3 Backspace to remove the word “foo”.
+ * 1 Backspace to remove the current block quote that the cursor is on, 1 Backspace to remove the list that wraps the block quote. Repeat this step for three times for a total of 6 Backspace until the cursor is on the outermost block quote.
+ * 1 Backspace to remove to toggle off the outermost block quote and turn it into a default paragraph.
+ * Total Backspaces required: 3 + 1 + 3 + ((1 + 1) * 3) + 1 = 14
+ */
+ .backspace({ times: 14 })
});
});
diff --git a/packages/netlify-cms-widget-markdown/src/MarkdownControl/plugins/List.js b/packages/netlify-cms-widget-markdown/src/MarkdownControl/plugins/List.js
index c47b68ee..d7867ac8 100644
--- a/packages/netlify-cms-widget-markdown/src/MarkdownControl/plugins/List.js
+++ b/packages/netlify-cms-widget-markdown/src/MarkdownControl/plugins/List.js
@@ -268,9 +268,42 @@ function ListPlugin({ defaultType, unorderedListType, orderedListType }) {
if (listOrListItem.type === 'list-item') {
const listItem = listOrListItem;
-
+ const {
+ value: { document },
+ } = editor;
// If focus is at start of list item, unwrap the entire list item.
if (editor.atStartOf(listItem)) {
+ /* If the list item in question has a grandparent list, this means it is a child of a nested list.
+ * Hitting Enter key on an empty nested list item like this should move that list item out of the nested list
+ * and into the grandparent list. The targeted list item becomes direct child of its grandparent list
+ * Example
+ * ----- GRANDPARENT LIST
+ * - ------ GRANDPARENT LIST ITEM
+ *
foo
+ * ----- PARENT LIST
+ * -
+ *
bar
+ *
+ * - ------ LIST ITEM
+ * ----- WHERE THE ENTER KEY HAPPENS
+ *
+ *
+ *
+ *
+ */
+ const parentList = document.getParent(listItem.key);
+ const grandparentListItem = document.getParent(parentList.key);
+ if (grandparentListItem.type === 'list-item') {
+ const grandparentList = document.getParent(grandparentListItem.key);
+ const indexOfGrandparentListItem = grandparentList.nodes.findIndex(
+ node => node.key === grandparentListItem.key,
+ );
+ return editor.moveNodeByKey(
+ listItem.key,
+ grandparentList.key,
+ indexOfGrandparentListItem + 1,
+ );
+ }
return editor.unwrapListItem(listItem);
}
@@ -285,7 +318,23 @@ function ListPlugin({ defaultType, unorderedListType, orderedListType }) {
editor.wrapBlockAtRange(range, newListItem).unwrapNodeByKey(newListItem.key);
});
}
-
+ const list = document.getParent(listItem.key);
+ if (LIST_TYPES.includes(list.type)) {
+ const newListItem = Block.create({
+ type: 'list-item',
+ nodes: [Block.create('paragraph')],
+ });
+ // Check if the targeted list item contains a nested list. If it does, insert a new list item in the beginning of that nested list.
+ const nestedList = listItem.findDescendant(block => LIST_TYPES.includes(block.type));
+ if (nestedList) {
+ return editor.insertNodeByKey(nestedList.key, 0, newListItem).moveForward(1); // Each list item is separated by a \n character. We need to move the cursor past this character so that it'd be on new list item that has just been inserted
+ }
+ // Find index of the list item block that receives Enter key
+ const previousListItemIndex = list.nodes.findIndex(block => block.key === listItem.key);
+ return editor
+ .insertNodeByKey(list.key, previousListItemIndex + 1, newListItem) // insert a new list item after the list item above
+ .moveForward(1);
+ }
return next();
} else {
const list = listOrListItem;