Procházet zdrojové kódy

Fixed: <ul> and <ol> elements no will not be inserted before view ui elements.

Szymon Cofalik před 8 roky
rodič
revize
cb3c6c3bf5

+ 13 - 0
packages/ckeditor5-list/src/converters.js

@@ -885,6 +885,8 @@ function injectViewList( modelItem, injectedItem, mapper, removePosition ) {
 		}
 	}
 
+	insertPosition = positionAfterUiElements( insertPosition );
+
 	// Insert the view item.
 	viewWriter.insert( insertPosition, injectedList );
 
@@ -1001,6 +1003,8 @@ function hoistNestedLists( nextIndent, modelRemoveStartPosition, viewRemoveStart
 		insertPosition = mapper.toViewPosition( modelPosition );
 	}
 
+	insertPosition = positionAfterUiElements( insertPosition );
+
 	// Handle multiple lists. This happens if list item has nested numbered and bulleted lists. Following lists
 	// are inserted after the first list (no need to recalculate insertion position for them).
 	for ( const child of [ ...viewRemovedItem.getChildren() ] ) {
@@ -1030,3 +1034,12 @@ function _getBoundaryItemOfSameList( item, getFirst ) {
 
 	return result;
 }
+
+// Helper function that for given `view.Position`, returns a `view.Position` that is after all `view.UIElement`s that
+// are after given position.
+// For example:
+// <container:p>foo^<ui:span></ui:span><ui:span></ui:span>bar</contain:p>
+// For position ^, a position before "bar" will be returned.
+function positionAfterUiElements( viewPosition ) {
+	return viewPosition.getLastMatchingPosition( value => value.item.is( 'uiElement' ) );
+}

+ 41 - 0
packages/ckeditor5-list/tests/listengine.js

@@ -11,6 +11,7 @@ import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position';
 import ModelElement from '@ckeditor/ckeditor5-engine/src/model/element';
 import ModelText from '@ckeditor/ckeditor5-engine/src/model/text';
 import ViewPosition from '@ckeditor/ckeditor5-engine/src/view/position';
+import ViewUIElement from '@ckeditor/ckeditor5-engine/src/view/uielement';
 
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import BoldEngine from '@ckeditor/ckeditor5-basic-styles/src/boldengine';
@@ -3303,6 +3304,46 @@ describe( 'ListEngine', () => {
 
 			editor.setData( '<ul><li>Foo</li><li>Bar</li></ul>' );
 		} );
+
+		// This test tests the fix in `injectViewList` helper.
+		it( 'ul and ol should not be inserted before ui element - injectViewList()', () => {
+			editor.setData( '<ul><li>Foo</li><li>Bar</li></ul>' );
+
+			const uiElement = new ViewUIElement( 'span' );
+
+			// Append ui element at the end of first <li>.
+			viewDoc.getRoot().getChild( 0 ).getChild( 0 ).appendChildren( [ uiElement ] );
+
+			expect( getViewData( editor.editing.view, { withoutSelection: true } ) )
+				.to.equal( '<ul><li>Foo<span></span></li><li>Bar</li></ul>' );
+
+			// Change indent of the second list item.
+			modelDoc.batch().setAttribute( modelRoot.getChild( 1 ), 'indent', 1 );
+
+			// Check if the new <ul> was added at correct position.
+			expect( getViewData( editor.editing.view, { withoutSelection: true } ) )
+				.to.equal( '<ul><li>Foo<span></span><ul><li>Bar</li></ul></li></ul>' );
+		} );
+
+		// This test tests the fix in `hoistNestedLists` helper.
+		it( 'ul and ol should not be inserted before ui element - hoistNestedLists()', () => {
+			editor.setData( '<ul><li>Foo</li><li>Bar<ul><li>Xxx</li><li>Yyy</li></ul></li></ul>' );
+
+			const uiElement = new ViewUIElement( 'span' );
+
+			// Append ui element at the end of first <li>.
+			viewRoot.getChild( 0 ).getChild( 0 ).appendChildren( [ uiElement ] );
+
+			expect( getViewData( editor.editing.view, { withoutSelection: true } ) )
+				.to.equal( '<ul><li>Foo<span></span></li><li>Bar<ul><li>Xxx</li><li>Yyy</li></ul></li></ul>' );
+
+			// Remove second list item. Expect that its sub-list will be moved to first list item.
+			modelDoc.batch().remove( modelRoot.getChild( 1 ) );
+
+			// Check if the <ul> was added at correct position.
+			expect( getViewData( editor.editing.view, { withoutSelection: true } ) )
+				.to.equal( '<ul><li>Foo<span></span><ul><li>Xxx</li><li>Yyy</li></ul></li></ul>' );
+		} );
 	} );
 
 	function getViewPosition( root, path ) {