8
0
Quellcode durchsuchen

Fixed ugly crashes when playing with List style feature.

Kamil Piechaczek vor 5 Jahren
Ursprung
Commit
49dc52bee2

+ 25 - 2
packages/ckeditor5-list/src/liststyleediting.js

@@ -141,11 +141,29 @@ export default class ListStyleEditing extends Plugin {
 				return;
 			}
 
+			// Find the most outer item list based on the `listIndent` attribute. We can't assume that `listIndent=0`
+			// because the selection can be hooked in nested lists.
+			//
+			// <listItem listIndent="0" listType="bulleted" listStyle="square">UL List item 1</listItem>
+			// <listItem listIndent="1" listType="bulleted" listStyle="square">UL List [item 1.1</listItem>
+			// <listItem listIndent="0" listType="bulleted" listStyle="circle">[]UL List item 1.</listItem>
+			// <listItem listIndent="1" listType="bulleted" listStyle="circle">UL List ]item 1.1</listItem>
+			//
+			// After deleting the content, we would like to inherit the "square" attribute for the last element:
+			//
+			// <listItem listIndent="0" listType="bulleted" listStyle="square">UL List item 1</listItem>
+			// <listItem listIndent="1" listType="bulleted" listStyle="square">UL List []item 1.1</listItem>
 			const mostOuterItemList = getSiblingListItem( firstPosition.parent, {
 				sameIndent: true,
-				listIndent: 0
+				listIndent: nextSibling.getAttribute( 'listIndent' )
 			} );
 
+			// The most outer list item may not exist while removing elements between lists with different value
+			// of the `listIndent` attribute. In such a case we don't want to update anything. See: #8073.
+			if ( !mostOuterItemList ) {
+				return;
+			}
+
 			if ( mostOuterItemList.getAttribute( 'listType' ) === nextSibling.getAttribute( 'listType' ) ) {
 				firstMostOuterItem = mostOuterItemList;
 			}
@@ -167,7 +185,7 @@ export default class ListStyleEditing extends Plugin {
 				// <listItem listIndent="0" listType="bulleted" listStyle="circle">UL List item 2</listItem>
 				const secondListMostOuterItem = getSiblingListItem( firstMostOuterItem.nextSibling, {
 					sameIndent: true,
-					listIndent: 0,
+					listIndent: firstMostOuterItem.getAttribute( 'listIndent' ),
 					direction: 'forward'
 				} );
 
@@ -459,6 +477,11 @@ function fixListStyleAttributeOnListItemElements( editor ) {
 				// ■ Paragraph[]  // <-- The inserted item.
 				while ( existingListItem.is( 'element', 'listItem' ) && existingListItem.getAttribute( 'listIndent' ) !== indent ) {
 					existingListItem = existingListItem.previousSibling;
+
+					// If the item does not exist, most probably there is no other content in the editor. See: #8072.
+					if ( !existingListItem ) {
+						break;
+					}
 				}
 			}
 		}

+ 58 - 4
packages/ckeditor5-list/tests/liststyleediting.js

@@ -725,6 +725,21 @@ describe( 'ListStyleEditing', () => {
 					'<listItem listIndent="1" listStyle="decimal" listType="numbered">3.[]</listItem>'
 				);
 			} );
+
+			// See: #8072.
+			it( 'should not throw when indenting a list without any other content in the editor', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="default" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="0" listStyle="default" listType="bulleted">[]</listItem>'
+				);
+
+				editor.execute( 'indentList' );
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="default" listType="bulleted">Foo</listItem>' +
+					'<listItem listIndent="1" listStyle="default" listType="bulleted">[]</listItem>'
+				);
+			} );
 		} );
 
 		describe( 'outdenting lists', () => {
@@ -1096,8 +1111,8 @@ describe( 'ListStyleEditing', () => {
 				setModelData( model,
 					'<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
 					'<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
-					'<listItem listIndent="1" listStyle="numbered" listType="decimal">2.1.</listItem>' +
-					'<listItem listIndent="2" listStyle="default" listType="default">2.1.1</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +
+					'<listItem listIndent="2" listStyle="default" listType="numbered">2.1.1</listItem>' +
 					'<paragraph>[]</paragraph>' +
 					'<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
 					'<listItem listIndent="0" listStyle="circle" listType="bulleted">2.</listItem>'
@@ -1108,8 +1123,8 @@ describe( 'ListStyleEditing', () => {
 				expect( getModelData( model ) ).to.equal(
 					'<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
 					'<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
-					'<listItem listIndent="1" listStyle="numbered" listType="decimal">2.1.</listItem>' +
-					'<listItem listIndent="2" listStyle="default" listType="default">2.1.1[]</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +
+					'<listItem listIndent="2" listStyle="default" listType="numbered">2.1.1[]</listItem>' +
 					'<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
 					'<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>'
 				);
@@ -1233,6 +1248,45 @@ describe( 'ListStyleEditing', () => {
 				);
 			} );
 
+			// See: #8073.
+			it( 'should not crash when removing a content between intended lists', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="default" listType="bulleted">aaaa</listItem>' +
+					'<listItem listIndent="1" listStyle="default" listType="bulleted">bb[bb</listItem>' +
+					'<listItem listIndent="2" listStyle="default" listType="bulleted">cc]cc</listItem>' +
+					'<listItem listIndent="3" listStyle="default" listType="bulleted">dddd</listItem>'
+				);
+
+				editor.execute( 'delete' );
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="default" listType="bulleted">aaaa</listItem>' +
+					'<listItem listIndent="1" listStyle="default" listType="bulleted">bb[]cc</listItem>' +
+					'<listItem listIndent="2" listStyle="default" listType="bulleted">dddd</listItem>'
+				);
+			} );
+
+			it( 'should read the `listStyle` attribute from the most outer selected list while removing content between lists', () => {
+				setModelData( model,
+					'<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
+					'<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +
+					'<listItem listIndent="2" listStyle="lower-latin" listType="numbered">2.1.1[foo</listItem>' +
+					'<paragraph>Foo</paragraph>' +
+					'<listItem listIndent="0" listStyle="circle" listType="bulleted">1.</listItem>' +
+					'<listItem listIndent="1" listStyle="circle" listType="bulleted">bar]2.</listItem>'
+				);
+
+				editor.execute( 'delete' );
+
+				expect( getModelData( model ) ).to.equal(
+					'<listItem listIndent="0" listStyle="square" listType="bulleted">1.</listItem>' +
+					'<listItem listIndent="0" listStyle="square" listType="bulleted">2.</listItem>' +
+					'<listItem listIndent="1" listStyle="decimal" listType="numbered">2.1.</listItem>' +
+					'<listItem listIndent="2" listStyle="lower-latin" listType="numbered">2.1.1[]2.</listItem>'
+				);
+			} );
+
 			function simulateTyping( text ) {
 				// While typing, every character is an atomic change.
 				text.split( '' ).forEach( character => {