Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit fcb00c0

Browse files
author
Piotr Jasiun
authored
Merge pull request #25 from ckeditor/t/ckeditor5-engine/1563
Added: Postfixer cleaning incorrect blockquotes.
2 parents 71edad5 + 5ee3158 commit fcb00c0

File tree

5 files changed

+157
-24
lines changed

5 files changed

+157
-24
lines changed

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
"@ckeditor/ckeditor5-list": "^11.0.1",
2525
"@ckeditor/ckeditor5-paragraph": "^10.0.2",
2626
"@ckeditor/ckeditor5-typing": "^11.0.0",
27+
"@ckeditor/ckeditor5-engine": "^10.2.0",
28+
"@ckeditor/ckeditor5-basic-styles": "^10.0.2",
2729
"eslint": "^5.5.0",
2830
"eslint-config-ckeditor5": "^1.0.7",
2931
"husky": "^0.14.3",

src/blockquoteediting.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@
88
*/
99

1010
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
11+
import Range from '@ckeditor/ckeditor5-engine/src/model/range';
12+
import Position from '@ckeditor/ckeditor5-engine/src/model/position';
13+
1114
import BlockQuoteCommand from './blockquotecommand';
1215

1316
/**
@@ -40,6 +43,57 @@ export default class BlockQuoteEditing extends Plugin {
4043
} );
4144

4245
editor.conversion.elementToElement( { model: 'blockQuote', view: 'blockquote' } );
46+
47+
// Postfixer which cleans incorrect model states connected with block quotes.
48+
editor.model.document.registerPostFixer( writer => {
49+
const changes = editor.model.document.differ.getChanges();
50+
51+
for ( const entry of changes ) {
52+
if ( entry.type == 'insert' ) {
53+
const element = entry.position.nodeAfter;
54+
55+
if ( !element ) {
56+
// We are inside a text node.
57+
continue;
58+
}
59+
60+
if ( element.is( 'blockQuote' ) && element.isEmpty ) {
61+
// Added an empty blockQuote - remove it.
62+
writer.remove( element );
63+
64+
return true;
65+
} else if ( element.is( 'blockQuote' ) && !schema.checkChild( entry.position, element ) ) {
66+
// Added a blockQuote in incorrect place - most likely inside another blockQuote. Unwrap it
67+
// so the content inside is not lost.
68+
writer.unwrap( element );
69+
70+
return true;
71+
} else if ( element.is( 'element' ) ) {
72+
// Just added an element. Check its children to see if there are no nested blockQuotes somewhere inside.
73+
const range = Range.createIn( element );
74+
75+
for ( const child of range.getItems() ) {
76+
if ( child.is( 'blockQuote' ) && !schema.checkChild( Position.createBefore( child ), child ) ) {
77+
writer.unwrap( child );
78+
79+
return true;
80+
}
81+
}
82+
}
83+
} else if ( entry.type == 'remove' ) {
84+
const parent = entry.position.parent;
85+
86+
if ( parent.is( 'blockQuote' ) && parent.isEmpty ) {
87+
// Something got removed and now blockQuote is empty. Remove the blockQuote as well.
88+
writer.remove( parent );
89+
90+
return true;
91+
}
92+
}
93+
}
94+
95+
return false;
96+
} );
4397
}
4498

4599
/**

tests/blockquotecommand.js

Lines changed: 33 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -193,17 +193,19 @@ describe( 'BlockQuoteCommand', () => {
193193

194194
editor.execute( 'blockQuote' );
195195

196+
// Selection incorrectly trimmed.
196197
expect( getModelData( model ) ).to.equal(
197198
'<blockQuote>' +
198-
'<heading>a[bc</heading>' +
199-
'<paragraph>x]x</paragraph>' +
199+
'<heading>abc</heading>' +
200+
'<paragraph>[x]x</paragraph>' +
200201
'<paragraph>yy</paragraph>' +
201202
'</blockQuote>' +
202203
'<paragraph>def</paragraph>'
203204
);
204205

206+
// Selection incorrectly trimmed.
205207
expect( getViewData( editor.editing.view ) ).to.equal(
206-
'<blockquote><h>a{bc</h><p>x}x</p><p>yy</p></blockquote><p>def</p>'
208+
'<blockquote><h>abc</h><p>{x}x</p><p>yy</p></blockquote><p>def</p>'
207209
);
208210
} );
209211

@@ -258,17 +260,19 @@ describe( 'BlockQuoteCommand', () => {
258260

259261
editor.execute( 'blockQuote' );
260262

263+
// Selection incorrectly trimmed.
261264
expect( getModelData( model ) ).to.equal(
262265
'<blockQuote>' +
263-
'<heading>a[bc</heading>' +
266+
'<heading>abc</heading>' +
264267
'<paragraph>def</paragraph>' +
265-
'<paragraph>x]x</paragraph>' +
268+
'<paragraph>[x]x</paragraph>' +
266269
'</blockQuote>' +
267270
'<paragraph>ghi</paragraph>'
268271
);
269272

273+
// Selection incorrectly trimmed.
270274
expect( getViewData( editor.editing.view ) ).to.equal(
271-
'<blockquote><h>a{bc</h><p>def</p><p>x}x</p></blockquote><p>ghi</p>'
275+
'<blockquote><h>abc</h><p>def</p><p>{x}x</p></blockquote><p>ghi</p>'
272276
);
273277
} );
274278

@@ -282,18 +286,20 @@ describe( 'BlockQuoteCommand', () => {
282286

283287
editor.execute( 'blockQuote' );
284288

289+
// Selection incorrectly trimmed.
285290
expect( getModelData( model ) ).to.equal(
286291
'<blockQuote>' +
287-
'<paragraph>a[bc</paragraph>' +
292+
'<paragraph>abc</paragraph>' +
288293
'</blockQuote>' +
289-
'<widget>xx</widget>' +
294+
'[<widget>xx</widget>' +
290295
'<blockQuote>' +
291296
'<paragraph>de]f</paragraph>' +
292297
'</blockQuote>'
293298
);
294299

300+
// Selection incorrectly trimmed.
295301
expect( getViewData( editor.editing.view ) ).to.equal(
296-
'<blockquote><p>a{bc</p></blockquote><widget>xx</widget><blockquote><p>de}f</p></blockquote>'
302+
'<blockquote><p>abc</p></blockquote>[<widget>xx</widget><blockquote><p>de}f</p></blockquote>'
297303
);
298304
} );
299305

@@ -310,17 +316,19 @@ describe( 'BlockQuoteCommand', () => {
310316

311317
editor.execute( 'blockQuote' );
312318

319+
// Selection incorrectly trimmed.
313320
expect( getModelData( model ) ).to.equal(
314-
'<blockQuote><paragraph>a[bc</paragraph></blockQuote>' +
315-
'<widget>xx</widget>' +
321+
'<blockQuote><paragraph>abc</paragraph></blockQuote>' +
322+
'[<widget>xx</widget>' +
316323
'<blockQuote><paragraph>def</paragraph><paragraph>ghi</paragraph></blockQuote>' +
317324
'<widget>yy</widget>' +
318325
'<blockQuote><paragraph>jk]l</paragraph></blockQuote>'
319326
);
320327

328+
// Selection incorrectly trimmed.
321329
expect( getViewData( editor.editing.view ) ).to.equal(
322-
'<blockquote><p>a{bc</p></blockquote>' +
323-
'<widget>xx</widget>' +
330+
'<blockquote><p>abc</p></blockquote>' +
331+
'[<widget>xx</widget>' +
324332
'<blockquote><p>def</p><p>ghi</p></blockquote>' +
325333
'<widget>yy</widget>' +
326334
'<blockquote><p>jk}l</p></blockquote>'
@@ -341,26 +349,28 @@ describe( 'BlockQuoteCommand', () => {
341349

342350
editor.execute( 'blockQuote' );
343351

352+
// Selection incorrectly trimmed.
344353
expect( getModelData( model ) ).to.equal(
345354
'<paragraph>x</paragraph>' +
346355
'<blockQuote>' +
347-
'<paragraph>a[bc</paragraph>' +
356+
'<paragraph>abc</paragraph>' +
348357
'<paragraph>def</paragraph>' +
349358
'<paragraph>ghi</paragraph>' +
350359
'<paragraph>jkl</paragraph>' +
351-
'<paragraph>mn]o</paragraph>' +
360+
'<paragraph>[mn]o</paragraph>' +
352361
'</blockQuote>' +
353362
'<paragraph>y</paragraph>'
354363
);
355364

365+
// Selection incorrectly trimmed.
356366
expect( getViewData( editor.editing.view ) ).to.equal(
357367
'<p>x</p>' +
358368
'<blockquote>' +
359-
'<p>a{bc</p>' +
369+
'<p>abc</p>' +
360370
'<p>def</p>' +
361371
'<p>ghi</p>' +
362372
'<p>jkl</p>' +
363-
'<p>mn}o</p>' +
373+
'<p>{mn}o</p>' +
364374
'</blockquote>' +
365375
'<p>y</p>'
366376
);
@@ -386,11 +396,12 @@ describe( 'BlockQuoteCommand', () => {
386396

387397
editor.execute( 'blockQuote' );
388398

399+
// Selection incorrectly trimmed.
389400
expect( getModelData( model ) ).to.equal(
390401
'<blockQuote>' +
391-
'<paragraph>a[bc</paragraph>' +
402+
'<paragraph>abc</paragraph>' +
392403
'</blockQuote>' +
393-
'<fooBlock>xx</fooBlock>' +
404+
'<fooBlock>[xx</fooBlock>' +
394405
'<blockQuote>' +
395406
'<paragraph>de]f</paragraph>' +
396407
'</blockQuote>'
@@ -417,11 +428,12 @@ describe( 'BlockQuoteCommand', () => {
417428

418429
editor.execute( 'blockQuote' );
419430

431+
// Selection incorrectly trimmed.
420432
expect( getModelData( model ) ).to.equal(
421433
'<blockQuote>' +
422-
'<paragraph>a[bc</paragraph>' +
434+
'<paragraph>abc</paragraph>' +
423435
'</blockQuote>' +
424-
'<fooWrapper><fooBlock>xx</fooBlock></fooWrapper>' +
436+
'<fooWrapper><fooBlock>[xx</fooBlock></fooWrapper>' +
425437
'<blockQuote>' +
426438
'<paragraph>de]f</paragraph>' +
427439
'</blockQuote>'

tests/blockquoteediting.js

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import BlockQuoteEditing from '../src/blockquoteediting';
77
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
88
import ListEditing from '@ckeditor/ckeditor5-list/src/listediting';
9+
import BoldEditing from '@ckeditor/ckeditor5-basic-styles/src/bold/boldediting';
10+
11+
import Range from '@ckeditor/ckeditor5-engine/src/model/range';
912

1013
import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
1114
import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
@@ -18,7 +21,7 @@ describe( 'BlockQuoteEditing', () => {
1821
beforeEach( () => {
1922
return VirtualTestEditor
2023
.create( {
21-
plugins: [ BlockQuoteEditing, Paragraph ]
24+
plugins: [ BlockQuoteEditing, Paragraph, BoldEditing ]
2225
} )
2326
.then( newEditor => {
2427
editor = newEditor;
@@ -77,4 +80,65 @@ describe( 'BlockQuoteEditing', () => {
7780
expect( editor.getData() ).to.equal( '<blockquote><ul><li>xx</li></ul></blockquote>' );
7881
} );
7982
} );
83+
84+
it( 'should remove empty blockQuote elements', () => {
85+
setModelData( model, '<blockQuote></blockQuote><paragraph>Foo</paragraph>' );
86+
87+
expect( editor.getData() ).to.equal( '<p>Foo</p>' );
88+
} );
89+
90+
it( 'should remove blockQuotes which became empty', () => {
91+
setModelData( model, '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
92+
93+
model.change( writer => {
94+
const root = model.document.getRoot();
95+
const bq = root.getChild( 0 );
96+
97+
writer.remove( Range.createIn( bq ) );
98+
} );
99+
100+
expect( editor.getData() ).to.equal( '<p>&nbsp;</p>' ); // Autoparagraphed.
101+
} );
102+
103+
it( 'should unwrap a blockQuote if it was inserted into another blockQuote', () => {
104+
setModelData( model, '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
105+
106+
model.change( writer => {
107+
const root = model.document.getRoot();
108+
const bq = writer.createElement( 'blockQuote' );
109+
const p = writer.createElement( 'paragraph' );
110+
111+
writer.insertText( 'Bar', p, 0 ); // <p>Bar</p>.
112+
writer.insert( p, bq, 0 ); // <blockquote><p>Bar</p></blockquote>.
113+
writer.insert( bq, root.getChild( 0 ), 1 ); // Insert after <p>Foo</p>.
114+
} );
115+
116+
expect( editor.getData() ).to.equal( '<blockquote><p>Foo</p><p>Bar</p></blockquote>' );
117+
} );
118+
119+
it( 'should unwrap nested blockQuote if it was wrapped into another blockQuote', () => {
120+
setModelData( model, '<blockQuote><paragraph>Foo</paragraph></blockQuote><paragraph>Bar</paragraph>' );
121+
122+
model.change( writer => {
123+
const root = model.document.getRoot();
124+
125+
writer.wrap( Range.createIn( root ), 'blockQuote' );
126+
} );
127+
128+
expect( editor.getData() ).to.equal( '<blockquote><p>Foo</p><p>Bar</p></blockquote>' );
129+
} );
130+
131+
it( 'postfixer should do nothing on attribute change', () => {
132+
// This is strictly a 100% CC test.
133+
setModelData( model, '<blockQuote><paragraph>Foo</paragraph></blockQuote>' );
134+
135+
model.change( writer => {
136+
const root = model.document.getRoot();
137+
const p = root.getChild( 0 ).getChild( 0 );
138+
139+
writer.setAttribute( 'bold', true, Range.createIn( p ) );
140+
} );
141+
142+
expect( editor.getData() ).to.equal( '<blockquote><p><strong>Foo</strong></p></blockquote>' );
143+
} );
80144
} );

tests/integration.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,13 +383,14 @@ describe( 'BlockQuote integration', () => {
383383

384384
editor.execute( 'blockQuote' );
385385

386+
// Selection incorrectly trimmed.
386387
expect( getModelData( model ) ).to.equal(
387388
'<blockQuote>' +
388-
'<paragraph>fo[o</paragraph>' +
389+
'<paragraph>foo</paragraph>' +
389390
'<image src="foo.png">' +
390391
'<caption>xxx</caption>' +
391392
'</image>' +
392-
'<paragraph>b]ar</paragraph>' +
393+
'<paragraph>[b]ar</paragraph>' +
393394
'</blockQuote>'
394395
);
395396
} );

0 commit comments

Comments
 (0)