8
0
Эх сурвалжийг харах

Observers will not fire DOM events from inside of UIElements.

Szymon Kupś 8 жил өмнө
parent
commit
0ddd6350fd

+ 26 - 20
packages/ckeditor5-engine/src/view/domconverter.js

@@ -353,8 +353,8 @@ export default class DomConverter {
 			return null;
 			return null;
 		}
 		}
 
 
-		// When node is inside UIElement it should not be converted to view.
-		if ( isInsideUIElement( domNode, this._domToViewMapping ) ) {
+		// When node is inside UIElement it has no representation in the view.
+		if ( this.isDomInsideUIlement( domNode ) ) {
 			return null;
 			return null;
 		}
 		}
 
 
@@ -837,6 +837,30 @@ export default class DomConverter {
 		return backward;
 		return backward;
 	}
 	}
 
 
+	/**
+	 * Returns true if `domNode` is placed inside {@link module:engine/view/uielement~UIElement}/
+	 *
+	 * @param {Node} domNode
+	 * @return {Boolean}
+	 */
+	isDomInsideUIlement( domNode ) {
+		const ancestors = getAncestors( domNode );
+
+		// Remove domNode from the list.
+		ancestors.pop();
+
+		while ( ancestors.length ) {
+			const domNode = ancestors.pop();
+			const viewNode = this._domToViewMapping.get( domNode );
+
+			if ( viewNode && viewNode.is( 'uiElement' ) ) {
+				return true;
+			}
+		}
+
+		return false;
+	}
+
 	/**
 	/**
 	 * Takes text data from given {@link module:engine/view/text~Text#data} and processes it so it is correctly displayed in DOM.
 	 * Takes text data from given {@link module:engine/view/text~Text#data} and processes it so it is correctly displayed in DOM.
 	 *
 	 *
@@ -1094,21 +1118,3 @@ function forEachDomNodeAncestor( node, callback ) {
 		node = node.parentNode;
 		node = node.parentNode;
 	}
 	}
 }
 }
-
-function isInsideUIElement( domNode, domToViewMapping ) {
-	const ancestors = getAncestors( domNode );
-
-	// Remove domNode from the list.
-	ancestors.pop();
-
-	while ( ancestors.length ) {
-		const domNode = ancestors.pop();
-		const viewNode = domToViewMapping.get( domNode );
-
-		if ( viewNode && viewNode.is( 'uiElement' ) ) {
-			return true;
-		}
-	}
-
-	return false;
-}

+ 4 - 1
packages/ckeditor5-engine/src/view/observer/domeventobserver.js

@@ -75,7 +75,10 @@ export default class DomEventObserver extends Observer {
 
 
 		types.forEach( type => {
 		types.forEach( type => {
 			this.listenTo( domElement, type, ( eventInfo, domEvent ) => {
 			this.listenTo( domElement, type, ( eventInfo, domEvent ) => {
-				if ( this.isEnabled ) {
+				const domConverter = this.document.domConverter;
+
+				// Block all events if observer is disabled and events from inside of UIElement.
+				if ( this.isEnabled && !domConverter.isDomInsideUIlement( domEvent.target ) ) {
 					this.onDomEvent( domEvent );
 					this.onDomEvent( domEvent );
 				}
 				}
 			}, { useCapture: this.useCapture } );
 			}, { useCapture: this.useCapture } );

+ 41 - 0
packages/ckeditor5-engine/tests/view/observer/domeventobserver.js

@@ -8,6 +8,7 @@
 import DomEventObserver from '../../../src/view/observer/domeventobserver';
 import DomEventObserver from '../../../src/view/observer/domeventobserver';
 import Observer from '../../../src/view/observer/observer';
 import Observer from '../../../src/view/observer/observer';
 import ViewDocument from '../../../src/view/document';
 import ViewDocument from '../../../src/view/document';
+import UIElement from '../../../src/view/uielement';
 
 
 class ClickObserver extends DomEventObserver {
 class ClickObserver extends DomEventObserver {
 	constructor( document ) {
 	constructor( document ) {
@@ -153,6 +154,46 @@ describe( 'DomEventObserver', () => {
 		childDomElement.dispatchEvent( domEvent );
 		childDomElement.dispatchEvent( domEvent );
 	} );
 	} );
 
 
+	describe( 'integration with UIElement', () => {
+		let domRoot, domEvent, evtSpy;
+
+		class MyUIElement extends UIElement {
+			render( domDocument ) {
+				const root = super.render( domDocument );
+				root.innerHTML = '<span>foo bar</span>';
+
+				return root;
+			}
+		}
+
+		beforeEach( () => {
+			domRoot = document.createElement( 'div' );
+			const viewRoot = viewDocument.createRoot( domRoot, 'root' );
+			const uiElement = new MyUIElement( 'p' );
+			viewRoot.appendChildren( uiElement );
+			viewDocument.render();
+
+			domEvent = new MouseEvent( 'click', { bubbles: true } );
+			evtSpy = sinon.spy();
+			viewDocument.addObserver( ClickObserver );
+			viewDocument.on( 'click', evtSpy );
+		} );
+
+		it( 'should fire events from UIElement itself', () => {
+			const domUiElement = domRoot.childNodes[ 0 ];
+			domUiElement.dispatchEvent( domEvent );
+
+			expect( evtSpy.calledOnce ).to.be.true;
+		} );
+
+		it( 'should block events from inside of UIElement', () => {
+			const domUiElementChild = domRoot.querySelector( 'span' );
+			domUiElementChild.dispatchEvent( domEvent );
+
+			expect( evtSpy.calledOnce ).to.be.false;
+		} );
+	} );
+
 	describe( 'fire', () => {
 	describe( 'fire', () => {
 		it( 'should do nothing if observer is disabled', () => {
 		it( 'should do nothing if observer is disabled', () => {
 			const testObserver = new ClickObserver( viewDocument );
 			const testObserver = new ClickObserver( viewDocument );