Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@ckeditor/ckeditor5-link

Package Overview
Dependencies
Maintainers
1
Versions
707
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@ckeditor/ckeditor5-link - npm Package Compare versions

Comparing version 0.6.0 to 0.7.0

lang/translations/da.po

16

CHANGELOG.md
Changelog
=========
## [0.7.0](https://github.com/ckeditor/ckeditor5-link/compare/v0.6.0...v0.6.1) (2017-05-07)
### Bug fixes
* `Esc` key should close the link panel even if none of the `LinkFormView` fields is focused. Closes [#90](https://github.com/ckeditor/ckeditor5-link/issues/90). ([866fa49](https://github.com/ckeditor/ckeditor5-link/commit/866fa49))
* The link balloon should hide the "Unlink" button when creating a link. Closes [#53](https://github.com/ckeditor/ckeditor5-link/issues/53). ([686e625](https://github.com/ckeditor/ckeditor5-link/commit/686e625))
* The link balloon should update its position upon external document changes. Closes [#113](https://github.com/ckeditor/ckeditor5-link/issues/113). ([18a5b90](https://github.com/ckeditor/ckeditor5-link/commit/18a5b90))
* The link plugin should manage focus when the balloon is open. Made Link plugins `_showPanel()` and `_hidePanel()` methods protected. Closes [#95](https://github.com/ckeditor/ckeditor5-link/issues/95). Closes [#94](https://github.com/ckeditor/ckeditor5-link/issues/94). ([5a83b70](https://github.com/ckeditor/ckeditor5-link/commit/5a83b70))
* Link should not be allowed directly in the root element. Closes [#97](https://github.com/ckeditor/ckeditor5-link/issues/97). ([81d4ba5](https://github.com/ckeditor/ckeditor5-link/commit/81d4ba5))
### Other changes
* Integrated the link plugin with the `ContextualBalloon` plugin. Closes [#91](https://github.com/ckeditor/ckeditor5-link/issues/91). ([26f148e](https://github.com/ckeditor/ckeditor5-link/commit/26f148e))
* Updated translations. ([7a35617](https://github.com/ckeditor/ckeditor5-link/commit/7a35617))
## [0.6.0](https://github.com/ckeditor/ckeditor5-link/compare/v0.5.1...v0.6.0) (2017-04-05)

@@ -5,0 +21,0 @@

24

package.json
{
"name": "@ckeditor/ckeditor5-link",
"version": "0.6.0",
"version": "0.7.0",
"description": "Link feature for CKEditor 5.",
"keywords": [],
"dependencies": {
"@ckeditor/ckeditor5-core": "^0.8.0",
"@ckeditor/ckeditor5-engine": "^0.9.0",
"@ckeditor/ckeditor5-theme-lark": "^0.7.0",
"@ckeditor/ckeditor5-ui": "^0.8.0"
"@ckeditor/ckeditor5-core": "^0.8.1",
"@ckeditor/ckeditor5-engine": "^0.10.0",
"@ckeditor/ckeditor5-theme-lark": "^0.8.0",
"@ckeditor/ckeditor5-ui": "^0.9.0"
},
"devDependencies": {
"@ckeditor/ckeditor5-dev-lint": "^2.0.2",
"@ckeditor/ckeditor5-editor-classic": "^0.7.2",
"@ckeditor/ckeditor5-enter": "^0.9.0",
"@ckeditor/ckeditor5-heading": "^0.9.0",
"@ckeditor/ckeditor5-paragraph": "^0.7.0",
"@ckeditor/ckeditor5-typing": "^0.9.0",
"@ckeditor/ckeditor5-undo": "^0.8.0",
"@ckeditor/ckeditor5-utils": "^0.9.0",
"@ckeditor/ckeditor5-editor-classic": "^0.7.3",
"@ckeditor/ckeditor5-enter": "^0.9.1",
"@ckeditor/ckeditor5-heading": "^0.9.1",
"@ckeditor/ckeditor5-paragraph": "^0.8.0",
"@ckeditor/ckeditor5-typing": "^0.9.1",
"@ckeditor/ckeditor5-undo": "^0.8.1",
"@ckeditor/ckeditor5-utils": "^0.9.1",
"gulp": "^3.9.0",

@@ -22,0 +22,0 @@ "guppy-pre-commit": "^0.4.0"

@@ -14,2 +14,3 @@ /**

import LinkElement from './linkelement';
import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';

@@ -19,4 +20,2 @@ import clickOutsideHandler from '@ckeditor/ckeditor5-ui/src/bindings/clickoutsidehandler';

import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import BalloonPanelView from '@ckeditor/ckeditor5-ui/src/panel/balloon/balloonpanelview';
import LinkFormView from './ui/linkformview';

@@ -30,5 +29,6 @@

/**
* The link feature. It introduces the Link and Unlink buttons and the <kbd>Ctrl+K</kbd> keystroke.
* The link plugin. It introduces the Link and Unlink buttons and the <kbd>Ctrl+K</kbd> keystroke.
*
* It uses the {@link module:link/linkengine~LinkEngine link engine feature}.
* It uses the {@link module:link/linkengine~LinkEngine link engine plugin} and the
* {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon plugin}.
*

@@ -42,3 +42,3 @@ * @extends module:core/plugin~Plugin

static get requires() {
return [ LinkEngine ];
return [ LinkEngine, ContextualBalloon ];
}

@@ -60,14 +60,15 @@

/**
* Balloon panel view to display the main UI.
* The form view displayed inside of the balloon.
*
* @member {module:link/ui/balloonpanel~BalloonPanelView}
* @member {module:link/ui/linkformview~LinkFormView}
*/
this.balloonPanelView = this._createBalloonPanel();
this.formView = this._createForm();
/**
* The form view inside {@link #balloonPanelView}.
* The contextual balloon plugin instance.
*
* @member {module:link/ui/linkformview~LinkFormView}
* @private
* @member {module:ui/panel/balloon/contextualballoon~ContextualBalloon}
*/
this.formView = this._createForm();
this._balloon = this.editor.plugins.get( ContextualBalloon );

@@ -77,7 +78,46 @@ // Create toolbar buttons.

this._createToolbarUnlinkButton();
// Attach lifecycle actions to the the balloon.
this._attachActions();
}
/**
* Creates the {@link module:link/ui/linkformview~LinkFormView} instance.
*
* @private
* @returns {module:link/ui/linkformview~LinkFormView} Link form instance.
*/
_createForm() {
const editor = this.editor;
const formView = new LinkFormView( editor.locale );
formView.urlInputView.bind( 'value' ).to( editor.commands.get( 'link' ), 'value' );
// Execute link command after clicking on formView `Save` button.
this.listenTo( formView, 'submit', () => {
editor.execute( 'link', formView.urlInputView.inputView.element.value );
this._hidePanel( true );
} );
// Execute unlink command after clicking on formView `Unlink` button.
this.listenTo( formView, 'unlink', () => {
editor.execute( 'unlink' );
this._hidePanel( true );
} );
// Hide the panel after clicking on formView `Cancel` button.
this.listenTo( formView, 'cancel', () => this._hidePanel( true ) );
// Close the panel on esc key press when the form has focus.
formView.keystrokes.set( 'Esc', ( data, cancel ) => {
this._hidePanel( true );
cancel();
} );
return formView;
}
/**
* Creates a toolbar link button. Clicking this button will show
* {@link #balloonPanelView} attached to the selection.
* {@link #_balloon} attached to the selection.
*

@@ -91,4 +131,4 @@ * @private

// Handle `Ctrl+K` keystroke and show panel.
editor.keystrokes.set( 'CTRL+K', () => this._showPanel() );
// Handle `Ctrl+K` keystroke and show the panel.
editor.keystrokes.set( 'CTRL+K', () => this._showPanel( true ) );

@@ -108,3 +148,3 @@ editor.ui.componentFactory.add( 'link', ( locale ) => {

// Show the panel on button click.
this.listenTo( button, 'execute', () => this._showPanel() );
this.listenTo( button, 'execute', () => this._showPanel( true ) );

@@ -145,19 +185,10 @@ return button;

/**
* Creates the {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView} instance.
* Attaches actions which control whether the balloon panel containing the
* {@link #formView} is visible or not.
*
* @private
* @returns {module:ui/panel/balloon/balloonpanelview~BalloonPanelView} Link balloon panel instance.
*/
_createBalloonPanel() {
const editor = this.editor;
const viewDocument = editor.editing.view;
_attachActions() {
const viewDocument = this.editor.editing.view;
// Create the balloon panel instance.
const balloonPanelView = new BalloonPanelView( editor.locale );
balloonPanelView.maxWidth = 300;
// Add balloonPanel.view#element to FocusTracker.
// @TODO: Do it automatically ckeditor5-core#23
editor.ui.focusTracker.add( balloonPanelView.element );
// Handle click on view document and show panel when selection is placed inside the link element.

@@ -167,24 +198,14 @@ // Keep panel open until selection will be inside the same link element.

const viewSelection = viewDocument.selection;
const parentLink = getPositionParentLink( viewSelection.getFirstPosition() );
const parentLink = this._getSelectedLinkElement();
// When collapsed selection is inside link element (link element is clicked).
if ( viewSelection.isCollapsed && parentLink ) {
this._attachPanelToElement();
this.listenTo( viewDocument, 'render', () => {
const currentParentLink = getPositionParentLink( viewSelection.getFirstPosition() );
if ( !viewSelection.isCollapsed || parentLink !== currentParentLink ) {
this._hidePanel();
} else {
this._attachPanelToElement( parentLink );
}
} );
this.listenTo( balloonPanelView, 'change:isVisible', () => this.stopListening( viewDocument, 'render' ) );
// Then show panel but keep focus inside editor editable.
this._showPanel();
}
} );
// Focus the form if balloon panel is open and tab key has been pressed.
editor.keystrokes.set( 'Tab', ( data, cancel ) => {
if ( balloonPanelView.isVisible && !this.formView.focusTracker.isFocused ) {
// Focus the form if the balloon is visible and the Tab key has been pressed.
this.editor.keystrokes.set( 'Tab', ( data, cancel ) => {
if ( this._balloon.visibleView === this.formView && !this.formView.focusTracker.isFocused ) {
this.formView.focus();

@@ -195,6 +216,6 @@ cancel();

// Close the panel on esc key press when editable has focus.
editor.keystrokes.set( 'Esc', ( data, cancel ) => {
if ( balloonPanelView.isVisible ) {
this._hidePanel( true );
// Close the panel on the Esc key press when the editable has focus and the balloon is visible.
this.editor.keystrokes.set( 'Esc', ( data, cancel ) => {
if ( this._balloon.visibleView === this.formView ) {
this._hidePanel();
cancel();

@@ -206,109 +227,141 @@ }

clickOutsideHandler( {
emitter: balloonPanelView,
activator: () => balloonPanelView.isVisible,
contextElement: balloonPanelView.element,
emitter: this.formView,
activator: () => this._balloon.hasView( this.formView ),
contextElement: this._balloon.view.element,
callback: () => this._hidePanel()
} );
editor.ui.view.body.add( balloonPanelView );
return balloonPanelView;
}
/**
* Creates the {@link module:link/ui/linkformview~LinkFormView} instance.
* 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
* @returns {module:link/ui/linkformview~LinkFormView} Link form instance.
* @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.
*/
_createForm() {
const editor = this.editor;
const formView = new LinkFormView( editor.locale );
_showPanel( focusInput ) {
const editing = this.editor.editing;
const showViewDocument = editing.view;
const showIsCollapsed = showViewDocument.selection.isCollapsed;
const showSelectedLink = this._getSelectedLinkElement();
formView.urlInputView.bind( 'value' ).to( editor.commands.get( 'link' ), 'value' );
// https://github.com/ckeditor/ckeditor5-link/issues/53
this.formView.unlinkButtonView.isVisible = !!showSelectedLink;
// Execute link command after clicking on formView `Save` button.
this.listenTo( formView, 'submit', () => {
editor.execute( 'link', formView.urlInputView.inputView.element.value );
this._hidePanel( true );
} );
this.listenTo( showViewDocument, 'render', () => {
const renderSelectedLink = this._getSelectedLinkElement();
const renderIsCollapsed = showViewDocument.selection.isCollapsed;
const hasSellectionExpanded = showIsCollapsed && !renderIsCollapsed;
// Execute unlink command after clicking on formView `Unlink` button.
this.listenTo( formView, 'unlink', () => {
editor.execute( 'unlink' );
this._hidePanel( true );
// Hide the panel if:
// * the selection went out of the original link element
// (e.g. paragraph containing the link was removed),
// * the selection has expanded
// upon the #render event.
if ( hasSellectionExpanded || showSelectedLink !== renderSelectedLink ) {
this._hidePanel( true );
}
// Update the position of the panel when:
// * the selection remains in the original link element,
// * there was no link element in the first place, i.e. creating a new link
else {
// If still in a link element, simply update the position of the balloon.
if ( renderSelectedLink ) {
this._balloon.updatePosition();
}
// If there was no link, upon #render, the balloon must be moved
// to the new position in the editing view (a new native DOM range).
else {
this._balloon.updatePosition( this._getBalloonPositionData() );
}
}
} );
// Close the panel on esc key press when the form has focus.
formView.keystrokes.set( 'Esc', ( data, cancel ) => {
this._hidePanel( true );
cancel();
} );
if ( this._balloon.hasView( this.formView ) ) {
// Check if formView should be focused and focus it if is visible.
if ( focusInput && this._balloon.visibleView === this.formView ) {
this.formView.urlInputView.select();
}
// Hide balloon panel after clicking on formView `Cancel` button.
this.listenTo( formView, 'cancel', () => this._hidePanel( true ) );
return Promise.resolve();
} else {
return this._balloon.add( {
view: this.formView,
position: this._getBalloonPositionData()
} ).then( () => {
if ( focusInput ) {
this.formView.urlInputView.select();
}
} );
}
}
this.balloonPanelView.content.add( formView );
/**
* Removes the {@link #formView} from the {@link #_balloon}.
*
* See {@link #_showPanel}.
*
* @protected
* @param {Boolean} [focusEditable=false] When `true`, editable focus will be restored on panel hide.
*/
_hidePanel( focusEditable ) {
this.stopListening( this.editor.editing.view, 'render' );
return formView;
if ( !this._balloon.hasView( this.formView ) ) {
return;
}
if ( focusEditable ) {
this.editor.editing.view.focus();
}
this.stopListening( this.editor.editing.view, 'render' );
this._balloon.remove( this.formView );
}
/**
* Shows {@link #balloonPanelView link balloon panel} and attach to target element.
* If selection is collapsed and is placed inside link element, then panel will be attached
* to whole link element, otherwise will be attached to the selection.
* Returns positioning options for the {@link #_balloon}. They control the way balloon is attached
* to the target element or selection.
*
* If the selection is collapsed and inside a link element, then the panel will be attached to the
* entire link element. Otherwise, it will be attached to the selection.
*
* @private
* @param {module:link/linkelement~LinkElement} [parentLink] Target element.
* @returns {module:utils/dom/position~Options}
*/
_attachPanelToElement( parentLink ) {
_getBalloonPositionData() {
const viewDocument = this.editor.editing.view;
const targetLink = parentLink || getPositionParentLink( viewDocument.selection.getFirstPosition() );
const targetLink = this._getSelectedLinkElement();
const target = targetLink ?
// When selection is inside link element, then attach panel to this element.
viewDocument.domConverter.getCorrespondingDomElement( targetLink )
// When selection is inside link element, then attach panel to this element.
viewDocument.domConverter.getCorrespondingDomElement( targetLink )
:
// Otherwise attach panel to the selection.
viewDocument.domConverter.viewRangeToDom( viewDocument.selection.getFirstRange() );
// Otherwise attach panel to the selection.
viewDocument.domConverter.viewRangeToDom( viewDocument.selection.getFirstRange() );
this.balloonPanelView.attachTo( {
return {
target,
limiter: viewDocument.domConverter.getCorrespondingDomElement( viewDocument.selection.editableElement )
} );
};
}
/**
* Hides {@link #balloonPanelView balloon panel view}.
* Returns the {@link module:link/linkelement~LinkElement} at the first
* {@link module:engine/model/position~Position} of
* {@link module:engine/view/document~Document editing view's} selection or `null`
* if there's none.
*
* @private
* @param {Boolean} [focusEditable=false] When `true` then editable focus will be restored on panel hide.
* @returns {module:link/linkelement~LinkElement|null}
*/
_hidePanel( focusEditable ) {
this.balloonPanelView.hide();
if ( focusEditable ) {
this.editor.editing.view.focus();
}
_getSelectedLinkElement() {
return this.editor.editing.view
.selection
.getFirstPosition()
.parent
.getAncestors()
.find( ancestor => ancestor instanceof LinkElement );
}
/**
* Shows {@link #balloonPanelView balloon panel view}.
*
* @private
*/
_showPanel() {
this._attachPanelToElement();
this.formView.urlInputView.select();
}
}
// Try to find if one of the position parent ancestors is a LinkElement,
// if yes return this element.
//
// @private
// @param {engine.view.Position} position
// @returns {module:link/linkelement~LinkElement|null}
function getPositionParentLink( position ) {
return position.parent.getAncestors().find( ( ancestor ) => ancestor instanceof LinkElement );
}

@@ -34,3 +34,3 @@ /**

// Allow link attribute on all inline nodes.
editor.document.schema.allow( { name: '$inline', attributes: 'linkHref' } );
editor.document.schema.allow( { name: '$inline', attributes: 'linkHref', inside: '$block' } );

@@ -37,0 +37,0 @@ // Build converter from model to view for data and editing pipelines.

@@ -127,3 +127,6 @@ /**

'ck-link-form',
]
],
// https://github.com/ckeditor/ckeditor5-link/issues/90
tabindex: '-1'
},

@@ -130,0 +133,0 @@

@@ -13,6 +13,7 @@ /**

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';
import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
import BalloonPanelView from '@ckeditor/ckeditor5-ui/src/panel/balloon/balloonpanelview';

@@ -25,3 +26,3 @@ import Range from '@ckeditor/ckeditor5-engine/src/view/range';

describe( 'Link', () => {
let editor, linkFeature, linkButton, unlinkButton, balloonPanelView, formView, editorElement;
let editor, linkFeature, linkButton, unlinkButton, balloon, formView, editorElement;

@@ -33,3 +34,3 @@ beforeEach( () => {

return ClassicTestEditor.create( editorElement, {
plugins: [ Link ]
plugins: [ Link, Paragraph ]
} )

@@ -44,4 +45,10 @@ .then( newEditor => {

unlinkButton = editor.ui.componentFactory.create( 'unlink' );
balloonPanelView = linkFeature.balloonPanelView;
balloon = editor.plugins.get( ContextualBalloon );
formView = linkFeature.formView;
// There is no point to execute BalloonPanelView attachTo and pin methods so lets override it.
testUtils.sinon.stub( balloon.view, 'attachTo', () => {} );
testUtils.sinon.stub( balloon.view, 'pin', () => {} );
return formView.init();
} );

@@ -51,2 +58,4 @@ } );

afterEach( () => {
editorElement.remove();
return editor.destroy();

@@ -63,2 +72,6 @@ } );

it( 'should load ContextualBalloon', () => {
expect( editor.plugins.get( ContextualBalloon ) ).to.instanceOf( ContextualBalloon );
} );
it( 'should register click observer', () => {

@@ -68,337 +81,532 @@ 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 open panel on linkButtonView 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, '<paragraph>f[o]o</paragraph>' );
const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
expect( linkFeature.balloonPanelView.isVisible ).to.true;
return linkFeature._showPanel()
.then( () => {
expect( balloon.visibleView ).to.equal( formView );
sinon.assert.calledWithExactly( balloonAddSpy, {
view: formView,
position: {
target: selectedRange,
limiter: editorElement
}
} );
} );
} );
it( 'should open panel attached to the link element, when collapsed selection is inside link element', () => {
const attachToSpy = testUtils.sinon.spy( balloonPanelView, 'attachTo' );
it( 'should add #formView to the #_balloon and attach the #_balloon to the selection when selection is collapsed', () => {
setModelData( editor.document, '<paragraph>f[]oo</paragraph>' );
const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
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 );
linkButton.fire( 'execute' );
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',
() => {
setModelData( editor.document, '<paragraph><$text linkHref="url">f[]oo</$text></paragraph>' );
const linkElement = editorElement.querySelector( 'a' );
sinon.assert.calledWithExactly( attachToSpy, sinon.match( {
target: linkElement,
limiter: editorElement
} ) );
return linkFeature._showPanel()
.then( () => {
expect( balloon.visibleView ).to.equal( formView );
sinon.assert.calledWithExactly( balloonAddSpy, {
view: formView,
position: {
target: linkElement,
limiter: editorElement
}
} );
} );
} );
it( 'should open panel attached to the selection, when there is non-collapsed selection', () => {
const attachToSpy = testUtils.sinon.spy( balloonPanelView, 'attachTo' );
it( 'should not focus the #formView at default', () => {
const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, 'so[me ur]l' );
editor.editing.view.isFocused = true;
return linkFeature._showPanel()
.then( () => {
sinon.assert.notCalled( spy );
} );
} );
linkButton.fire( 'execute' );
it( 'should not focus the #formView when called with a `false` parameter', () => {
const spy = testUtils.sinon.spy( formView.urlInputView, 'select' );
const selectedRange = editorElement.ownerDocument.getSelection().getRangeAt( 0 );
sinon.assert.calledWithExactly( attachToSpy, sinon.match( {
target: selectedRange,
limiter: editorElement
} ) );
return linkFeature._showPanel( false )
.then( () => {
sinon.assert.notCalled( spy );
} );
} );
it( 'should select panel input value when panel is opened', () => {
const selectUrlInputSpy = testUtils.sinon.spy( linkFeature.formView.urlInputView, 'select' );
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.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' );
expect( selectUrlInputSpy.calledOnce ).to.true;
return linkFeature._showPanel( true )
.then( () => {
sinon.assert.calledOnce( spy );
} );
} );
} );
describe( 'unlink toolbar button', () => {
it( 'should register unlink button', () => {
expect( unlinkButton ).to.instanceOf( ButtonView );
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 bind unlinkButtonView to unlink command', () => {
const command = editor.commands.get( 'unlink' );
// https://github.com/ckeditor/ckeditor5-link/issues/53
it( 'should set formView.unlinkButtonView#isVisible depending on the selection in a link or not', () => {
setModelData( editor.document, '<paragraph>f[]oo</paragraph>' );
command.isEnabled = true;
expect( unlinkButton.isEnabled ).to.be.true;
return linkFeature._showPanel()
.then( () => {
expect( formView.unlinkButtonView.isVisible ).to.be.false;
command.isEnabled = false;
expect( unlinkButton.isEnabled ).to.be.false;
setModelData( editor.document, '<paragraph><$text linkHref="url">f[]oo</$text></paragraph>' );
return linkFeature._showPanel()
.then( () => {
expect( formView.unlinkButtonView.isVisible ).to.be.true;
} );
} );
} );
it( 'should execute unlink command on unlinkButtonView execute event', () => {
const executeSpy = testUtils.sinon.spy( editor, 'execute' );
describe( 'when the document is rendering', () => {
it( 'should not duplicate #render listeners', () => {
const viewDocument = editor.editing.view;
unlinkButton.fire( 'execute' );
setModelData( editor.document, '<paragraph>f[]oo</paragraph>' );
expect( executeSpy.calledOnce ).to.true;
expect( executeSpy.calledWithExactly( 'unlink' ) ).to.true;
} );
} );
const spy = testUtils.sinon.stub( balloon, 'updatePosition', () => {} );
describe( 'link balloon panel', () => {
let hidePanelSpy, focusEditableSpy;
return linkFeature._showPanel()
.then( () => {
viewDocument.render();
linkFeature._hidePanel();
beforeEach( () => {
hidePanelSpy = testUtils.sinon.spy( balloonPanelView, 'hide' );
focusEditableSpy = testUtils.sinon.spy( editor.editing.view, 'focus' );
} );
return linkFeature._showPanel()
.then( () => {
viewDocument.render();
sinon.assert.calledTwice( spy );
} );
} );
} );
it( 'should be created', () => {
expect( balloonPanelView ).to.instanceOf( BalloonPanelView );
} );
//https://github.com/ckeditor/ckeditor5-link/issues/113
it( 'updates the position of the panel – editing a link, then the selection remains in the link upon #render', () => {
const viewDocument = editor.editing.view;
it( 'should be appended to the document body', () => {
expect( document.body.contains( balloonPanelView.element ) );
} );
setModelData( editor.document, '<paragraph><$text linkHref="url">f[]oo</$text></paragraph>' );
it( 'should open with selected url input on `CTRL+K` keystroke', () => {
const selectUrlInputSpy = testUtils.sinon.spy( linkFeature.formView.urlInputView, 'select' );
return linkFeature._showPanel()
.then( () => {
const spy = testUtils.sinon.stub( balloon, 'updatePosition', () => {} );
editor.keystrokes.press( { keyCode: keyCodes.k, ctrlKey: true } );
const root = viewDocument.getRoot();
const text = root.getChild( 0 ).getChild( 0 ).getChild( 0 );
expect( balloonPanelView.isVisible ).to.true;
expect( selectUrlInputSpy.calledOnce ).to.true;
} );
// Move selection to foo[].
viewDocument.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 3, text, 3 ) ], true );
viewDocument.render();
it( 'should add balloon panel element to focus tracker', () => {
editor.ui.focusTracker.isFocused = false;
sinon.assert.calledOnce( spy );
sinon.assert.calledWithExactly( spy );
} );
} );
balloonPanelView.element.dispatchEvent( new Event( 'focus' ) );
//https://github.com/ckeditor/ckeditor5-link/issues/113
it( 'updates the position of the panel – creating a new link, then the selection moved upon #render', () => {
const viewDocument = editor.editing.view;
expect( editor.ui.focusTracker.isFocused ).to.true;
setModelData( editor.document, '<paragraph>f[]oo</paragraph>' );
return linkFeature._showPanel()
.then( () => {
const spy = testUtils.sinon.stub( balloon, 'updatePosition', () => {} );
// Fires #render.
const root = viewDocument.getRoot();
const text = root.getChild( 0 ).getChild( 0 );
viewDocument.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 3, text, 3 ) ], true );
viewDocument.render();
sinon.assert.calledOnce( spy );
sinon.assert.calledWithExactly( spy, {
target: editorElement.ownerDocument.getSelection().getRangeAt( 0 ),
limiter: editorElement
} );
} );
} );
//https://github.com/ckeditor/ckeditor5-link/issues/113
it( 'hides of the panel – editing a link, then the selection moved out of the link upon #render', () => {
const viewDocument = editor.editing.view;
setModelData( editor.document, '<paragraph><$text linkHref="url">f[]oo</$text>bar</paragraph>' );
return linkFeature._showPanel()
.then( () => {
const spyUpdate = testUtils.sinon.stub( balloon, 'updatePosition', () => {} );
const spyHide = testUtils.sinon.spy( linkFeature, '_hidePanel' );
const root = viewDocument.getRoot();
const text = root.getChild( 0 ).getChild( 1 );
// Move selection to b[]ar.
viewDocument.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 1 ) ], true );
viewDocument.render();
sinon.assert.calledOnce( spyHide );
sinon.assert.notCalled( spyUpdate );
} );
} );
//https://github.com/ckeditor/ckeditor5-link/issues/113
it( 'hides of the panel – editing a link, then the selection moved to another link upon #render', () => {
const viewDocument = editor.editing.view;
setModelData( editor.document, '<paragraph><$text linkHref="url">f[]oo</$text>bar<$text linkHref="url">b[]az</$text></paragraph>' );
return linkFeature._showPanel()
.then( () => {
const spyUpdate = testUtils.sinon.stub( balloon, 'updatePosition', () => {} );
const spyHide = testUtils.sinon.spy( linkFeature, '_hidePanel' );
const root = viewDocument.getRoot();
const text = root.getChild( 0 ).getChild( 2 ).getChild( 0 );
// Move selection to b[]az.
viewDocument.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 1 ) ], true );
viewDocument.render();
sinon.assert.calledOnce( spyHide );
sinon.assert.notCalled( spyUpdate );
} );
} );
//https://github.com/ckeditor/ckeditor5-link/issues/113
it( 'hides the panel – editing a link, then the selection expands upon #render', () => {
const viewDocument = editor.editing.view;
setModelData( editor.document, '<paragraph><$text linkHref="url">f[]oo</$text></paragraph>' );
return linkFeature._showPanel()
.then( () => {
const spyUpdate = testUtils.sinon.stub( balloon, 'updatePosition', () => {} );
const spyHide = testUtils.sinon.spy( linkFeature, '_hidePanel' );
const root = viewDocument.getRoot();
const text = root.getChild( 0 ).getChild( 0 ).getChild( 0 );
// Move selection to f[o]o.
viewDocument.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 2 ) ], true );
viewDocument.render();
sinon.assert.calledOnce( spyHide );
sinon.assert.notCalled( spyUpdate );
} );
} );
} );
} );
it( 'should focus the link form on Tab key press', () => {
const keyEvtData = {
keyCode: keyCodes.tab,
preventDefault: sinon.spy(),
stopPropagation: sinon.spy()
};
describe( '_hidePanel()', () => {
beforeEach( () => {
return balloon.add( { view: formView } );
} );
// Mock balloon invisible, form not focused.
balloonPanelView.isVisible = false;
formView.focusTracker.isFocused = false;
it( 'should remove #formView from the #_balloon', () => {
linkFeature._hidePanel();
expect( balloon.hasView( formView ) ).to.false;
} );
const spy = sinon.spy( formView, 'focus' );
it( 'should not focus the `editable` by default', () => {
const spy = testUtils.sinon.spy( editor.editing.view, 'focus' );
editor.keystrokes.press( keyEvtData );
sinon.assert.notCalled( keyEvtData.preventDefault );
sinon.assert.notCalled( keyEvtData.stopPropagation );
linkFeature._hidePanel();
sinon.assert.notCalled( spy );
} );
// Mock balloon visible, form focused.
balloonPanelView.isVisible = true;
formView.focusTracker.isFocused = true;
it( 'should not focus the `editable` when called with a `false` parameter', () => {
const spy = testUtils.sinon.spy( editor.editing.view, 'focus' );
editor.keystrokes.press( keyEvtData );
sinon.assert.notCalled( keyEvtData.preventDefault );
sinon.assert.notCalled( keyEvtData.stopPropagation );
linkFeature._hidePanel( false );
sinon.assert.notCalled( spy );
} );
// Mock balloon visible, form not focused.
balloonPanelView.isVisible = true;
formView.focusTracker.isFocused = false;
it( 'should focus the `editable` when called with a `true` parameter', () => {
const spy = testUtils.sinon.spy( editor.editing.view, 'focus' );
editor.keystrokes.press( keyEvtData );
sinon.assert.calledOnce( keyEvtData.preventDefault );
sinon.assert.calledOnce( keyEvtData.stopPropagation );
linkFeature._hidePanel( true );
sinon.assert.calledOnce( spy );
} );
describe( 'close listeners', () => {
describe( 'keyboard', () => {
it( 'should close after Esc key press (from editor)', () => {
const keyEvtData = {
keyCode: keyCodes.esc,
preventDefault: sinon.spy(),
stopPropagation: sinon.spy()
};
it( 'should not throw an error when #formView is not added to the `balloon`', () => {
linkFeature._hidePanel( true );
balloonPanelView.isVisible = false;
expect( () => {
linkFeature._hidePanel( true );
} ).to.not.throw();
} );
editor.keystrokes.press( keyEvtData );
it( 'should clear `render` listener from ViewDocument', () => {
const spy = sinon.spy();
sinon.assert.notCalled( hidePanelSpy );
sinon.assert.notCalled( focusEditableSpy );
linkFeature.listenTo( editor.editing.view, 'render', spy );
balloonPanelView.isVisible = true;
linkFeature._hidePanel();
editor.keystrokes.press( keyEvtData );
editor.editing.view.render();
sinon.assert.calledOnce( hidePanelSpy );
sinon.assert.calledOnce( focusEditableSpy );
} );
sinon.assert.notCalled( spy );
} );
} );
it( 'should close after Esc key press (from the form)', () => {
const keyEvtData = {
keyCode: keyCodes.esc,
preventDefault: sinon.spy(),
stopPropagation: sinon.spy()
};
describe( 'link toolbar button', () => {
it( 'should register link button', () => {
expect( linkButton ).to.instanceOf( ButtonView );
} );
formView.keystrokes.press( keyEvtData );
it( 'should bind linkButtonView to link command', () => {
const command = editor.commands.get( 'link' );
sinon.assert.calledOnce( hidePanelSpy );
sinon.assert.calledOnce( focusEditableSpy );
} );
} );
command.isEnabled = true;
expect( linkButton.isEnabled ).to.be.true;
describe( 'mouse', () => {
it( 'should close and not focus editable on click outside the panel', () => {
balloonPanelView.isVisible = true;
document.body.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
command.isEnabled = false;
expect( linkButton.isEnabled ).to.be.false;
} );
expect( hidePanelSpy.calledOnce ).to.true;
expect( focusEditableSpy.notCalled ).to.true;
} );
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', () => {} );
it( 'should not close on click inside the panel', () => {
balloonPanelView.isVisible = true;
balloonPanelView.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
linkButton.fire( 'execute' );
expect( hidePanelSpy.notCalled ).to.true;
} );
} );
sinon.assert.calledWithExactly( spy, true );
} );
} );
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.formView.urlInputView, 'select' );
const observer = editor.editing.view.getObserver( ClickObserver );
describe( 'unlink toolbar button', () => {
it( 'should register unlink button', () => {
expect( unlinkButton ).to.instanceOf( ButtonView );
} );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, '<$text linkHref="url">fo[]o</$text>' );
it( 'should bind unlinkButtonView to unlink command', () => {
const command = editor.commands.get( 'unlink' );
observer.fire( 'click', { target: document.body } );
command.isEnabled = true;
expect( unlinkButton.isEnabled ).to.be.true;
expect( balloonPanelView.isVisible ).to.true;
expect( selectUrlInputSpy.notCalled ).to.true;
} );
command.isEnabled = false;
expect( unlinkButton.isEnabled ).to.be.false;
} );
it( 'should keep open and update position until collapsed selection stay inside the same link element', () => {
const observer = editor.editing.view.getObserver( ClickObserver );
it( 'should execute unlink command on unlinkButtonView execute event', () => {
const executeSpy = testUtils.sinon.spy( editor, 'execute' );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, '<$text linkHref="url">b[]ar</$text>' );
unlinkButton.fire( 'execute' );
const root = editor.editing.view.getRoot();
const text = root.getChild( 0 ).getChild( 0 );
expect( executeSpy.calledOnce ).to.true;
expect( executeSpy.calledWithExactly( 'unlink' ) ).to.true;
} );
} );
observer.fire( 'click', { target: document.body } );
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', () => {} );
expect( balloonPanelView.isVisible ).to.true;
editor.keystrokes.press( { keyCode: keyCodes.k, ctrlKey: true } );
const attachToSpy = testUtils.sinon.spy( balloonPanelView, 'attachTo' );
sinon.assert.calledWithExactly( spy, true );
} );
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 1 ) ], true );
editor.editing.view.render();
it( 'should focus the the #formView on `Tab` key press when the #_balloon is open', () => {
const keyEvtData = {
keyCode: keyCodes.tab,
preventDefault: sinon.spy(),
stopPropagation: sinon.spy()
};
expect( balloonPanelView.isVisible ).to.true;
expect( attachToSpy.calledOnce ).to.true;
} );
// Balloon is invisible, form not focused.
formView.focusTracker.isFocused = false;
it( 'should close when selection goes outside the link element', () => {
const observer = editor.editing.view.getObserver( ClickObserver );
const spy = sinon.spy( formView, 'focus' );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, 'foo <$text linkHref="url">b[]ar</$text>' );
editor.keystrokes.press( keyEvtData );
sinon.assert.notCalled( keyEvtData.preventDefault );
sinon.assert.notCalled( keyEvtData.stopPropagation );
sinon.assert.notCalled( spy );
const root = editor.editing.view.getRoot();
const text = root.getChild( 0 );
// Balloon is visible, form focused.
return linkFeature._showPanel( true )
.then( () => {
formView.focusTracker.isFocused = true;
observer.fire( 'click', { target: document.body } );
editor.keystrokes.press( keyEvtData );
sinon.assert.notCalled( keyEvtData.preventDefault );
sinon.assert.notCalled( keyEvtData.stopPropagation );
sinon.assert.notCalled( spy );
expect( balloonPanelView.isVisible ).to.true;
// Balloon is still visible, form not focused.
formView.focusTracker.isFocused = false;
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 3, text, 3 ) ], true );
editor.editing.view.render();
editor.keystrokes.press( keyEvtData );
sinon.assert.calledOnce( keyEvtData.preventDefault );
sinon.assert.calledOnce( keyEvtData.stopPropagation );
sinon.assert.calledOnce( spy );
} );
} );
expect( balloonPanelView.isVisible ).to.false;
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 linkFeature._showPanel( false ).then( () => {
editor.keystrokes.press( keyEvtData );
sinon.assert.calledWithExactly( spy );
} );
} );
it( 'should close when selection goes to the other link element with the same href', () => {
const observer = editor.editing.view.getObserver( ClickObserver );
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: () => {}
};
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, '<$text linkHref="url">f[]oo</$text> bar <$text linkHref="url">biz</$text>' );
const viewMock = {
ready: true,
init: () => {},
destroy: () => {}
};
const root = editor.editing.view.getRoot();
const text = root.getChild( 2 ).getChild( 0 );
return linkFeature._showPanel( false )
.then( () => balloon.add( { view: viewMock } ) )
.then( () => {
editor.keystrokes.press( keyEvtData );
observer.fire( 'click', { target: document.body } );
sinon.assert.notCalled( spy );
} );
} );
expect( balloonPanelView.isVisible ).to.true;
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()
};
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 1 ) ], true );
editor.editing.view.render();
return linkFeature._showPanel( true )
.then( () => {
formView.keystrokes.press( keyEvtData );
expect( balloonPanelView.isVisible ).to.false;
} );
sinon.assert.calledWithExactly( spy, true );
} );
} );
} );
it( 'should close when selection becomes non-collapsed', () => {
const observer = editor.editing.view.getObserver( ClickObserver );
describe( 'mouse support', () => {
it( 'should hide #_balloon and not focus editable on click outside the #_balloon', () => {
const spy = testUtils.sinon.spy( linkFeature, '_hidePanel' );
editor.document.schema.allow( { name: '$text', inside: '$root' } );
setModelData( editor.document, '<$text linkHref="url">f[]oo</$text>' );
return linkFeature._showPanel( true )
.then( () => {
document.body.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
const root = editor.editing.view.getRoot();
const text = root.getChild( 0 ).getChild( 0 );
sinon.assert.calledWithExactly( spy );
} );
} );
observer.fire( 'click', { target: {} } );
it( 'should not hide #_balloon on click inside the #_balloon', () => {
const spy = testUtils.sinon.spy( linkFeature, '_hidePanel' );
expect( balloonPanelView.isVisible ).to.true;
return linkFeature._showPanel( true )
.then( () => {
balloon.view.element.dispatchEvent( new Event( 'mouseup', { bubbles: true } ) );
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 1, text, 2 ) ] );
editor.editing.view.render();
sinon.assert.notCalled( spy );
} );
} );
expect( balloonPanelView.isVisible ).to.false;
describe( 'clicking on editable', () => {
let observer;
beforeEach( () => {
observer = editor.editing.view.getObserver( ClickObserver );
} );
it( 'should stop updating position after close', () => {
const 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">b[]ar</$text>' );
setModelData( editor.document, '<$text linkHref="url">fo[]o</$text>' );
const root = editor.editing.view.getRoot();
const text = root.getChild( 0 ).getChild( 0 );
observer.fire( 'click', { target: document.body } );
observer.fire( 'click', { target: {} } );
expect( balloonPanelView.isVisible ).to.true;
balloonPanelView.isVisible = false;
const attachToSpy = testUtils.sinon.spy( balloonPanelView, 'attachTo' );
editor.editing.view.selection.setRanges( [ Range.createFromParentsAndOffsets( text, 2, text, 2 ) ], true );
editor.editing.view.render();
expect( attachToSpy.notCalled ).to.true;
sinon.assert.calledWithExactly( spy );
} );
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' );

@@ -409,7 +617,7 @@ setModelData( editor.document, '[]' );

expect( balloonPanelView.isVisible ).to.false;
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' );

@@ -419,5 +627,5 @@ editor.document.schema.allow( { name: '$text', inside: '$root' } );

observer.fire( 'click', { target: document.body } );
observer.fire( 'click', { target: {} } );
expect( balloonPanelView.isVisible ).to.false;
sinon.assert.notCalled( showSpy );
} );

@@ -428,9 +636,19 @@ } );

describe( 'link form', () => {
let hidePanelSpy, focusEditableSpy;
let focusEditableSpy;
beforeEach( () => {
hidePanelSpy = testUtils.sinon.spy( balloonPanelView, 'hide' );
focusEditableSpy = testUtils.sinon.spy( editor.editing.view, 'focus' );
} );
it( 'should mark the editor ui as focused when the #formView is focused', () => {
return linkFeature._showPanel()
.then( () => {
editor.ui.focusTracker.isFocused = false;
formView.element.dispatchEvent( new Event( 'focus' ) );
expect( editor.ui.focusTracker.isFocused ).to.true;
} );
} );
describe( 'binding', () => {

@@ -460,6 +678,9 @@ it( 'should bind formView.urlInputView#value to link command value', () => {

it( 'should hide and focus editable on formView#submit event', () => {
formView.fire( 'submit' );
return linkFeature._showPanel()
.then( () => {
formView.fire( 'submit' );
expect( hidePanelSpy.calledOnce ).to.true;
expect( focusEditableSpy.calledOnce ).to.true;
expect( balloon.visibleView ).to.null;
expect( focusEditableSpy.calledOnce ).to.true;
} );
} );

@@ -477,13 +698,19 @@

it( 'should hide and focus editable on formView#unlink event', () => {
formView.fire( 'unlink' );
return linkFeature._showPanel()
.then( () => {
formView.fire( 'unlink' );
expect( hidePanelSpy.calledOnce ).to.true;
expect( focusEditableSpy.calledOnce ).to.true;
expect( balloon.visibleView ).to.null;
expect( focusEditableSpy.calledOnce ).to.true;
} );
} );
it( 'should hide and focus editable on formView#cancel event', () => {
formView.fire( 'cancel' );
return linkFeature._showPanel()
.then( () => {
formView.fire( 'cancel' );
expect( hidePanelSpy.calledOnce ).to.true;
expect( focusEditableSpy.calledOnce ).to.true;
expect( balloon.visibleView ).to.null;
expect( focusEditableSpy.calledOnce ).to.true;
} );
} );

@@ -490,0 +717,0 @@ } );

@@ -10,3 +10,5 @@ /**

import UnlinkCommand from '../src/unlinkcommand';
import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';

@@ -20,3 +22,3 @@ import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';

return VirtualTestEditor.create( {
plugins: [ LinkEngine ]
plugins: [ Paragraph, LinkEngine ]
} )

@@ -27,4 +29,2 @@ .then( newEditor => {

doc = editor.document;
doc.schema.allow( { name: '$text', inside: '$root' } );
} );

@@ -38,3 +38,4 @@ } );

it( 'should set proper schema rules', () => {
expect( doc.schema.check( { name: '$inline', attributes: [ 'linkHref' ] } ) ).to.be.true;
expect( doc.schema.check( { name: '$inline', attributes: [ 'linkHref' ], inside: '$root' } ) ).to.be.false;
expect( doc.schema.check( { name: '$inline', attributes: [ 'linkHref' ], inside: '$block' } ) ).to.be.true;
} );

@@ -62,6 +63,19 @@

it( 'should convert `<a href="url">` to `linkHref="url"` attribute', () => {
editor.setData( '<p><a href="url">foo</a>bar</p>' );
expect( getModelData( doc, { withoutSelection: true } ) )
.to.equal( '<paragraph><$text linkHref="url">foo</$text>bar</paragraph>' );
expect( editor.getData() ).to.equal( '<p><a href="url">foo</a>bar</p>' );
} );
it( 'should be integrated with autoparagraphing', () => {
// Incorrect results because autoparagraphing works incorrectly (issue in paragraph).
// https://github.com/ckeditor/ckeditor5-paragraph/issues/10
editor.setData( '<a href="url">foo</a>bar' );
expect( getModelData( doc, { withoutSelection: true } ) ).to.equal( '<$text linkHref="url">foo</$text>bar' );
expect( editor.getData() ).to.equal( '<a href="url">foo</a>bar' );
expect( getModelData( doc, { withoutSelection: true } ) ).to.equal( '<paragraph>foobar</paragraph>' );
expect( editor.getData() ).to.equal( '<p>foobar</p>' );
} );

@@ -72,13 +86,13 @@ } );

it( 'should convert attribute', () => {
setModelData( doc, '<$text linkHref="url">foo</$text>bar' );
setModelData( doc, '<paragraph><$text linkHref="url">foo</$text>bar</paragraph>' );
expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<a href="url">foo</a>bar' );
expect( getViewData( editor.editing.view, { withoutSelection: true } ) ).to.equal( '<p><a href="url">foo</a>bar</p>' );
} );
it( 'should convert to `LinkElement` instance', () => {
setModelData( doc, '<$text linkHref="url">foo</$text>bar' );
setModelData( doc, '<paragraph><$text linkHref="url">foo</$text>bar</paragraph>' );
expect( editor.editing.view.getRoot().getChild( 0 ) ).to.be.instanceof( LinkElement );
expect( editor.editing.view.getRoot().getChild( 0 ).getChild( 0 ) ).to.be.instanceof( LinkElement );
} );
} );
} );

@@ -29,2 +29,3 @@ /**

expect( view.element.classList.contains( 'ck-link-form' ) ).to.true;
expect( view.element.getAttribute( 'tabindex' ) ).to.equal( '-1' );
} );

@@ -31,0 +32,0 @@

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc