Skip to content

Commit

Permalink
Merge pull request #7778 from ckeditor/i/1006
Browse files Browse the repository at this point in the history
Feature (clipboard): Pasting a plain text will inherit selection attributes. Closes #1006.
  • Loading branch information
jodator authored Aug 6, 2020
2 parents 579c1c8 + 454bab1 commit 2a163e3
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 0 deletions.
23 changes: 23 additions & 0 deletions packages/ckeditor5-clipboard/src/clipboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,15 @@ export default class Clipboard extends Plugin {
return;
}

// While pasting plain text, apply selection attributes on the text.
if ( isPlainText( modelFragment ) ) {
const node = modelFragment.getChild( 0 );

model.change( writer => {
writer.setAttributes( modelDocument.selection.getAttributes(), node );
} );
}

model.insertContent( modelFragment );
evt.stop();
}
Expand Down Expand Up @@ -198,3 +207,17 @@ export default class Clipboard extends Plugin {
*
* @member {'copy'|'cut'} module:clipboard/clipboard~ClipboardOutputEventData#method
*/

// Returns true if specified `documentFragment` represents a plain text.
//
// @param {module:engine/view/documentfragment~DocumentFragment} documentFragment
// @returns {Boolean}
function isPlainText( documentFragment ) {
if ( documentFragment.childCount > 1 ) {
return false;
}

const child = documentFragment.getChild( 0 );

return [ ...child.getAttributeKeys() ].length == 0;
}
110 changes: 110 additions & 0 deletions packages/ckeditor5-clipboard/tests/clipboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,116 @@ describe( 'Clipboard feature', () => {
expect( spy.callCount ).to.equal( 1 );
} );

// https://github.com/ckeditor/ckeditor5/issues/1006
describe( 'pasting plain text', () => {
let model;

beforeEach( () => {
model = editor.model;

model.schema.extend( '$text', { allowAttributes: 'bold' } );
} );

it( 'should inherit selection attributes (collapsed selection)', () => {
const insertContent = model.insertContent.bind( model );
let insertedNode;

sinon.stub( model, 'insertContent' ).callsFake( documentFragment => {
insertedNode = documentFragment.getChild( 0 );

return insertContent( documentFragment );
} );

setModelData( model, '<paragraph><$text bold="true">Bolded []text.</$text></paragraph>' );

const dataTransferMock = createDataTransfer( { 'text/plain': 'foo' } );

viewDocument.fire( 'paste', {
dataTransfer: dataTransferMock,
stopPropagation() {},
preventDefault() {}
} );

expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bolded foo[]text.</$text></paragraph>' );
expect( insertedNode.getAttribute( 'bold' ) ).to.equal( true );
} );

it( 'should inherit selection attributes (non-collapsed selection)', () => {
const insertContent = model.insertContent.bind( model );
let insertedNode;

sinon.stub( model, 'insertContent' ).callsFake( documentFragment => {
insertedNode = documentFragment.getChild( 0 );

return insertContent( documentFragment );
} );

setModelData( model, '<paragraph><$text bold="true">Bolded [text.]</$text></paragraph>' );

const dataTransferMock = createDataTransfer( { 'text/plain': 'foo' } );

viewDocument.fire( 'paste', {
dataTransfer: dataTransferMock,
stopPropagation() {},
preventDefault() {}
} );

expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bolded foo[]</$text></paragraph>' );
expect( insertedNode.getAttribute( 'bold' ) ).to.equal( true );
} );

it( 'should inherit selection attributes while pasting a plain text as text/html', () => {
setModelData( model, '<paragraph><$text bold="true">Bolded []text.</$text></paragraph>' );

const dataTransferMock = createDataTransfer( {
'text/html': 'foo',
'text/plain': 'foo'
} );

viewDocument.fire( 'paste', {
dataTransfer: dataTransferMock,
stopPropagation() {},
preventDefault() {}
} );

expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bolded foo[]text.</$text></paragraph>' );
} );

it( 'should inherit selection attributes while pasting a plain text as text/html (Chrome style)', () => {
setModelData( model, '<paragraph><$text bold="true">Bolded []text.</$text></paragraph>' );

const dataTransferMock = createDataTransfer( {
'text/html': '<meta http-equiv="content-type" content="text/html; charset=utf-8">foo',
'text/plain': 'foo'
} );

viewDocument.fire( 'paste', {
dataTransfer: dataTransferMock,
stopPropagation() {},
preventDefault() {}
} );

expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bolded foo[]text.</$text></paragraph>' );
} );

it( 'should inherit selection attributes while pasting HTML with unsupported attributes', () => {
setModelData( model, '<paragraph><$text bold="true">Bolded []text.</$text></paragraph>' );

const dataTransferMock = createDataTransfer( {
'text/html': '<i>foo</i>',
'text/plain': 'foo'
} );

viewDocument.fire( 'paste', {
dataTransfer: dataTransferMock,
stopPropagation() {},
preventDefault() {}
} );

expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bolded foo[]text.</$text></paragraph>' );
} );
} );

function createDataTransfer( data ) {
return {
getData( type ) {
Expand Down

0 comments on commit 2a163e3

Please sign in to comment.