Procházet zdrojové kódy

Merge pull request #58 from ckeditor/t/57

Refactoring after UI architecture refactoring.
Piotrek Koszuliński před 9 roky
rodič
revize
a5e68e78b6

+ 62 - 74
packages/ckeditor5-link/src/link.js

@@ -8,17 +8,12 @@ import ClickObserver from '../engine/view/observer/clickobserver.js';
 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 Button from '../ui/button/button.js';
 import ButtonView from '../ui/button/buttonview.js';
-
-import BalloonPanel from '../ui/balloonpanel/balloonpanel.js';
 import BalloonPanelView from '../ui/balloonpanel/balloonpanelview.js';
 
-import LinkForm from './ui/linkform.js';
 import LinkFormView from './ui/linkformview.js';
 
 /**
@@ -44,18 +39,18 @@ export default class Link extends Feature {
 		this.editor.editing.view.addObserver( ClickObserver );
 
 		/**
-		 * Balloon panel component to display the main UI.
+		 * Balloon panel view to display the main UI.
 		 *
-		 * @member {link.ui.LinkBalloonPanel} link.Link#balloonPanel
+		 * @member {link.ui.LinkBalloonPanelView} link.Link#balloonPanelView
 		 */
-		this.balloonPanel = this._createBalloonPanel();
+		this.balloonPanelView = this._createBalloonPanel();
 
 		/**
-		 * The form component inside {@link link.Link#balloonPanel}.
+		 * The form view inside {@link link.Link#balloonPanelView}.
 		 *
-		 * @member {link.ui.LinkForm} link.Link#form
+		 * @member {link.ui.LinkFormView} link.Link#formView
 		 */
-		this.form = this._createForm();
+		this.formView = this._createForm();
 
 		// Create toolbar buttons.
 		this._createToolbarLinkButton();
@@ -73,26 +68,26 @@ export default class Link extends Feature {
 		const linkCommand = editor.commands.get( 'link' );
 		const t = editor.t;
 
-		// Create button model.
-		const linkButtonModel = new Model( {
-			isEnabled: true,
-			isOn: false,
-			label: t( 'Link' ),
-			icon: 'link',
-			keystroke: 'CTRL+K'
-		} );
+		// Handle `Ctrl+K` keystroke and show panel.
+		editor.keystrokes.set( 'CTRL+K', () => this._showPanel() );
+
+		// Add link button to feature components.
+		editor.ui.featureComponents.add( 'link', ( locale ) => {
+			const button = new ButtonView( locale );
 
-		// Bind button model to the command.
-		linkButtonModel.bind( 'isEnabled' ).to( linkCommand, 'isEnabled' );
+			button.isEnabled = true;
+			button.label = t( 'Link' );
+			button.icon = 'link';
+			button.keystroke = 'CTRL+K';
 
-		// Show the panel on button click only when editor is focused.
-		this.listenTo( linkButtonModel, 'execute', () => this._showPanel() );
+			// Bind button to the command.
+			button.bind( 'isEnabled' ).to( linkCommand, 'isEnabled' );
 
-		// Add link button to feature components.
-		editor.ui.featureComponents.add( 'link', Button, ButtonView, linkButtonModel );
+			// Show the panel on button click.
+			this.listenTo( button, 'execute', () => this._showPanel() );
 
-		// Handle `Ctrl+K` keystroke and show panel.
-		editor.keystrokes.set( 'CTRL+K', () => this._showPanel() );
+			return button;
+		} );
 	}
 
 	/**
@@ -106,24 +101,22 @@ export default class Link extends Feature {
 		const t = editor.t;
 		const unlinkCommand = editor.commands.get( 'unlink' );
 
-		// Create the button model.
-		const unlinkButtonModel = new Model( {
-			isEnabled: false,
-			isOn: false,
-			label: t( 'Unlink' ),
-			icon: 'unlink'
-		} );
+		// Add unlink button to feature components.
+		editor.ui.featureComponents.add( 'unlink', ( locale ) => {
+			const button = new ButtonView( locale );
 
-		// Bind button model to the command.
-		unlinkButtonModel.bind( 'isEnabled' ).to( unlinkCommand, 'isEnabled' );
+			button.isEnabled = false;
+			button.label = t( 'Unlink' );
+			button.icon = 'unlink';
 
-		// Execute unlink command and hide panel, if open.
-		this.listenTo( unlinkButtonModel, 'execute', () => {
-			editor.execute( 'unlink' );
-		} );
+			// Bind button to the command.
+			button.bind( 'isEnabled' ).to( unlinkCommand, 'isEnabled' );
 
-		// Add unlink button to feature components.
-		editor.ui.featureComponents.add( 'unlink', Button, ButtonView, unlinkButtonModel );
+			// Execute unlink command and hide panel, if open on button click.
+			this.listenTo( button, 'execute', () => editor.execute( 'unlink' ) );
+
+			return button;
+		} );
 	}
 
 	/**
@@ -137,16 +130,13 @@ export default class Link extends Feature {
 		const viewDocument = editor.editing.view;
 
 		// Create the balloon panel instance.
-		const balloonPanel = new BalloonPanel(
-			new Model( {
-				maxWidth: 300
-			} ),
-			new BalloonPanelView( editor.locale )
-		);
+		const balloonPanelView = new BalloonPanelView( editor.locale );
+
+		balloonPanelView.maxWidth = 300;
 
 		// Add balloonPanel.view#element to FocusTracker.
 		// @TODO: Do it automatically ckeditor5-core#23
-		editor.focusTracker.add( balloonPanel.view.element );
+		editor.focusTracker.add( balloonPanelView.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.
@@ -167,30 +157,30 @@ export default class Link extends Feature {
 					}
 				} );
 
-				this.listenTo( balloonPanel.view, 'change:isVisible', () => this.stopListening( viewDocument, 'render' ) );
+				this.listenTo( balloonPanelView, 'change:isVisible', () => this.stopListening( viewDocument, 'render' ) );
 			}
 		} );
 
 		// Close on `ESC` press.
 		escPressHandler( {
-			controller: balloonPanel.view,
-			model: balloonPanel.view,
+			emitter: balloonPanelView,
+			model: balloonPanelView,
 			activeIf: 'isVisible',
 			callback: () => this._hidePanel( true )
 		} );
 
 		// Close on click outside of balloon panel element.
 		clickOutsideHandler( {
-			controller: balloonPanel.view,
-			model: balloonPanel.view,
+			emitter: balloonPanelView,
+			model: balloonPanelView,
 			activeIf: 'isVisible',
-			contextElement: balloonPanel.view.element,
+			contextElement: balloonPanelView.element,
 			callback: () => this._hidePanel()
 		} );
 
-		editor.ui.add( 'body', balloonPanel );
+		editor.ui.body.add( balloonPanelView );
 
-		return balloonPanel;
+		return balloonPanelView;
 	}
 
 	/**
@@ -201,30 +191,28 @@ export default class Link extends Feature {
 	 */
 	_createForm() {
 		const editor = this.editor;
-		const formModel = new Model();
-
-		formModel.bind( 'url' ).to( editor.commands.get( 'link' ), 'value' );
+		const formView = new LinkFormView( editor.locale );
 
-		const form = new LinkForm( formModel, new LinkFormView( editor.locale ) );
+		formView.urlInputView.bind( 'value' ).to( editor.commands.get( 'link' ), 'value' );
 
-		// Execute link command after clicking on balloon panel `Link` button.
-		this.listenTo( formModel, 'submit', () => {
-			editor.execute( 'link', this.form.urlInput.value );
+		// Execute link command after clicking on formView `Save` button.
+		this.listenTo( formView, 'submit', () => {
+			editor.execute( 'link', formView.urlInputView.value );
 			this._hidePanel( true );
 		} );
 
-		// Execute unlink command after clicking on balloon panel `Unlink` button.
-		this.listenTo( formModel, 'unlink', () => {
+		// Execute unlink command after clicking on formView `Unlink` button.
+		this.listenTo( formView, 'unlink', () => {
 			editor.execute( 'unlink' );
 			this._hidePanel( true );
 		} );
 
-		// Hide balloon panel after clicking on balloon panel `Cancel` button.
-		this.listenTo( formModel, 'cancel', () => this._hidePanel( true ) );
+		// Hide balloon panel after clicking on formView `Cancel` button.
+		this.listenTo( formView, 'cancel', () => this._hidePanel( true ) );
 
-		this.balloonPanel.add( 'content', form );
+		this.balloonPanelView.content.add( formView );
 
-		return form;
+		return formView;
 	}
 
 	/**
@@ -242,14 +230,14 @@ export default class Link extends Feature {
 
 		// When selection is inside link element, then attach panel to this element.
 		if ( targetLink ) {
-			this.balloonPanel.view.attachTo(
+			this.balloonPanelView.attachTo(
 				viewDocument.domConverter.getCorrespondingDomElement( targetLink ),
 				domEditableElement
 			);
 		}
 		// Otherwise attach panel to the selection.
 		else {
-			this.balloonPanel.view.attachTo(
+			this.balloonPanelView.attachTo(
 				viewDocument.domConverter.viewRangeToDom( viewDocument.selection.getFirstRange() ),
 				domEditableElement
 			);
@@ -263,7 +251,7 @@ export default class Link extends Feature {
 	 * @param {Boolean} [focusEditable=false] When `true` then editable focus will be restored on panel hide.
 	 */
 	_hidePanel( focusEditable ) {
-		this.balloonPanel.view.hide();
+		this.balloonPanelView.hide();
 
 		if ( focusEditable ) {
 			this.editor.editing.view.focus();
@@ -277,7 +265,7 @@ export default class Link extends Feature {
 	 */
 	_showPanel() {
 		this._attachPanelToElement();
-		this.form.urlInput.view.select();
+		this.formView.urlInputView.select();
 	}
 }
 

+ 0 - 134
packages/ckeditor5-link/src/ui/linkform.js

@@ -1,134 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-import Model from '../../ui/model.js';
-import Controller from '../../ui/controller.js';
-
-import Button from '../../ui/button/button.js';
-import LabeledInput from '../../ui/labeledinput/labeledinput.js';
-import InputText from '../../ui/inputtext/inputtext.js';
-
-/**
- * The link form class.
- *
- *		new LinkForm( new Model(), new LinkFormView() );
- *
- * See {@link link.ui.LinkFormView}.
- *
- * @memberOf link.ui
- * @extends ui.Controller
- */
-export default class LinkForm extends Controller {
-	/**
-	 * Creates an instance of {@link link.ui.LinkForm} class.
-	 *
-	 * @param {link.ui.LinkFormModel} model Model of this link form.
-	 * @param {ui.View} view View of this link form.
-	 */
-	constructor( model, view ) {
-		super( model, view );
-
-		const t = this.view.t;
-		const urlInputModel = new Model( {
-			label: t( 'Link URL' )
-		} );
-
-		// Bind LabeledInputModel#value to LinkFormModel#url.
-		urlInputModel.bind( 'value' ).to( model, 'url' );
-
-		/**
-		 * The URL input inside {@link link.ui.LinkForm}.
-		 *
-		 * @member {ui.input.labeled.LabeledInput} link.ui.LinkForm#urlInput
-		 */
-		this.urlInput = new LabeledInput( urlInputModel, view.urlInputView, InputText, new Model() );
-
-		/**
-		 * The save button inside {@link link.ui.LinkForm}.
-		 *
-		 * @member {ui.button.Button} link.ui.LinkForm#saveButton
-		 */
-		this.saveButton = new Button( new Model( {
-			isEnabled: true,
-			isOn: false,
-			label: t( 'Save' ),
-			withText: true,
-			type: 'submit'
-		} ), view.saveButtonView );
-
-		/**
-		 * The cancel button inside {@link link.ui.LinkForm}.
-		 *
-		 * @member {ui.button.Button} link.ui.LinkForm#cancelButton
-		 */
-		this.cancelButton = new Button( new Model( {
-			isEnabled: true,
-			isOn: false,
-			label: t( 'Cancel' ),
-			withText: true
-		} ), view.cancelButtonView );
-
-		/**
-		 * The unlink button inside {@link link.ui.LinkForm}.
-		 *
-		 * @member {ui.button.Button} link.ui.LinkForm#unlinkButton
-		 */
-		this.unlinkButton = new Button( new Model( {
-			isEnabled: true,
-			isOn: false,
-			label: t( 'Unlink' ),
-			icon: 'unlink'
-		} ), view.unlinkButtonView );
-
-		view.delegate( 'submit' ).to( model );
-
-		// TODO: Delegate event with changed name.
-		this.listenTo( this.cancelButton.model, 'execute', () => {
-			this.model.fire( 'cancel' );
-		} );
-
-		this.listenTo( this.unlinkButton.model, 'execute', () => {
-			this.model.fire( 'unlink' );
-		} );
-
-		// TODO: add() should accept multiple items.
-		this.add( this.urlInput );
-		this.add( this.saveButton );
-		this.add( this.cancelButton );
-		this.add( this.unlinkButton );
-	}
-}
-
-/**
- * The link form component {@link ui.Model model} interface.
- *
- * @interface link.ui.LinkFormModel
- */
-
-/**
- * The url value as in {@link link.ui.LinkForm#urlInput}.
- *
- * @observable
- * @member {String} link.ui.LinkFormModel#url
- */
-
-/**
- * Fired when the view is submitted by the user, i.e. when
- * the {@link link.ui.LinkForm#saveButton} has been executed.
- *
- * @event link.ui.LinkFormModel#submit
- */
-
-/**
- * Fired when the {@link link.ui.LinkForm#cancelButton} has been executed.
- *
- * @event link.ui.LinkFormModel#cancel
- */
-
-/**
- * Fired when the {@link link.ui.LinkForm#unlinkButton} has been executed.
- *
- * @event link.ui.LinkFormModel#unlink
- */

+ 51 - 4
packages/ckeditor5-link/src/ui/linkformview.js

@@ -32,28 +32,37 @@ export default class LinkFormView extends View {
 		 *
 		 * @member {ui.input.labeled.LabeledInputView} link.ui.LinkFormView#urlInputView
 		 */
-		this.urlInputView = new LabeledInputView( InputTextView, locale );
+		this.urlInputView = this._createUrlInput();
 
 		/**
 		 * The save button view.
 		 *
 		 * @member {ui.button.ButtonView} link.ui.LinkFormView#saveButtonView
 		 */
-		this.saveButtonView = new ButtonView( locale );
+		this.saveButtonView = this._createButton( 'Save' );
+		this.saveButtonView.type = 'submit';
 
 		/**
 		 * The cancel button view.
 		 *
 		 * @member {ui.button.ButtonView} link.ui.LinkFormView#cancelButtonView
 		 */
-		this.cancelButtonView = new ButtonView( locale );
+		this.cancelButtonView = this._createButton( 'Cancel', 'cancel' );
 
 		/**
 		 * The unlink button view.
 		 *
 		 * @member {ui.button.ButtonView} link.ui.LinkFormView#unlinkButtonView
 		 */
-		this.unlinkButtonView = new ButtonView( locale );
+		this.unlinkButtonView = this._createButton( 'Unlink', 'unlink' );
+
+		// Register child views.
+		this.addChild(
+			this.urlInputView,
+			this.saveButtonView,
+			this.cancelButtonView,
+			this.unlinkButtonView
+		);
 
 		Template.extend( this.saveButtonView.template, {
 			attributes: {
@@ -96,6 +105,44 @@ export default class LinkFormView extends View {
 			view: this
 		} );
 	}
+
+	/**
+	 * Create labeled input view.
+	 *
+	 * @private
+	 * @returns {ui.labeled.LabeledInputView} Labeled input view instance.
+	 */
+	_createUrlInput() {
+		const t = this.locale.t;
+
+		const labeledInput = new LabeledInputView( this.locale, InputTextView );
+
+		labeledInput.label = t( 'Link URL' );
+
+		return labeledInput;
+	}
+
+	/**
+	 * Creates button View.
+	 *
+	 * @private
+	 * @param {String} label Button label
+	 * @param {String} [event] Event name which ButtonView#execute event will be delegated to.
+	 * @returns {ui.button.ButtonView} Button view instance.
+	 */
+	_createButton( label, event ) {
+		const t = this.locale.t;
+		const button = new ButtonView( this.locale );
+
+		button.label = t( label );
+		button.withText = true;
+
+		if ( event ) {
+			this.listenTo( button, 'execute', () => this.fire( event ) );
+		}
+
+		return button;
+	}
 }
 
 /**

+ 65 - 68
packages/ckeditor5-link/tests/link.js

@@ -12,8 +12,8 @@ import { setData as setModelData } from 'ckeditor5/engine/dev-utils/model.js';
 
 import Link from 'ckeditor5/link/link.js';
 import LinkEngine from 'ckeditor5/link/linkengine.js';
-import Button from 'ckeditor5/ui/button/button.js';
-import BalloonPanel from 'ckeditor5/ui/balloonpanel/balloonpanel.js';
+import ButtonView from 'ckeditor5/ui/button/buttonview.js';
+import BalloonPanelView from 'ckeditor5/ui/balloonpanel/balloonpanelview.js';
 
 import Range from 'ckeditor5/engine/view/range.js';
 import ClickObserver from 'ckeditor5/engine/view/observer/clickobserver.js';
@@ -21,7 +21,7 @@ import ClickObserver from 'ckeditor5/engine/view/observer/clickobserver.js';
 testUtils.createSinonSandbox();
 
 describe( 'Link', () => {
-	let editor, linkFeature, linkButton, unlinkButton, balloonPanel, form, editorElement;
+	let editor, linkFeature, linkButton, unlinkButton, balloonPanelView, formView, editorElement;
 
 	beforeEach( () => {
 		editorElement = document.createElement( 'div' );
@@ -38,8 +38,8 @@ describe( 'Link', () => {
 			linkFeature = editor.plugins.get( Link );
 			linkButton = editor.ui.featureComponents.create( 'link' );
 			unlinkButton = editor.ui.featureComponents.create( 'unlink' );
-			balloonPanel = linkFeature.balloonPanel;
-			form = linkFeature.form;
+			balloonPanelView = linkFeature.balloonPanelView;
+			formView = linkFeature.formView;
 		} );
 	} );
 
@@ -61,34 +61,33 @@ describe( 'Link', () => {
 
 	describe( 'link toolbar button', () => {
 		it( 'should register link feature component', () => {
-			expect( linkButton ).to.instanceOf( Button );
+			expect( linkButton ).to.instanceOf( ButtonView );
 		} );
 
-		it( 'should bind linkButton#model to link command', () => {
-			const model = linkButton.model;
+		it( 'should bind linkButtonView to link command', () => {
 			const command = editor.commands.get( 'link' );
 
 			command.isEnabled = true;
-			expect( model.isEnabled ).to.be.true;
+			expect( linkButton.isEnabled ).to.be.true;
 
 			command.isEnabled = false;
-			expect( model.isEnabled ).to.be.false;
+			expect( linkButton.isEnabled ).to.be.false;
 		} );
 
-		it( 'should open panel on linkButton#model execute event', () => {
-			linkButton.model.fire( 'execute' );
+		it( 'should open panel on linkButtonView execute event', () => {
+			linkButton.fire( 'execute' );
 
-			expect( linkFeature.balloonPanel.view.isVisible ).to.true;
+			expect( linkFeature.balloonPanelView.isVisible ).to.true;
 		} );
 
 		it( 'should open panel attached to the link element, when collapsed selection is inside link element', () => {
-			const attachToSpy = testUtils.sinon.spy( balloonPanel.view, 'attachTo' );
+			const attachToSpy = testUtils.sinon.spy( balloonPanelView, 'attachTo' );
 
 			editor.document.schema.allow( { name: '$text', inside: '$root' } );
 			setModelData( editor.document, '<$text linkHref="url">some[] url</$text>' );
 			editor.editing.view.isFocused = true;
 
-			linkButton.model.fire( 'execute' );
+			linkButton.fire( 'execute' );
 
 			const linkElement = editorElement.querySelector( 'a' );
 
@@ -96,13 +95,13 @@ describe( 'Link', () => {
 		} );
 
 		it( 'should open panel attached to the selection, when there is non-collapsed selection', () => {
-			const attachToSpy = testUtils.sinon.spy( balloonPanel.view, 'attachTo' );
+			const attachToSpy = testUtils.sinon.spy( balloonPanelView, 'attachTo' );
 
 			editor.document.schema.allow( { name: '$text', inside: '$root' } );
 			setModelData( editor.document, 'so[me ur]l' );
 			editor.editing.view.isFocused = true;
 
-			linkButton.model.fire( 'execute' );
+			linkButton.fire( 'execute' );
 
 			const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
 
@@ -110,11 +109,11 @@ describe( 'Link', () => {
 		} );
 
 		it( 'should select panel input value when panel is opened', () => {
-			const selectUrlInputSpy = testUtils.sinon.spy( linkFeature.form.urlInput.view, 'select' );
+			const selectUrlInputSpy = testUtils.sinon.spy( linkFeature.formView.urlInputView, 'select' );
 
 			editor.editing.view.isFocused = true;
 
-			linkButton.model.fire( 'execute' );
+			linkButton.fire( 'execute' );
 
 			expect( selectUrlInputSpy.calledOnce ).to.true;
 		} );
@@ -122,24 +121,23 @@ describe( 'Link', () => {
 
 	describe( 'unlink toolbar button', () => {
 		it( 'should register unlink feature component', () => {
-			expect( unlinkButton ).to.instanceOf( Button );
+			expect( unlinkButton ).to.instanceOf( ButtonView );
 		} );
 
-		it( 'should bind unlinkButton#model to unlink command', () => {
-			const model = unlinkButton.model;
+		it( 'should bind unlinkButtonView to unlink command', () => {
 			const command = editor.commands.get( 'unlink' );
 
 			command.isEnabled = true;
-			expect( model.isEnabled ).to.be.true;
+			expect( unlinkButton.isEnabled ).to.be.true;
 
 			command.isEnabled = false;
-			expect( model.isEnabled ).to.be.false;
+			expect( unlinkButton.isEnabled ).to.be.false;
 		} );
 
-		it( 'should execute unlink command on unlinkButton#model execute event', () => {
+		it( 'should execute unlink command on unlinkButtonView execute event', () => {
 			const executeSpy = testUtils.sinon.spy( editor, 'execute' );
 
-			unlinkButton.model.fire( 'execute' );
+			unlinkButton.fire( 'execute' );
 
 			expect( executeSpy.calledOnce ).to.true;
 			expect( executeSpy.calledWithExactly( 'unlink' ) ).to.true;
@@ -150,31 +148,31 @@ describe( 'Link', () => {
 		let hidePanelSpy, focusEditableSpy;
 
 		beforeEach( () => {
-			hidePanelSpy = testUtils.sinon.spy( balloonPanel.view, 'hide' );
+			hidePanelSpy = testUtils.sinon.spy( balloonPanelView, 'hide' );
 			focusEditableSpy = testUtils.sinon.spy( editor.editing.view, 'focus' );
 		} );
 
 		it( 'should be created', () => {
-			expect( balloonPanel ).to.instanceOf( BalloonPanel );
+			expect( balloonPanelView ).to.instanceOf( BalloonPanelView );
 		} );
 
 		it( 'should be appended to the document body', () => {
-			expect( document.body.contains( balloonPanel.view.element ) );
+			expect( document.body.contains( balloonPanelView.element ) );
 		} );
 
 		it( 'should open with selected url input on `CTRL+K` keystroke', () => {
-			const selectUrlInputSpy = testUtils.sinon.spy( linkFeature.form.urlInput.view, 'select' );
+			const selectUrlInputSpy = testUtils.sinon.spy( linkFeature.formView.urlInputView, 'select' );
 
 			editor.keystrokes.press( { keyCode: keyCodes.k, ctrlKey: true } );
 
-			expect( balloonPanel.view.isVisible ).to.true;
+			expect( balloonPanelView.isVisible ).to.true;
 			expect( selectUrlInputSpy.calledOnce ).to.true;
 		} );
 
 		it( 'should add balloon panel element to focus tracker', () => {
 			editor.focusTracker.isFocused = false;
 
-			balloonPanel.view.element.dispatchEvent( new Event( 'focus' ) );
+			balloonPanelView.element.dispatchEvent( new Event( 'focus' ) );
 
 			expect( editor.focusTracker.isFocused ).to.true;
 		} );
@@ -182,7 +180,7 @@ describe( 'Link', () => {
 		describe( 'close listeners', () => {
 			describe( 'keyboard', () => {
 				it( 'should close after `ESC` press', () => {
-					balloonPanel.view.isVisible = true;
+					balloonPanelView.isVisible = true;
 
 					dispatchKeyboardEvent( document, 'keydown', keyCodes.esc );
 
@@ -193,7 +191,7 @@ describe( 'Link', () => {
 
 			describe( 'mouse', () => {
 				it( 'should close and not focus editable on click outside the panel', () => {
-					balloonPanel.view.isVisible = true;
+					balloonPanelView.isVisible = true;
 					document.body.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
 
 					expect( hidePanelSpy.calledOnce ).to.true;
@@ -201,8 +199,8 @@ describe( 'Link', () => {
 				} );
 
 				it( 'should not close on click inside the panel', () => {
-					balloonPanel.view.isVisible = true;
-					balloonPanel.view.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
+					balloonPanelView.isVisible = true;
+					balloonPanelView.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
 
 					expect( hidePanelSpy.notCalled ).to.true;
 				} );
@@ -211,7 +209,7 @@ describe( 'Link', () => {
 
 		describe( 'click on editable', () => {
 			it( 'should open with not selected url input when collapsed selection is inside link element', () => {
-				const selectUrlInputSpy = testUtils.sinon.spy( linkFeature.form.urlInput.view, 'select' );
+				const selectUrlInputSpy = testUtils.sinon.spy( linkFeature.formView.urlInputView, 'select' );
 				const observer = editor.editing.view.getObserver( ClickObserver );
 
 				editor.document.schema.allow( { name: '$text', inside: '$root' } );
@@ -219,7 +217,7 @@ describe( 'Link', () => {
 
 				observer.fire( 'click', { target: document.body } );
 
-				expect( balloonPanel.view.isVisible ).to.true;
+				expect( balloonPanelView.isVisible ).to.true;
 				expect( selectUrlInputSpy.notCalled ).to.true;
 			} );
 
@@ -234,14 +232,14 @@ describe( 'Link', () => {
 
 				observer.fire( 'click', { target: document.body } );
 
-				expect( balloonPanel.view.isVisible ).to.true;
+				expect( balloonPanelView.isVisible ).to.true;
 
-				const attachToSpy = testUtils.sinon.spy( balloonPanel.view, 'attachTo' );
+				const attachToSpy = testUtils.sinon.spy( balloonPanelView, 'attachTo' );
 
 				editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 1 ) ], true );
 				editor.editing.view.render();
 
-				expect( balloonPanel.view.isVisible ).to.true;
+				expect( balloonPanelView.isVisible ).to.true;
 				expect( attachToSpy.calledOnce ).to.true;
 			} );
 
@@ -256,12 +254,12 @@ describe( 'Link', () => {
 
 				observer.fire( 'click', { target: document.body } );
 
-				expect( balloonPanel.view.isVisible ).to.true;
+				expect( balloonPanelView.isVisible ).to.true;
 
 				editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 3, text, 3 ) ], true );
 				editor.editing.view.render();
 
-				expect( balloonPanel.view.isVisible ).to.false;
+				expect( balloonPanelView.isVisible ).to.false;
 			} );
 
 			it( 'should close when selection goes to the other link element with the same href', () => {
@@ -275,12 +273,12 @@ describe( 'Link', () => {
 
 				observer.fire( 'click', { target: document.body } );
 
-				expect( balloonPanel.view.isVisible ).to.true;
+				expect( balloonPanelView.isVisible ).to.true;
 
 				editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 1 ) ], true );
 				editor.editing.view.render();
 
-				expect( balloonPanel.view.isVisible ).to.false;
+				expect( balloonPanelView.isVisible ).to.false;
 			} );
 
 			it( 'should close when selection becomes non-collapsed', () => {
@@ -294,12 +292,12 @@ describe( 'Link', () => {
 
 				observer.fire( 'click', { target: {} } );
 
-				expect( balloonPanel.view.isVisible ).to.true;
+				expect( balloonPanelView.isVisible ).to.true;
 
 				editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 2 ) ] );
 				editor.editing.view.render();
 
-				expect( balloonPanel.view.isVisible ).to.false;
+				expect( balloonPanelView.isVisible ).to.false;
 			} );
 
 			it( 'should stop updating position after close', () => {
@@ -313,11 +311,11 @@ describe( 'Link', () => {
 
 				observer.fire( 'click', { target: {} } );
 
-				expect( balloonPanel.view.isVisible ).to.true;
+				expect( balloonPanelView.isVisible ).to.true;
 
-				balloonPanel.view.isVisible = false;
+				balloonPanelView.isVisible = false;
 
-				const attachToSpy = testUtils.sinon.spy( balloonPanel.view, 'attachTo' );
+				const attachToSpy = testUtils.sinon.spy( balloonPanelView, 'attachTo' );
 
 				editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 2, text, 2 ) ], true );
 				editor.editing.view.render();
@@ -332,7 +330,7 @@ describe( 'Link', () => {
 
 				observer.fire( 'click', { target: {} } );
 
-				expect( balloonPanel.view.isVisible ).to.false;
+				expect( balloonPanelView.isVisible ).to.false;
 			} );
 
 			it( 'should not open when selection is non-collapsed', () => {
@@ -343,7 +341,7 @@ describe( 'Link', () => {
 
 				observer.fire( 'click', { target: document.body } );
 
-				expect( balloonPanel.view.isVisible ).to.false;
+				expect( balloonPanelView.isVisible ).to.false;
 			} );
 		} );
 	} );
@@ -352,56 +350,55 @@ describe( 'Link', () => {
 		let hidePanelSpy, focusEditableSpy;
 
 		beforeEach( () => {
-			hidePanelSpy = testUtils.sinon.spy( balloonPanel.view, 'hide' );
+			hidePanelSpy = testUtils.sinon.spy( balloonPanelView, 'hide' );
 			focusEditableSpy = testUtils.sinon.spy( editor.editing.view, 'focus' );
 		} );
 
 		describe( 'binding', () => {
-			it( 'should bind form.model#url to link command value', () => {
-				const model = linkFeature.form.model;
+			it( 'should bind formView.urlInputView#value to link command value', () => {
 				const command = editor.commands.get( 'link' );
 
-				expect( model.value ).to.undefined;
+				expect( formView.urlInputView.value ).to.undefined;
 
 				command.value = 'http://cksource.com';
-				expect( model.url ).to.equal( 'http://cksource.com' );
+				expect( formView.urlInputView.value ).to.equal( 'http://cksource.com' );
 			} );
 
-			it( 'should execute link command on form.model#submit event', () => {
+			it( 'should execute link command on formView#submit event', () => {
 				const executeSpy = testUtils.sinon.spy( editor, 'execute' );
 
-				form.model.url = 'http://cksource.com';
-				form.model.fire( 'submit' );
+				formView.urlInputView.value = 'http://cksource.com';
+				formView.fire( 'submit' );
 
 				expect( executeSpy.calledOnce ).to.true;
 				expect( executeSpy.calledWithExactly( 'link', 'http://cksource.com' ) ).to.true;
 			} );
 
-			it( 'should hide and focus editable on form.model#submit event', () => {
-				form.model.fire( 'submit' );
+			it( 'should hide and focus editable on formView#submit event', () => {
+				formView.fire( 'submit' );
 
 				expect( hidePanelSpy.calledOnce ).to.true;
 				expect( focusEditableSpy.calledOnce ).to.true;
 			} );
 
-			it( 'should execute unlink command on form.model#unlink event', () => {
+			it( 'should execute unlink command on formView#unlink event', () => {
 				const executeSpy = testUtils.sinon.spy( editor, 'execute' );
 
-				form.model.fire( 'unlink' );
+				formView.fire( 'unlink' );
 
 				expect( executeSpy.calledOnce ).to.true;
 				expect( executeSpy.calledWithExactly( 'unlink' ) ).to.true;
 			} );
 
-			it( 'should hide and focus editable on form.model#unlink event', () => {
-				form.model.fire( 'unlink' );
+			it( 'should hide and focus editable on formView#unlink event', () => {
+				formView.fire( 'unlink' );
 
 				expect( hidePanelSpy.calledOnce ).to.true;
 				expect( focusEditableSpy.calledOnce ).to.true;
 			} );
 
-			it( 'should hide and focus editable on form.model#cancel event', () => {
-				form.model.fire( 'cancel' );
+			it( 'should hide and focus editable on formView#cancel event', () => {
+				formView.fire( 'cancel' );
 
 				expect( hidePanelSpy.calledOnce ).to.true;
 				expect( focusEditableSpy.calledOnce ).to.true;

+ 0 - 111
packages/ckeditor5-link/tests/ui/linkform.js

@@ -1,111 +0,0 @@
-/**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/* bender-tags: ui, form */
-
-import LinkForm from 'ckeditor5/link/ui/linkform.js';
-import LinkFormView from 'ckeditor5/link/ui/linkformview.js';
-import Model from 'ckeditor5/ui/model.js';
-
-import Button from 'ckeditor5/ui/button/button.js';
-import LabeledInput from 'ckeditor5/ui/labeledinput/labeledinput.js';
-
-describe( 'LinkForm', () => {
-	let linkForm, view, model;
-
-	beforeEach( () => {
-		view = new LinkFormView( {
-			t: () => {}
-		} );
-		model = new Model( {
-			url: 'foo'
-		} );
-		linkForm = new LinkForm( model, view );
-	} );
-
-	describe( 'constructor()', () => {
-		it( 'creates view#submit -> model#submit binding', () => {
-			const spy = sinon.spy();
-
-			model.on( 'submit', spy );
-
-			view.fire( 'submit' );
-			expect( spy.calledOnce ).to.be.true;
-		} );
-	} );
-
-	describe( 'child components', () => {
-		describe( 'urlInput', () => {
-			it( 'is created', () => {
-				expect( linkForm.urlInput ).to.be.instanceOf( LabeledInput );
-			} );
-
-			it( 'belongs to anonymous collection', () => {
-				expect( linkForm.collections.get( '$anonymous' ).find( ( item ) => {
-					return item === linkForm.urlInput;
-				} ) ).to.be.not.undefined;
-			} );
-
-			it( 'binds urlInput.model#value to linkForm.model#url', () => {
-				expect( linkForm.urlInput.model.value ).to.equal( 'foo' );
-
-				model.url = 'bar';
-				expect( linkForm.urlInput.model.value ).to.equal( 'bar' );
-			} );
-		} );
-
-		describe( 'saveButton', () => {
-			it( 'is created', () => {
-				expect( linkForm.saveButton ).to.be.instanceOf( Button );
-			} );
-
-			it( 'belongs to anonymous collection', () => {
-				expect( linkForm.collections.get( '$anonymous' ).find( ( item ) => {
-					return item === linkForm.saveButton;
-				} ) ).to.be.not.undefined;
-			} );
-		} );
-
-		describe( 'cancelButton', () => {
-			it( 'is created', () => {
-				expect( linkForm.cancelButton ).to.be.instanceOf( Button );
-			} );
-
-			it( 'belongs to anonymous collection', () => {
-				expect( linkForm.collections.get( '$anonymous' ).find( ( item ) => {
-					return item === linkForm.cancelButton;
-				} ) ).to.be.not.undefined;
-			} );
-
-			it( 'passes cancelButton.model#execute as linkForm.model#cancel', ( done ) => {
-				model.on( 'cancel', () => {
-					done();
-				} );
-
-				linkForm.cancelButton.model.fire( 'execute' );
-			} );
-		} );
-
-		describe( 'unlinkButton', () => {
-			it( 'is created', () => {
-				expect( linkForm.unlinkButton ).to.be.instanceOf( Button );
-			} );
-
-			it( 'belongs to anonymous collection', () => {
-				expect( linkForm.collections.get( '$anonymous' ).find( ( item ) => {
-					return item === linkForm.unlinkButton;
-				} ) ).to.be.not.undefined;
-			} );
-
-			it( 'passes unlinkButton.model#execute as linkForm.model#unlink', ( done ) => {
-				model.on( 'unlink', () => {
-					done();
-				} );
-
-				linkForm.unlinkButton.model.fire( 'execute' );
-			} );
-		} );
-	} );
-} );

+ 26 - 1
packages/ckeditor5-link/tests/ui/linkformview.js

@@ -12,7 +12,7 @@ describe( 'LinkFormView', () => {
 	let view;
 
 	beforeEach( () => {
-		view = new LinkFormView();
+		view = new LinkFormView( { t: () => {} } );
 
 		view.init();
 	} );
@@ -27,6 +27,31 @@ describe( 'LinkFormView', () => {
 			expect( view.saveButtonView ).to.be.instanceOf( View );
 			expect( view.cancelButtonView ).to.be.instanceOf( View );
 			expect( view.unlinkButtonView ).to.be.instanceOf( View );
+
+			expect( view._unboundChildren.get( 0 ) ).to.equal( view.urlInputView );
+			expect( view._unboundChildren.get( 1 ) ).to.equal( view.saveButtonView );
+			expect( view._unboundChildren.get( 2 ) ).to.equal( view.cancelButtonView );
+			expect( view._unboundChildren.get( 3 ) ).to.equal( view.unlinkButtonView );
+		} );
+
+		it( 'should fire `cancel` event on cancelButtonView#execute', () => {
+			const spy = sinon.spy();
+
+			view.on( 'cancel', spy );
+
+			view.cancelButtonView.fire( 'execute' );
+
+			expect( spy.calledOnce ).to.true;
+		} );
+
+		it( 'should fire `unlink` event on unlinkButtonView#execute', () => {
+			const spy = sinon.spy();
+
+			view.on( 'unlink', spy );
+
+			view.unlinkButtonView.fire( 'execute' );
+
+			expect( spy.calledOnce ).to.true;
 		} );
 
 		describe( 'template', () => {