Skip to content

Commit

Permalink
Merge pull request #14722 from ckeditor/ck/14714
Browse files Browse the repository at this point in the history
Fix (paragraph): Fixed inserting paragraph after/before a widget inside a table cell. Closes #14714.
  • Loading branch information
arkflpc authored Aug 3, 2023
2 parents b823341 + 6ea380e commit 9266125
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 34 deletions.
72 changes: 38 additions & 34 deletions packages/ckeditor5-paragraph/src/insertparagraphcommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
*/

import { Command, type Editor } from '@ckeditor/ckeditor5-core';
import type { Element, Position } from '@ckeditor/ckeditor5-engine';
import type { Element, Position, Writer } from '@ckeditor/ckeditor5-engine';

/**
* The insert paragraph command. It inserts a new paragraph at a specific
Expand Down Expand Up @@ -50,61 +50,65 @@ export default class InsertParagraphCommand extends Command {
const model = this.editor.model;
const attributes = options.attributes;

let position = options.position;
let position: Position | null = options.position;

// Don't execute command if position is in non-editable place.
if ( !model.canEditAt( position ) ) {
return;
}

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

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

const paragraph = writer.createElement( 'paragraph' );

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

if ( position.path.length < 2 ) {
model.insertContent( paragraph, position );
writer.setSelection( paragraph, 'in' );

return;
}
model.insertContent( paragraph, position );
writer.setSelection( paragraph, 'in' );
} );
}

const positionParent = position.parent as Element;
/**
* Returns the best position to insert a new paragraph.
*/
private _findPositionToInsertParagraph( position: Position, writer: Writer ): Position | null {
const model = this.editor.model;

// E.g.
// <paragraph>[]</paragraph> ---> <paragraph></paragraph><paragraph>[]</paragraph>
const isInEmptyBlock = positionParent.isEmpty;
if ( model.schema.checkChild( position, 'paragraph' ) ) {
return position;
}

// E.g.
// <paragraph>foo[]</paragraph> ---> <paragraph>foo</paragraph><paragraph>[]</paragraph>
const isAtEndOfTextBlock = position.isAtEnd && !positionParent.isEmpty;
const allowedParent = model.schema.findAllowedParent( position, 'paragraph' );

// 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 null;
}

const canBeChild = model.schema.checkChild( positionParent, paragraph );
const positionParent = position.parent as Element;
const isTextAllowed = model.schema.checkChild( positionParent, '$text' );

if ( isInEmptyBlock || isAtEndOfTextBlock ) {
position = writer.createPositionAfter( positionParent );
} else if ( isAtStartOfTextBlock ) {
position = writer.createPositionBefore( positionParent );
} else if ( !canBeChild ) {
position = writer.split( position, allowedParent ).position;
}
// At empty $block or at the end of $block.
// <paragraph>[]</paragraph> ---> <paragraph></paragraph><paragraph>[]</paragraph>
// <paragraph>foo[]</paragraph> ---> <paragraph>foo</paragraph><paragraph>[]</paragraph>
if ( positionParent.isEmpty || isTextAllowed && position.isAtEnd ) {
return model.createPositionAfter( positionParent );
}

model.insertContent( paragraph, position );
// At the start of $block with text.
// <paragraph>[]foo</paragraph> ---> <paragraph>[]</paragraph><paragraph>foo</paragraph>
if ( !positionParent.isEmpty && isTextAllowed && position.isAtStart ) {
return model.createPositionBefore( positionParent );
}

writer.setSelection( paragraph, 'in' );
} );
return writer.split( position, allowedParent ).position;
}
}
62 changes: 62 additions & 0 deletions packages/ckeditor5-paragraph/tests/insertparagraphcommand.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,68 @@ describe( 'InsertParagraphCommand', () => {
);
} );

// See https://github.com/ckeditor/ckeditor5/issues/14714.
it( 'should insert paragraph bellow the block widget (inside container)', () => {
schema.register( 'blockContainer', { inheritAllFrom: '$container' } );
schema.register( 'blockWidget', { inheritAllFrom: '$blockObject', allowIn: 'allowP' } );

setData( model,
'<blockContainer>' +
'[<blockWidget></blockWidget>]' +
'</blockContainer>'
);

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

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

// See https://github.com/ckeditor/ckeditor5/issues/14714.
it( 'should insert paragraph bellow the block widget (inside table cell)', () => {
schema.register( 'table', { inheritAllFrom: '$blockObject' } );
schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
schema.register( 'tableCell', {
allowContentOf: '$container',
allowIn: 'tableRow',
isLimit: true,
isSelectable: true
} );

schema.register( 'blockWidget', { inheritAllFrom: '$blockObject' } );

setData( model,
'<table>' +
'<tableRow>' +
'<tableCell>' +
'[<blockWidget></blockWidget>]' +
'</tableCell>' +
'</tableRow>' +
'</table>'
);

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

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

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

0 comments on commit 9266125

Please sign in to comment.