diff --git a/src/link.js b/src/link.js
index f6eb95c..9c910be 100644
--- a/src/link.js
+++ b/src/link.js
@@ -237,29 +237,38 @@ export default class Link extends Plugin {
/**
* Adds the {@link #formView} to the {@link #_balloon}.
+ * When view is already added then try to focus it `focusInput` parameter is set as true.
*
- * @private
+ * @protected
* @param {Boolean} [focusInput=false] When `true`, link form will be focused on panel show.
+ * @return {Promise} A promise resolved when the {@link #formView} {@link module:ui/view~View#init} is done.
*/
_showPanel( focusInput ) {
if ( this._balloon.hasView( this.formView ) ) {
- return;
- }
-
- this._balloon.add( {
- view: this.formView,
- position: this._getBalloonPositionData()
- } );
+ // Check if formView should be focused and focus it if is visible.
+ if ( focusInput && this._balloon.visibleView === this.formView ) {
+ this.formView.urlInputView.select();
+ }
- if ( focusInput ) {
- this.formView.urlInputView.select();
+ return Promise.resolve();
}
+
+ return this._balloon.add( {
+ view: this.formView,
+ position: this._getBalloonPositionData()
+ } ).then( () => {
+ if ( focusInput ) {
+ this.formView.urlInputView.select();
+ }
+ } );
}
/**
* Removes the {@link #formView} from the {@link #_balloon}.
*
- * @private
+ * See {@link #_showPanel}.
+ *
+ * @protected
* @param {Boolean} [focusEditable=false] When `true`, editable focus will be restored on panel hide.
*/
_hidePanel( focusEditable ) {
@@ -267,12 +276,12 @@ export default class Link extends Plugin {
return;
}
- this._balloon.remove( this.formView );
- this.stopListening( this.editor.editing.view, 'render' );
-
if ( focusEditable ) {
this.editor.editing.view.focus();
}
+
+ this.stopListening( this.editor.editing.view, 'render' );
+ this._balloon.remove( this.formView );
}
/**
diff --git a/tests/link.js b/tests/link.js
index f2db92a..068d415 100644
--- a/tests/link.js
+++ b/tests/link.js
@@ -10,6 +10,7 @@ import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import Link from '../src/link';
import LinkEngine from '../src/linkengine';
import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
@@ -28,7 +29,7 @@ describe( 'Link', () => {
document.body.appendChild( editorElement );
return ClassicTestEditor.create( editorElement, {
- plugins: [ Link ]
+ plugins: [ Link, Paragraph ]
} )
.then( newEditor => {
newEditor.editing.view.attachDomRoot( editorElement );
@@ -50,6 +51,8 @@ describe( 'Link', () => {
} );
afterEach( () => {
+ editorElement.remove();
+
return editor.destroy();
} );
@@ -69,77 +72,206 @@ describe( 'Link', () => {
expect( editor.editing.view.getObserver( ClickObserver ) ).to.instanceOf( ClickObserver );
} );
- describe( 'link toolbar button', () => {
- it( 'should register link button', () => {
- expect( linkButton ).to.instanceOf( ButtonView );
+ describe( '_showPanel()', () => {
+ let balloonAddSpy;
+
+ beforeEach( () => {
+ balloonAddSpy = testUtils.sinon.spy( balloon, 'add' );
+ editor.editing.view.isFocused = true;
} );
- it( 'should bind linkButtonView to link command', () => {
- const command = editor.commands.get( 'link' );
+ it( 'should return promise', () => {
+ const returned = linkFeature._showPanel();
- command.isEnabled = true;
- expect( linkButton.isEnabled ).to.be.true;
+ expect( returned ).to.instanceof( Promise );
- command.isEnabled = false;
- expect( linkButton.isEnabled ).to.be.false;
+ return returned;
} );
- it( 'should add link form to the ContextualBalloon on execute event', () => {
- linkButton.fire( 'execute' );
+ it( 'should add #formView to the #_balloon and attach the #_balloon to the selection when text fragment is selected', () => {
+ setModelData( editor.document, 'f[o]o' );
+ const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
- expect( balloon.visibleView ).to.equal( formView );
+ return linkFeature._showPanel()
+ .then( () => {
+ expect( balloon.visibleView ).to.equal( formView );
+
+ sinon.assert.calledWithExactly( balloonAddSpy, {
+ view: formView,
+ position: {
+ target: selectedRange,
+ limiter: editorElement
+ }
+ } );
+ } );
} );
- it( 'should add link form to the ContextualBalloon and attach balloon to the link element ' +
- 'when collapsed selection is inside link element',
+ it( 'should add #formView to the #_balloon and attach the #_balloon to the selection when selection is collapsed', () => {
+ setModelData( editor.document, 'f[]oo' );
+ const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
+
+ return linkFeature._showPanel()
+ .then( () => {
+ expect( balloon.visibleView ).to.equal( formView );
+
+ sinon.assert.calledWithExactly( balloonAddSpy, {
+ view: formView,
+ position: {
+ target: selectedRange,
+ limiter: editorElement
+ }
+ } );
+ } );
+ } );
+
+ it( 'should add #formView to the #_balloon and attach the #_balloon to the link element when collapsed selection is inside ' +
+ 'that link',
() => {
- const balloonAddSpy = testUtils.sinon.spy( balloon, 'add' );
+ setModelData( editor.document, '<$text linkHref="url">f[]oo$text>' );
+ const linkElement = editorElement.querySelector( 'a' );
- editor.document.schema.allow( { name: '$text', inside: '$root' } );
- setModelData( editor.document, '<$text linkHref="url">some[] url$text>' );
- editor.editing.view.isFocused = true;
+ return linkFeature._showPanel()
+ .then( () => {
+ expect( balloon.visibleView ).to.equal( formView );
+
+ sinon.assert.calledWithExactly( balloonAddSpy, {
+ view: formView,
+ position: {
+ target: linkElement,
+ limiter: editorElement
+ }
+ } );
+ } );
+ } );
- linkButton.fire( 'execute' );
+ it( 'should not focus the #formView at default', () => {
+ const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
- const linkElement = editorElement.querySelector( 'a' );
+ return linkFeature._showPanel()
+ .then( () => {
+ sinon.assert.notCalled( spy );
+ } );
+ } );
- sinon.assert.calledWithExactly( balloonAddSpy, {
- view: formView,
- position: {
- target: linkElement,
- limiter: editorElement
- }
- } );
+ it( 'should not focus the #formView when called with a `false` parameter', () => {
+ const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
+
+ return linkFeature._showPanel( false )
+ .then( () => {
+ sinon.assert.notCalled( spy );
+ } );
} );
- it( 'should add link form to the ContextualBalloon and attach balloon to the selection, when selection is non-collapsed', () => {
- const balloonAddSpy = testUtils.sinon.spy( balloon, 'add' );
+ it( 'should not focus the #formView when called with a `true` parameter while the balloon is opened but link form is not visible', () => {
+ const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
+ const viewMock = {
+ ready: true,
+ init: () => {},
+ destroy: () => {}
+ };
- editor.document.schema.allow( { name: '$text', inside: '$root' } );
- setModelData( editor.document, 'so[me ur]l' );
- editor.editing.view.isFocused = true;
+ return linkFeature._showPanel( false )
+ .then( () => balloon.add( { view: viewMock } ) )
+ .then( () => linkFeature._showPanel( true ) )
+ .then( () => {
+ sinon.assert.notCalled( spy );
+ } );
+ } );
- linkButton.fire( 'execute' );
+ it( 'should focus the #formView when called with a `true` parameter', () => {
+ const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
- const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
+ return linkFeature._showPanel( true )
+ .then( () => {
+ sinon.assert.calledOnce( spy );
+ } );
+ } );
- sinon.assert.calledWithExactly( balloonAddSpy, {
- view: formView,
- position: {
- target: selectedRange,
- limiter: editorElement
- }
- } );
+ it( 'should focus the #formView when called with a `true` parameter while the balloon is open and the #formView is visible', () => {
+ const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
+
+ return linkFeature._showPanel( false )
+ .then( () => linkFeature._showPanel( true ) )
+ .then( () => {
+ sinon.assert.calledOnce( spy );
+ } );
} );
+ } );
- it( 'should select link input value when link balloon is opened', () => {
- const selectUrlInputSpy = testUtils.sinon.spy( linkFeature.formView.urlInputView, 'select' );
+ describe( '_hidePanel()', () => {
+ beforeEach( () => {
+ return balloon.add( { view: formView } );
+ } );
- editor.editing.view.isFocused = true;
+ it( 'should remove #formView from the #_balloon', () => {
+ linkFeature._hidePanel();
+ expect( balloon.hasView( formView ) ).to.false;
+ } );
+
+ it( 'should not focus the `editable` by default', () => {
+ const spy = testUtils.sinon.spy( editor.editing.view, 'focus' );
+
+ linkFeature._hidePanel();
+ sinon.assert.notCalled( spy );
+ } );
+
+ it( 'should not focus the `editable` when called with a `false` parameter', () => {
+ const spy = testUtils.sinon.spy( editor.editing.view, 'focus' );
+
+ linkFeature._hidePanel( false );
+ sinon.assert.notCalled( spy );
+ } );
+
+ it( 'should focus the `editable` when called with a `true` parameter', () => {
+ const spy = testUtils.sinon.spy( editor.editing.view, 'focus' );
+
+ linkFeature._hidePanel( true );
+ sinon.assert.calledOnce( spy );
+ } );
+
+ it( 'should not throw an error when #formView is not added to the `balloon`', () => {
+ linkFeature._hidePanel( true );
+
+ expect( () => {
+ linkFeature._hidePanel( true );
+ } ).to.not.throw();
+ } );
+
+ it( 'should clear `render` listener from ViewDocument', () => {
+ const spy = sinon.spy();
+
+ linkFeature.listenTo( editor.editing.view, 'render', spy );
+
+ linkFeature._hidePanel();
+
+ editor.editing.view.render();
+
+ sinon.assert.notCalled( spy );
+ } );
+ } );
+
+ describe( 'link toolbar button', () => {
+ it( 'should register link button', () => {
+ expect( linkButton ).to.instanceOf( ButtonView );
+ } );
+
+ it( 'should bind linkButtonView to link command', () => {
+ const command = editor.commands.get( 'link' );
+
+ command.isEnabled = true;
+ expect( linkButton.isEnabled ).to.be.true;
+
+ command.isEnabled = false;
+ expect( linkButton.isEnabled ).to.be.false;
+ } );
+
+ it( 'should show the #_balloon on execute event with the selected #formView', () => {
+ // Method is stubbed because it returns internal promise which can't be returned in test.
+ const spy = testUtils.sinon.stub( linkFeature, '_showPanel', () => {} );
linkButton.fire( 'execute' );
- expect( selectUrlInputSpy.calledOnce ).to.true;
+ sinon.assert.calledWithExactly( spy, true );
} );
} );
@@ -168,37 +300,17 @@ describe( 'Link', () => {
} );
} );
- describe( 'ContextualBalloon', () => {
- let focusEditableSpy;
-
- beforeEach( () => {
- focusEditableSpy = testUtils.sinon.spy( editor.editing.view, 'focus' );
- } );
-
- it( 'should not be added to ContextualBalloon at default', () => {
- expect( balloon.visibleView ).to.null;
- } );
-
- it( 'should be added to ContextualBalloon and form should be selected on `CTRL+K` keystroke', () => {
- const selectUrlInputSpy = testUtils.sinon.spy( formView.urlInputView, 'select' );
+ describe( 'keyboard support', () => {
+ it( 'should show the #_balloon with selected #formView on `CTRL+K` keystroke', () => {
+ // Method is stubbed because it returns internal promise which can't be returned in test.
+ const spy = testUtils.sinon.stub( linkFeature, '_showPanel', () => {} );
editor.keystrokes.press( { keyCode: keyCodes.k, ctrlKey: true } );
- expect( balloon.visibleView ).to.equal( formView );
- expect( selectUrlInputSpy.calledOnce ).to.true;
+ sinon.assert.calledWithExactly( spy, true );
} );
- it( 'should not add panel to ContextualBalloon more than once', () => {
- // Add panel to balloon by pressing toolbar button.
- linkButton.fire( 'execute' );
-
- // Press button once again.
- expect( () => {
- linkButton.fire( 'execute' );
- } ).to.not.throw();
- } );
-
- it( 'should focus the link form on Tab key press', () => {
+ it( 'should focus the the #formView on `Tab` key press when the #_balloon is open', () => {
const keyEvtData = {
keyCode: keyCodes.tab,
preventDefault: sinon.spy(),
@@ -216,7 +328,7 @@ describe( 'Link', () => {
sinon.assert.notCalled( spy );
// Balloon is visible, form focused.
- return balloon.add( { view: formView } )
+ return linkFeature._showPanel( true )
.then( () => {
formView.focusTracker.isFocused = true;
@@ -235,104 +347,109 @@ describe( 'Link', () => {
} );
} );
- describe( 'close listeners', () => {
- describe( 'keyboard', () => {
- it( 'should close after Esc key press (from editor) and not focus editable', () => {
- const keyEvtData = {
- keyCode: keyCodes.esc,
- preventDefault: sinon.spy(),
- stopPropagation: sinon.spy()
- };
+ it( 'should hide the #_balloon after Esc key press (from editor) and not focus the editable', () => {
+ const spy = testUtils.sinon.spy( linkFeature, '_hidePanel' );
+ const keyEvtData = {
+ keyCode: keyCodes.esc,
+ preventDefault: sinon.spy(),
+ stopPropagation: sinon.spy()
+ };
- // Balloon is visible.
- return balloon.add( { view: formView } ).then( () => {
- editor.keystrokes.press( keyEvtData );
+ // Balloon is visible.
+ return linkFeature._showPanel( false ).then( () => {
+ editor.keystrokes.press( keyEvtData );
- expect( balloon.visibleView ).to.null;
- sinon.assert.notCalled( focusEditableSpy );
- } );
- } );
+ sinon.assert.calledWithExactly( spy );
+ } );
+ } );
+
+ it( 'should not hide #_balloon after Esc key press (from editor) when #_balloon is open but is not visible', () => {
+ const spy = testUtils.sinon.spy( linkFeature, '_hidePanel' );
+ const keyEvtData = {
+ keyCode: keyCodes.esc,
+ preventDefault: () => {},
+ stopPropagation: () => {}
+ };
+
+ const viewMock = {
+ ready: true,
+ init: () => {},
+ destroy: () => {}
+ };
+
+ return linkFeature._showPanel( false )
+ .then( () => balloon.add( { view: viewMock } ) )
+ .then( () => {
+ editor.keystrokes.press( keyEvtData );
- it( 'should not close after Esc key press (from editor) when panel is in stack but not visible', () => {
- const keyEvtData = {
- keyCode: keyCodes.esc,
- preventDefault: () => {},
- stopPropagation: () => {}
- };
-
- const viewMock = {
- init: () => {},
- destroy: () => {}
- };
-
- return balloon.add( { view: formView } )
- .then( () => {
- return balloon.add( { view: viewMock } );
- } )
- .then( () => {
- editor.keystrokes.press( keyEvtData );
-
- expect( balloon.visibleView ).to.equal( viewMock );
- expect( balloon.hasView( formView ) ).to.true;
- sinon.assert.notCalled( focusEditableSpy );
- } );
+ sinon.assert.notCalled( spy );
} );
+ } );
- it( 'should close after Esc key press (from the form) and focus editable', () => {
- const keyEvtData = {
- keyCode: keyCodes.esc,
- preventDefault: sinon.spy(),
- stopPropagation: sinon.spy()
- };
+ it( 'should hide the #_balloon after Esc key press (from the form) and focus the editable', () => {
+ const spy = testUtils.sinon.spy( linkFeature, '_hidePanel' );
+ const keyEvtData = {
+ keyCode: keyCodes.esc,
+ preventDefault: sinon.spy(),
+ stopPropagation: sinon.spy()
+ };
- return balloon.add( { view: formView } )
- .then( () => {
- formView.keystrokes.press( keyEvtData );
+ return linkFeature._showPanel( true )
+ .then( () => {
+ formView.keystrokes.press( keyEvtData );
- expect( balloon.visibleView ).to.null;
- sinon.assert.calledOnce( focusEditableSpy );
- } );
+ sinon.assert.calledWithExactly( spy, true );
} );
- } );
+ } );
+ } );
+
+ describe( 'mouse support', () => {
+ it( 'should hide #_balloon and not focus editable on click outside the #_balloon', () => {
+ const spy = testUtils.sinon.spy( linkFeature, '_hidePanel' );
- describe( 'mouse', () => {
- it( 'should close and not focus editable on click outside the panel', () => {
- return balloon.add( { view: formView } )
- .then( () => {
- document.body.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
+ return linkFeature._showPanel( true )
+ .then( () => {
+ document.body.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
- expect( balloon.visibleView ).to.null;
- expect( focusEditableSpy.notCalled ).to.true;
- } );
+ sinon.assert.calledWithExactly( spy );
} );
+ } );
+
+ it( 'should not hide #_balloon on click inside the #_balloon', () => {
+ const spy = testUtils.sinon.spy( linkFeature, '_hidePanel' );
- it( 'should not close on click inside the panel', () => {
- return balloon.add( { view: formView } )
- .then( () => {
- balloon.view.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
+ return linkFeature._showPanel( true )
+ .then( () => {
+ balloon.view.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
- expect( balloon.visibleView ).to.equal( formView );
- } );
+ sinon.assert.notCalled( spy );
} );
- } );
} );
- describe( 'click on editable', () => {
- it( 'should open with not selected url input when collapsed selection is inside link element', () => {
- const selectUrlInputSpy = testUtils.sinon.spy( formView.urlInputView, 'select' );
- const observer = editor.editing.view.getObserver( ClickObserver );
+ describe( 'clicking on editable', () => {
+ let observer;
+
+ beforeEach( () => {
+ observer = editor.editing.view.getObserver( ClickObserver );
+ } );
+
+ it( 'should open with not selected formView when collapsed selection is inside link element', () => {
+ // Method is stubbed because it returns internal promise which can't be returned in test.
+ const spy = testUtils.sinon.stub( linkFeature, '_showPanel', () => {} );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, '<$text linkHref="url">fo[]o$text>' );
observer.fire( 'click', { target: document.body } );
- expect( balloon.visibleView ).to.equal( formView );
- expect( selectUrlInputSpy.notCalled ).to.true;
+ sinon.assert.calledWithExactly( spy );
} );
it( 'should keep open and update position until collapsed selection stay inside the same link element', () => {
- const observer = editor.editing.view.getObserver( ClickObserver );
+ // Method is stubbed because it returns internal promise which can't be returned in test.
+ const showSpy = testUtils.sinon.stub( linkFeature, '_showPanel', () => {} );
+ const hideSpy = testUtils.sinon.stub( linkFeature, '_hidePanel' );
+ const updatePositionSpy = testUtils.sinon.stub( balloon, 'updatePosition', () => {} );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, '<$text linkHref="url">b[]ar$text>' );
@@ -342,22 +459,24 @@ describe( 'Link', () => {
observer.fire( 'click', { target: document.body } );
- expect( balloon.visibleView ).to.equal( formView );
-
- const updatePositionSpy = testUtils.sinon.spy( balloon, 'updatePosition' );
+ // Panel is shown.
+ sinon.assert.calledOnce( showSpy );
// Move selection.
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 1 ) ], true );
editor.editing.view.render();
- // Check if balloon is still open and position was updated.
- expect( balloon.visibleView ).to.equal( formView );
- expect( updatePositionSpy.calledOnce ).to.true;
+ // Check if balloon is still opened (wasn't hide).
+ sinon.assert.notCalled( hideSpy );
+ // And position was updated
+ sinon.assert.calledOnce( updatePositionSpy );
} );
it( 'should not duplicate `render` listener on `ViewDocument`', () => {
- const observer = editor.editing.view.getObserver( ClickObserver );
- const updatePositionSpy = testUtils.sinon.spy( balloon, 'updatePosition' );
+ const updatePositionSpy = testUtils.sinon.stub( balloon, 'updatePosition', () => {} );
+
+ // Method is stubbed because it returns internal promise which can't be returned in test.
+ testUtils.sinon.stub( linkFeature, '_showPanel', () => {} );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, '<$text linkHref="url">b[]ar$text>' );
@@ -381,7 +500,10 @@ describe( 'Link', () => {
} );
it( 'should close when selection goes outside the link element', () => {
- const observer = editor.editing.view.getObserver( ClickObserver );
+ const hideSpy = testUtils.sinon.stub( linkFeature, '_hidePanel' );
+
+ // Method is stubbed because it returns internal promise which can't be returned in test.
+ testUtils.sinon.stub( linkFeature, '_showPanel', () => {} );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, 'foo <$text linkHref="url">b[]ar$text>' );
@@ -391,16 +513,19 @@ describe( 'Link', () => {
observer.fire( 'click', { target: document.body } );
- expect( balloon.visibleView ).to.equal( formView );
+ sinon.assert.notCalled( hideSpy );
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 3, text, 3 ) ], true );
editor.editing.view.render();
- expect( balloon.visibleView ).to.null;
+ sinon.assert.calledOnce( hideSpy );
} );
it( 'should close when selection goes to the other link element with the same href', () => {
- const observer = editor.editing.view.getObserver( ClickObserver );
+ const hideSpy = testUtils.sinon.stub( linkFeature, '_hidePanel' );
+
+ // Method is stubbed because it returns internal promise which can't be returned in test.
+ testUtils.sinon.stub( linkFeature, '_showPanel', () => {} );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, '<$text linkHref="url">f[]oo$text> bar <$text linkHref="url">biz$text>' );
@@ -410,16 +535,19 @@ describe( 'Link', () => {
observer.fire( 'click', { target: document.body } );
- expect( balloon.visibleView ).to.equal( formView );
+ sinon.assert.notCalled( hideSpy );
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 1 ) ], true );
editor.editing.view.render();
- expect( balloon.visibleView ).to.null;
+ sinon.assert.calledOnce( hideSpy );
} );
it( 'should close when selection becomes non-collapsed', () => {
- const observer = editor.editing.view.getObserver( ClickObserver );
+ const hideSpy = testUtils.sinon.stub( linkFeature, '_hidePanel' );
+
+ // Method is stubbed because it returns internal promise which can't be returned in test.
+ testUtils.sinon.stub( linkFeature, '_showPanel', () => {} );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, '<$text linkHref="url">f[]oo$text>' );
@@ -429,58 +557,31 @@ describe( 'Link', () => {
observer.fire( 'click', { target: {} } );
- expect( balloon.visibleView ).to.equal( formView );
-
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 2 ) ] );
editor.editing.view.render();
- expect( balloon.visibleView ).to.null;
- } );
-
- it( 'should stop updating position after close', () => {
- const observer = editor.editing.view.getObserver( ClickObserver );
-
- editor.document.schema.allow( { name: '$text', inside: '$root' } );
- setModelData( editor.document, '<$text linkHref="url">b[]ar$text>' );
-
- const root = editor.editing.view.getRoot();
- const text = root.getChild( 0 ).getChild( 0 );
-
- observer.fire( 'click', { target: {} } );
-
- expect( balloon.visibleView ).to.equal( formView );
-
- // Close balloon by dispatching `cancel` event on formView.
- formView.fire( 'cancel' );
-
- const updatePositionSpy = testUtils.sinon.spy( balloon, 'updatePosition' );
-
- // Move selection inside link element.
- editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 2, text, 2 ) ], true );
- editor.editing.view.render();
-
- expect( updatePositionSpy.notCalled ).to.true;
+ sinon.assert.calledOnce( hideSpy );
} );
it( 'should not open when selection is not inside link element', () => {
- const observer = editor.editing.view.getObserver( ClickObserver );
+ const showSpy = testUtils.sinon.stub( linkFeature, '_showPanel' );
setModelData( editor.document, '[]' );
observer.fire( 'click', { target: {} } );
- expect( balloon.visibleView ).to.null;
+ sinon.assert.notCalled( showSpy );
} );
it( 'should not open when selection is non-collapsed', () => {
- const observer = editor.editing.view.getObserver( ClickObserver );
+ const showSpy = testUtils.sinon.stub( linkFeature, '_showPanel' );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, '<$text linkHref="url">f[o]o$text>' );
- observer.fire( 'click', { target: document.body } );
+ observer.fire( 'click', { target: {} } );
- expect( balloon.visibleView ).to.null;
+ sinon.assert.notCalled( showSpy );
} );
} );
} );
@@ -516,7 +617,7 @@ describe( 'Link', () => {
} );
it( 'should hide and focus editable on formView#submit event', () => {
- return balloon.add( { view: formView } )
+ return linkFeature._showPanel()
.then( () => {
formView.fire( 'submit' );
@@ -535,7 +636,7 @@ describe( 'Link', () => {
} );
it( 'should hide and focus editable on formView#unlink event', () => {
- return balloon.add( { view: formView } )
+ return linkFeature._showPanel()
.then( () => {
formView.fire( 'unlink' );
@@ -545,7 +646,7 @@ describe( 'Link', () => {
} );
it( 'should hide and focus editable on formView#cancel event', () => {
- return balloon.add( { view: formView } )
+ return linkFeature._showPanel()
.then( () => {
formView.fire( 'cancel' );