Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

insertParagraph command creates additional empty paragraph when used at the end of block element #14510

40 changes: 33 additions & 7 deletions packages/ckeditor5-paragraph/src/insertparagraphcommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,46 @@ export default class InsertParagraphCommand extends Command {

model.change( writer => {
const paragraph = writer.createElement( 'paragraph' );
const allowedParent = model.schema.findAllowedParent( position, paragraph );

// It could be there's no ancestor limit that would allow paragraph.
// In theory, "paragraph" could be disallowed even in the "$root".
if ( !allowedParent ) {
return;
}

if ( attributes ) {
model.schema.setAllowedAttributes( paragraph, attributes, writer );
}

if ( !model.schema.checkChild( position.parent as Element, paragraph ) ) {
const allowedParent = model.schema.findAllowedParent( position, paragraph );
if ( position.path.length < 2 ) {
model.insertContent( paragraph, position );
writer.setSelection( paragraph, 'in' );

return;
}

const positionParent = position.parent as Element;

// E.g.
// <paragraph>[]</paragraph> ---> <paragraph></paragraph><paragraph>[]</paragraph>
const isInEmptyBlock = positionParent.isEmpty;

// E.g.
// <paragraph>foo[]</paragraph> ---> <paragraph>foo</paragraph><paragraph>[]</paragraph>
const isAtEndOfTextBlock = position.isAtEnd && !positionParent.isEmpty;

// E.g.
// <paragraph>[]foo</paragraph> ---> <paragraph>[]</paragraph><paragraph>foo</paragraph>
const isAtStartOfTextBlock = position.isAtStart && !positionParent.isEmpty;

// It could be there's no ancestor limit that would allow paragraph.
// In theory, "paragraph" could be disallowed even in the "$root".
if ( !allowedParent ) {
return;
}
const canBeChild = model.schema.checkChild( positionParent, paragraph );

if ( isInEmptyBlock || isAtEndOfTextBlock ) {
position = writer.createPositionAfter( positionParent );
} else if ( isAtStartOfTextBlock ) {
position = writer.createPositionBefore( positionParent );
} else if ( !canBeChild ) {
position = writer.split( position, allowedParent ).position;
}

Expand Down
67 changes: 67 additions & 0 deletions packages/ckeditor5-paragraph/tests/insertparagraphcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,73 @@ describe( 'InsertParagraphCommand', () => {
);
} );

it( 'should insert paragraph when position is at the end of line', () => {
setData( model, '<paragraph>foo[]</paragraph>' );

command.execute( {
position: model.document.selection.getFirstPosition()
} );

expect( getData( model ) ).to.equal(
'<paragraph>foo</paragraph>' +
'<paragraph>[]</paragraph>'
);
} );

it( 'should insert paragraph when position is at the end of line with an inline widget', () => {
schema.register( 'inlineWidget', { inheritAllFrom: '$inlineObject' } );
setData( model, '<paragraph><inlineWidget></inlineWidget>[]</paragraph>' );

command.execute( {
position: model.document.selection.getFirstPosition()
} );

expect( getData( model ) ).to.equal(
'<paragraph><inlineWidget></inlineWidget></paragraph>' +
'<paragraph>[]</paragraph>'
);
} );

it( 'should insert paragraph when position is at the start of line', () => {
setData( model, '<paragraph>[]foo</paragraph>' );

command.execute( {
position: model.document.selection.getLastPosition()
} );

expect( getData( model ) ).to.equal(
'<paragraph>[]</paragraph>' +
'<paragraph>foo</paragraph>'
);
} );

it( 'should insert paragraph when position is at the start of line with an inline widget', () => {
schema.register( 'inlineWidget', { inheritAllFrom: '$inlineObject' } );
setData( model, '<paragraph>[]<inlineWidget></inlineWidget></paragraph>' );

command.execute( {
position: model.document.selection.getLastPosition()
} );

expect( getData( model ) ).to.equal(
'<paragraph>[]</paragraph>' +
'<paragraph><inlineWidget></inlineWidget></paragraph>'
);
} );

it( 'should insert paragraph bellow when paragraph is empty', () => {
setData( model, '<paragraph>[]</paragraph>' );

command.execute( {
position: model.document.selection.getLastPosition()
} );

expect( getData( model ) ).to.equal(
'<paragraph></paragraph>' +
'<paragraph>[]</paragraph>'
);
} );

it( 'should do nothing if the paragraph is not allowed at the provided position', () => {
// Create a situation where "paragraph" is disallowed even in the "root".
schema.addChildCheck( ( context, childDefinition ) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1053,7 +1053,7 @@ describe( 'WidgetTypeAround', () => {
expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
} );

it( 'should split ancestors to find a place that allows a widget', () => {
it( 'should split ancestors to find a place that allows a widget (no content after widget)', () => {
model.schema.register( 'allowP', {
inheritAllFrom: '$block'
} );
Expand Down Expand Up @@ -1083,7 +1083,41 @@ describe( 'WidgetTypeAround', () => {
'<allowP>' +
'<disallowP><blockWidget></blockWidget></disallowP>' +
'<paragraph>[]</paragraph>' +
'<disallowP></disallowP>' +
'</allowP>'
);
} );

it( 'should split ancestors to find a place that allows a widget (with content after widget)', () => {
model.schema.register( 'allowP', {
inheritAllFrom: '$block'
} );
model.schema.register( 'disallowP', {
inheritAllFrom: '$block',
allowIn: [ 'allowP' ]
} );
model.schema.extend( 'blockWidget', {
allowIn: [ 'allowP', 'disallowP' ]
} );
model.schema.extend( 'paragraph', {
allowIn: [ 'allowP' ]
} );

editor.conversion.for( 'downcast' ).elementToElement( { model: 'allowP', view: 'allowP' } );
editor.conversion.for( 'downcast' ).elementToElement( { model: 'disallowP', view: 'disallowP' } );

setModelData( model,
'<allowP>' +
'<disallowP>[<blockWidget></blockWidget>]<blockWidget></blockWidget></disallowP>' +
'</allowP>'
);

fireEnter();

expect( getModelData( model ) ).to.equal(
'<allowP>' +
'<disallowP><blockWidget></blockWidget></disallowP>' +
'<paragraph>[]</paragraph>' +
'<disallowP><blockWidget></blockWidget></disallowP>' +
'</allowP>'
);
} );
Expand Down