Sfoglia il codice sorgente

Integrated helpers to closing link balloon panel.

Oskar Wrobel 9 anni fa
parent
commit
6cc651ae35
2 ha cambiato i file con 55 aggiunte e 106 eliminazioni
  1. 32 51
      packages/ckeditor5-link/src/link.js
  2. 23 55
      packages/ckeditor5-link/tests/link.js

+ 32 - 51
packages/ckeditor5-link/src/link.js

@@ -9,6 +9,8 @@ import LinkEngine from './linkengine.js';
 import LinkElement from './linkelement.js';
 
 import Model from '../ui/model.js';
+import clickOutsideHandler from '../ui/bindings/clickoutsidehandler.js';
+import escPressHandler from '../ui/bindings/escpresshandler.js';
 
 import ButtonController from '../ui/button/button.js';
 import ButtonView from '../ui/button/buttonview.js';
@@ -16,8 +18,6 @@ import ButtonView from '../ui/button/buttonview.js';
 import LinkBalloonPanel from './ui/linkballoonpanel.js';
 import LinkBalloonPanelView from './ui/linkballoonpanelview.js';
 
-import { keyCodes } from '../utils/keyboard.js';
-
 /**
  * The link feature. It introduces the Link and Unlink buttons and the <kbd>Ctrl+K</kbd> keystroke.
  *
@@ -152,41 +152,12 @@ export default class Link extends Feature {
 			this._hidePanel( { focusEditable: true } );
 		} );
 
-		// Hide ballon panel after clicking on balloon panel `Cancel` button.
+		// Hide balloon panel after clicking on balloon panel `Cancel` button.
 		this.listenTo( panelModel, 'executeCancel', () => this._hidePanel( { focusEditable: true } ) );
 
-		// Handle `Ctrl+K` keystroke and show panel.
-		editor.keystrokes.set( 'CTRL+K', () => {
-			this._attachPanelToElement();
-			balloonPanel.urlInput.view.select();
-		} );
-
-		// Attach close by `Esc` press and click out of panel actions on panel show, on panel hide clean up listeners.
-		this.listenTo( balloonPanel.view.model, 'change:isVisible', ( evt, propertyName, value ) => {
-			if ( value ) {
-				// Handle close by `Esc`.
-				balloonPanel.view.listenTo( document, 'keydown', this._closePanelOnEsc.bind( this ) );
-
-				// Handle close by clicking out of the panel.
-				// Note that it is not handled by a `click` event, this is because clicking on link button or directly on link element
-				// opens and closes panel at the same time.
-				balloonPanel.view.listenTo( document, 'mouseup', ( evt, domEvt ) => {
-					// Do nothing when the panel was clicked.
-					if ( balloonPanel.view.element.contains( domEvt.target ) ) {
-						return;
-					}
-
-					// When click was out of the panel then hide it.
-					balloonPanel.view.hide();
-				} );
-			} else {
-				balloonPanel.view.stopListening( document );
-			}
-		} );
-
-		// Handle click on document and show panel when selection is placed inside the link element.
+		// Handle click on view document and show panel when selection is placed inside the link element.
 		// Keep panel open until selection will be inside the same link element.
-		viewDocument.on( 'click', () => {
+		this.listenTo( viewDocument, 'click', () => {
 			const viewSelection = viewDocument.selection;
 			const parentLink = getPositionParentLink( viewSelection.getFirstPosition() );
 
@@ -203,10 +174,34 @@ export default class Link extends Feature {
 					}
 				} );
 
-				this.listenTo( balloonPanel.view.model, 'change:isVisible', () => this.stopListening( viewDocument ) );
+				this.listenTo( balloonPanel.view.model, 'change:isVisible', () => this.stopListening( viewDocument, 'render' ) );
 			}
 		} );
 
+		// Apply closing actions.
+		const closingActionDefaultConfig = {
+			controller: balloonPanel.view,
+			model: balloonPanel.view.model,
+			activeIf: 'isVisible',
+		};
+
+		// Close on `ESC` press.
+		escPressHandler( Object.assign( {
+			action: () => this._hidePanel( { focusEditable: true } )
+		}, closingActionDefaultConfig ) );
+
+		// Close on click outside of balloon panel element.
+		clickOutsideHandler( Object.assign( {
+			contextElement: balloonPanel.view.element,
+			action: () => this._hidePanel()
+		}, closingActionDefaultConfig ) );
+
+		// Handle `Ctrl+K` keystroke and show panel.
+		editor.keystrokes.set( 'CTRL+K', () => {
+			this._attachPanelToElement();
+			balloonPanel.urlInput.view.select();
+		} );
+
 		// Append panel element to body.
 		editor.ui.add( 'body', balloonPanel );
 
@@ -256,24 +251,10 @@ export default class Link extends Feature {
 			this.editor.editing.view.focus();
 		}
 	}
-
-	/**
-	 * Hides balloon panel on `ESC` key press event and restores editor focus.
-	 *
-	 * **Note**: this method is `@protected` for testing purposes only.
-	 *
-	 * @protected
-	 * @param {utils.EventInfo} evt Information about the event.
-	 * @param {KeyboardEvent} domEvt DOM `keydown` event.
-	 */
-	_closePanelOnEsc( evt, domEvt ) {
-		if ( domEvt.keyCode == keyCodes.esc ) {
-			this._hidePanel( { focusEditable: true } );
-		}
-	}
 }
 
-// Get position parent LinkElement.
+// Try to find if one of the position parent ancestors is a LinkElement,
+// if yes return this element.
 //
 // @private
 // @param {engine.view.Position} position

+ 23 - 55
packages/ckeditor5-link/tests/link.js

@@ -223,64 +223,28 @@ describe( 'Link', () => {
 
 		describe( 'close listeners', () => {
 			describe( 'keyboard', () => {
-				it( 'should listen keyboard events when is open', () => {
-					const escCloseSpy = testUtils.sinon.spy( linkFeature, '_closePanelOnEsc' );
-
+				it( 'should close after `ESC` press', () => {
 					balloonPanel.view.model.isVisible = true;
-					document.dispatchEvent( new Event( 'keydown' ) );
-
-					expect( escCloseSpy.calledOnce ).to.true;
-				} );
-
-				it( 'should not listen keyboard events when is closed', () => {
-					const escCloseSpy = testUtils.sinon.spy( linkFeature, '_closePanelOnEsc' );
-
-					balloonPanel.view.model.isVisible = false;
-					document.dispatchEvent( new Event( 'keydown' ) );
 
-					expect( escCloseSpy.calledOnce ).to.false;
-				} );
-
-				it( 'should stop listening keyboard events after close', () => {
-					const escCloseSpy = testUtils.sinon.spy( linkFeature, '_closePanelOnEsc' );
-
-					balloonPanel.view.model.isVisible = true;
-					balloonPanel.view.model.isVisible = false;
+					dispatchKeyboardEvent( document, 'keydown', keyCodes.esc );
 
-					document.dispatchEvent( new Event( 'keydown' ) );
-
-					expect( escCloseSpy.notCalled ).to.true;
+					expect( hidePanelSpy.calledOnce ).to.true;
+					expect( focusEditableSpy.calledOnce ).to.true;
 				} );
 			} );
 
 			describe( 'mouse', () => {
-				it( 'should not close on click inside the panel', () => {
-					balloonPanel.view.model.isVisible = true;
-					balloonPanel.view.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
-
-					expect( hidePanelSpy.notCalled ).to.true;
-				} );
-
 				it( 'should close and not focus editable on click outside the panel', () => {
 					balloonPanel.view.model.isVisible = true;
-					document.dispatchEvent( new Event( 'mouseup' ) );
+					document.body.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
 
 					expect( hidePanelSpy.calledOnce ).to.true;
 					expect( focusEditableSpy.notCalled ).to.true;
 				} );
 
-				it( 'should not listen mouse events before open', () => {
-					balloonPanel.view.model.isVisible = false;
-					document.dispatchEvent( new Event( 'mouseup' ) );
-
-					expect( hidePanelSpy.notCalled ).to.true;
-				} );
-
-				it( 'should stop listening mouse events after closed', () => {
+				it( 'should not close on click inside the panel', () => {
 					balloonPanel.view.model.isVisible = true;
-					balloonPanel.view.model.isVisible = false;
-
-					document.dispatchEvent( new Event( 'mouseup' ) );
+					balloonPanel.view.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
 
 					expect( hidePanelSpy.notCalled ).to.true;
 				} );
@@ -424,17 +388,21 @@ describe( 'Link', () => {
 				expect( balloonPanel.view.model.isVisible ).to.false;
 			} );
 		} );
-
-		describe( '_closePanelOnEsc', () => {
-			it( 'should hide panel and focus editable on ESC press', () => {
-				const eventInfo = {};
-				const domEvent = { keyCode: 27 };
-
-				linkFeature._closePanelOnEsc( eventInfo, domEvent );
-
-				expect( hidePanelSpy.calledOnce ).to.true;
-				expect( focusEditableSpy.calledOnce ).to.true;
-			} );
-		} );
 	} );
 } );
+
+// Creates and dispatches keyboard event with specified keyCode.
+//
+// @private
+// @param {EventTarget} eventTarget
+// @param {String} eventName
+// @param {Number} keyCode
+function dispatchKeyboardEvent( element, eventName, keyCode ) {
+	const event = document.createEvent( 'Events' );
+
+	event.initEvent( eventName, true, true );
+	event.which = keyCode;
+	event.keyCode = keyCode;
+
+	element.dispatchEvent( event );
+}