diff --git a/src/view/renderer.js b/src/view/renderer.js index a16adc5a2..83de61120 100644 --- a/src/view/renderer.js +++ b/src/view/renderer.js @@ -306,7 +306,9 @@ export default class Renderer { // It may also happen that 'newViewChild' mapping is not present since its parent mapping // was already removed (the 'domConverter.unbindDomElement()' method also unbinds children // mappings) so we also check for '!newViewChild'. - if ( !newViewChild || newViewChild && !newViewChild.isSimilar( viewElement ) ) { + // Also check if new element ('newViewChild') was marked to have its attributes rerenderd, + // if so, marked reused view element too (#1560). + if ( !newViewChild || newViewChild && !newViewChild.isSimilar( viewElement ) || this.markedAttributes.has( newViewChild ) ) { this.markedAttributes.add( viewElement ); } diff --git a/tests/view/renderer.js b/tests/view/renderer.js index 68c5c8f8b..5bf88a609 100644 --- a/tests/view/renderer.js +++ b/tests/view/renderer.js @@ -17,6 +17,7 @@ import DocumentSelection from '../../src/view/documentselection'; import DomConverter from '../../src/view/domconverter'; import Renderer from '../../src/view/renderer'; import DocumentFragment from '../../src/view/documentfragment'; +import DowncastWriter from '../../src/view/downcastwriter'; import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror'; import { parse, setData as setViewData, getData as getViewData } from '../../src/dev-utils/view'; import { INLINE_FILLER, INLINE_FILLER_LENGTH, isBlockFiller, BR_FILLER } from '../../src/view/filler'; @@ -3038,6 +3039,68 @@ describe( 'Renderer', () => { expect( domRoot.innerHTML ).to.equal( '

FooBar

' ); } ); } ); + + // #1560 + describe( 'attributes manipulation on replaced element', () => { + it( 'should rerender element if it was removed after having its attributes removed (attribute)', () => { + const writer = new DowncastWriter(); + + // 1. Setup initial view/DOM. + viewRoot._appendChild( parse( '1' ) ); + + const viewP = viewRoot.getChild( 0 ); + + writer.setAttribute( 'data-placeholder', 'Body', viewP ); + + renderer.markToSync( 'children', viewRoot ); + renderer.render(); + + expect( domRoot.innerHTML ).to.equal( '

1

' ); + + // 2. Modify view. + writer.removeAttribute( 'data-placeholder', viewP ); + + viewRoot._removeChildren( 0, viewRoot.childCount ); + + viewRoot._appendChild( parse( '12' ) ); + + renderer.markToSync( 'attributes', viewP ); + renderer.markToSync( 'children', viewRoot ); + renderer.render(); + + expect( domRoot.innerHTML ).to.equal( '

1

2

' ); + } ); + + it( 'should rerender element if it was removed after having its attributes removed (classes)', () => { + const writer = new DowncastWriter(); + + // 1. Setup initial view/DOM. + viewRoot._appendChild( parse( 'h1p' ) ); + + const viewP = viewRoot.getChild( 1 ); + + writer.addClass( [ 'cke-test1', 'cke-test2' ], viewP ); + + renderer.markToSync( 'children', viewRoot ); + renderer.render(); + + expect( domRoot.innerHTML ).to.equal( '

h1

p

' ); + + // 2. Modify view. + writer.removeClass( 'cke-test2', viewP ); + + viewRoot._removeChildren( 0, viewRoot.childCount ); + + viewRoot._appendChild( parse( 'h1' + + 'pp2' ) ); + + renderer.markToSync( 'attributes', viewP ); + renderer.markToSync( 'children', viewRoot ); + renderer.render(); + + expect( domRoot.innerHTML ).to.equal( '

h1

p

p2

' ); + } ); + } ); } ); describe( '#922', () => {