fix(widget-markdown): Hitting Enter key in a list item doesn't create a new list item (#5550)

This commit is contained in:
Trang Le 2021-07-22 21:01:35 +07:00 committed by GitHub
parent 3bd36776d6
commit ab3e8e1f5a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 180 additions and 97 deletions

View File

@ -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', () => {
<ul>
<li>
<p>foo</p>
<ul>
<li>
<p></p>
</li>
</ul>
</li>
</ul>
<p></p>
`)
.type('bar')
.enter()
.clickUnorderedListButton()
.confirmMarkdownEditorContent(`
<ul>
<li>
<p>foo</p>
<ul>
<li>
<p>bar</p>
<ul>
<li>
<p></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
`);
});
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(`
<ul>
<li>
@ -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(`
<ul>
<li>
@ -250,7 +226,6 @@ describe('Markdown widget', () => {
.up()
.enter()
.type('quux')
.clickUnorderedListButton()
.confirmMarkdownEditorContent(`
<ul>
<li>
@ -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(`
<ul>
<li>
<p>foo</p>
</li>
<li>
<p>bar</p>
<ul>
<li>
<p>baz</p>
</li>
</ul>
</li>
</ul>
`)
.setCursorAfter('baz')
.enter()
.clickUnorderedListButton()
.tabkey()
.type('qux')
.confirmMarkdownEditorContent(`
<ul>
<li>
<p>foo</p>
</li>
<li>
<p>bar</p>
<ul>
<li>
<p>baz</p>
<ul>
<li>
<p>qux</p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
`)
.setSelection('baz')
.clickOrderedListButton()
.confirmMarkdownEditorContent(`
<ul>
<li>
<p>foo</p>
</li>
<li>
<p>bar</p>
<ol>
<li>
<p>baz</p>
<ul>
<li>
<p>qux</p>
</li>
</ul>
</li>
</ol>
</li>
</ul>
`)
.setCursorAfter('qux')
.enter({ times: 4 })
.enter({ times: 2 })
.clickUnorderedListButton()
.confirmMarkdownEditorContent(`
<ul>
@ -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', () => {
<ul>
<li>
<p>foo</p>
</li>
<li>
<p></p>
</li>
</ul>
@ -416,46 +448,21 @@ describe('Markdown widget', () => {
<ul>
<li>
<p>foo</p>
</li>
<li>
<p>bar</p>
</li>
<li>
<p></p>
</li>
</ul>
`);
});
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(`
<ul>
<li>
<p>foo</p>
</li>
<li>
<p></p>
</li>
</ul>
`)
.type('bar')
.enter()
.confirmMarkdownEditorContent(`
<ul>
<li>
<p>foo</p>
</li>
<li>
<p>bar</p>
<p></p>
</li>
</ul>
`);
});
it('creates a new block below list', () => {
cy.clickUnorderedListButton()
.type('foo')
.enter({ times: 3 })
.confirmMarkdownEditorContent(`
<ul>
<li>
@ -476,24 +483,10 @@ describe('Markdown widget', () => {
`);
});
it('removes empty block in non-empty list item', () => {
cy.clickUnorderedListButton()
.type('foo')
.enter()
.backspace()
.confirmMarkdownEditorContent(`
<ul>
<li>
<p>foo</p>
</li>
</ul>
`);
});
it('removes the list item if list not empty', () => {
cy.clickUnorderedListButton()
.type('foo')
.enter({ times: 2 })
.enter()
.backspace()
.confirmMarkdownEditorContent(`
<ul>
@ -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', () => {
</li>
</ul>
`)
.enter({ times: 2 })
.enter()
.tabkey()
.confirmMarkdownEditorContent(`
<ul>
@ -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(`
<ul>
<li>
@ -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(`

View File

@ -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(`
<ul>
<li>
@ -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', () => {
</ul>
`)
.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 <blockquote> tags and 3 <ul> tags
.confirmMarkdownEditorContent(`
<blockquote>
<ul>
<li>
<blockquote>
<ul>
<li>
<blockquote>
<ul>
<li>
<blockquote>
<p>foo</p>
</blockquote>
</li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
</li>
</ul>
</blockquote>
`)
/*
* 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', () => {
<p>bar</p>
</blockquote>
`)
.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 })
});
});

View File

@ -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
* <ul> ----- GRANDPARENT LIST
* <li> ------ GRANDPARENT LIST ITEM
* <p>foo</p>
* <ul> ----- PARENT LIST
* <li>
* <p>bar</p>
* </li>
* <li> ------ LIST ITEM
* <p></p> ----- WHERE THE ENTER KEY HAPPENS
* </li>
* </ul>
* </li>
* </ul>
*/
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;