8
0
Просмотр исходного кода

Merge pull request #7582 from ckeditor/i/6774-disable-type-around

Feature (widget): Made it possible to disable the `WidgetTypeAround` plugin on the fly. Closes #6774.
Kuba Niegowski 5 лет назад
Родитель
Сommit
8cecf39f06

BIN
packages/ckeditor5-widget/docs/assets/img/framework-deep-dive-widget-type-around-buttons.gif


+ 67 - 0
packages/ckeditor5-widget/docs/framework/guides/deep-dive/widget-internals.md

@@ -0,0 +1,67 @@
+---
+category: framework-deep-dive-ui
+menu-title: Widget internals
+---
+
+# Deep dive into widget internals
+
+## Disabling the widget type around feature
+
+The {@link module:widget/widgettypearound~WidgetTypeAround `WidgetTypeAround`} plugin allows users to type around widgets where normally it is impossible to place the caret due to limitations of web browsers. Although this feature boosts the productivity, some integrations may not want or need it, for instance:
+
+* when the UI of the feature collides with the integration,
+* when typing outside widgets should be impossible (content made exclusively of widgets),
+* when certain widgets have a fixed location in the document that should never change.
+
+In the sections that follows, you will learn how to configure the editor to address these specific cases.
+
+### Hiding the buttons that insert paragraphs
+
+{@img assets/img/framework-deep-dive-widget-type-around-buttons.gif A screenshot showing the location of the buttons that insert paragraphs around widgets.}
+
+The easiest way to get rid of the type around buttons is to hide them using CSS. Put the following code snippet anywhere in your application and the buttons will no longer bother the users:
+
+```css
+.ck.ck-editor__editable .ck-widget .ck-widget__type-around__button {
+	display: none;
+}
+```
+
+If you only want to customize the type around buttons you can use the same CSS selector to modify their look or the position.
+
+<info-box hint>
+	Hiding the type around buttons does not disable the feature. Users will still be able to activate the caret before or after individual widgets using the keyboard and start typing. [Learn how to disable the entire feature](#disabling-the-entire-feature).
+</info-box>
+
+### Disabling the entire feature
+
+Although the {@link module:widget/widgettypearound~WidgetTypeAround `WidgetTypeAround`} plugin is an integral part of the {@link module:widget/widget~Widget widget} sub–system and loaded by default whenever an editor feature uses widgets (for instance, {@link features/image images} or {@link features/table tables}), you can still disable on the fly. Disabling the feature will both hide the buttons in the widgets and disable other behaviors, for instance:
+
+* the caret will not be rendered before or after a widget when a user navigates the document using arrow keys,
+* the <kbd>Enter</kbd> and <kbd>Shift</kbd>+<kbd>Enter</kbd> keystrokes will no longer insert paragraphs if pressed when a widget is selected.
+
+Use the {@link module:core/plugin~Plugin#forceDisabled `forceDisabled()`} method of the plugin to disable it on the fly like in the snippet below:
+
+```js
+ClassicEditor
+	.create( document.querySelector( '#editor' ), {
+		// ...
+	} )
+	.then( editor => {
+		const widgetTypeAroundPlugin = editor.plugins.get( 'WidgetTypeAround' );
+
+		// Disable the WidgetTypeAround plugin.
+		widgetTypeAroundPlugin.forceDisabled( 'MyApplication' );
+	} )
+	.catch( err => {
+		console.error( err.stack );
+	} );
+```
+
+<info-box hint>
+	You can always re–enable the plugin using the following snippet
+	```js
+	widgetTypeAroundPlugin.clearForceDisabled( 'MyApplication' );
+	```
+	Please refer to the {@link module:core/plugin~Plugin#clearForceDisabled API documentation} to learn more.
+</info-box>

+ 57 - 11
packages/ckeditor5-widget/src/widgettypearound/widgettypearound.js

@@ -39,6 +39,8 @@ const POSSIBLE_INSERTION_POSITIONS = [ 'before', 'after' ];
 // Do the SVG parsing once and then clone the result <svg> DOM element for each new button.
 const RETURN_ARROW_ICON_ELEMENT = new DOMParser().parseFromString( returnIcon, 'image/svg+xml' ).firstChild;
 
+const PLUGIN_DISABLED_EDITING_ROOT_CLASS = 'ck-widget__type-around_disabled';
+
 /**
  * A plugin that allows users to type around widgets where normally it is impossible to place the caret due
  * to limitations of web browsers. These "tight spots" occur, for instance, before (or after) a widget being
@@ -50,7 +52,6 @@ const RETURN_ARROW_ICON_ELEMENT = new DOMParser().parseFromString( returnIcon, '
  * in it so that users can type (or insert content, paste, etc.) straight away.
  *
  * @extends module:core/plugin~Plugin
- * @private
  */
 export default class WidgetTypeAround extends Plugin {
 	/**
@@ -81,6 +82,29 @@ export default class WidgetTypeAround extends Plugin {
 	 * @inheritDoc
 	 */
 	init() {
+		const editor = this.editor;
+		const editingView = editor.editing.view;
+
+		// Set a CSS class on the view editing root when the plugin is disabled so all the buttons
+		// and lines visually disappear. All the interactions are disabled in individual plugin methods.
+		this.on( 'change:isEnabled', ( evt, data, isEnabled ) => {
+			editingView.change( writer => {
+				for ( const root of editingView.document.roots ) {
+					if ( isEnabled ) {
+						writer.removeClass( PLUGIN_DISABLED_EDITING_ROOT_CLASS, root );
+					} else {
+						writer.addClass( PLUGIN_DISABLED_EDITING_ROOT_CLASS, root );
+					}
+				}
+			} );
+
+			if ( !isEnabled ) {
+				editor.model.change( writer => {
+					writer.removeSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE );
+				} );
+			}
+		} );
+
 		this._enableTypeAroundUIInjection();
 		this._enableInsertingParagraphsOnButtonClick();
 		this._enableInsertingParagraphsOnEnterKeypress();
@@ -119,6 +143,28 @@ export default class WidgetTypeAround extends Plugin {
 		editingView.scrollToTheSelection();
 	}
 
+	/**
+	 * A wrapper for the {@link module:utils/emittermixin~EmitterMixin#listenTo} method that executes the callbacks only
+	 * when the plugin {@link #isEnabled is enabled}.
+	 *
+	 * @private
+	 * @param {module:utils/emittermixin~Emitter} emitter The object that fires the event.
+	 * @param {String} event The name of the event.
+	 * @param {Function} callback The function to be called on event.
+	 * @param {Object} [options={}] Additional options.
+	 * @param {module:utils/priorities~PriorityString|Number} [options.priority='normal'] The priority of this event callback. The higher
+	 * the priority value the sooner the callback will be fired. Events having the same priority are called in the
+	 * order they were added.
+	 */
+	_listenToIfEnabled( emitter, event, callback, options ) {
+		this.listenTo( emitter, event, ( ...args ) => {
+			// Do not respond if the plugin is disabled.
+			if ( this.isEnabled ) {
+				callback( ...args );
+			}
+		}, options );
+	}
+
 	/**
 	 * Similar to {@link #_insertParagraph}, this method inserts a paragraph except that it
 	 * does not expect a position but it performs the insertion next to a selected widget
@@ -213,7 +259,7 @@ export default class WidgetTypeAround extends Plugin {
 		// This is the main listener responsible for the "fake caret".
 		// Note: The priority must precede the default Widget class keydown handler ("high") and the
 		// TableKeyboard keydown handler ("high-10").
-		editingView.document.on( 'keydown', ( evt, domEventData ) => {
+		this._listenToIfEnabled( editingView.document, 'keydown', ( evt, domEventData ) => {
 			if ( isArrowKeyCode( domEventData.keyCode ) ) {
 				this._handleArrowKeyPress( evt, domEventData );
 			}
@@ -223,7 +269,7 @@ export default class WidgetTypeAround extends Plugin {
 		// selection as soon as the model range changes. This attribute only makes sense when a widget is selected
 		// (and the "fake horizontal caret" is visible) so whenever the range changes (e.g. selection moved somewhere else),
 		// let's get rid of the attribute so that the selection downcast dispatcher isn't even bothered.
-		modelSelection.on( 'change:range', ( evt, data ) => {
+		this._listenToIfEnabled( modelSelection, 'change:range', ( evt, data ) => {
 			// Do not reset the selection attribute when the change was indirect.
 			if ( !data.directChange ) {
 				return;
@@ -238,7 +284,7 @@ export default class WidgetTypeAround extends Plugin {
 
 		// Get rid of the widget type around attribute of the selection on every document change
 		// that makes widget not selected any more (i.e. widget was removed).
-		model.document.on( 'change:data', () => {
+		this._listenToIfEnabled( model.document, 'change:data', () => {
 			const selectedModelElement = modelSelection.getSelectedElement();
 
 			if ( selectedModelElement ) {
@@ -257,7 +303,7 @@ export default class WidgetTypeAround extends Plugin {
 		// React to changes of the model selection attribute made by the arrow keys listener.
 		// If the block widget is selected and the attribute changes, downcast the attribute to special
 		// CSS classes associated with the active ("fake horizontal caret") mode of the widget.
-		editor.editing.downcastDispatcher.on( 'selection', ( evt, data, conversionApi ) => {
+		this._listenToIfEnabled( editor.editing.downcastDispatcher, 'selection', ( evt, data, conversionApi ) => {
 			const writer = conversionApi.writer;
 
 			if ( this._currentFakeCaretModelElement ) {
@@ -296,7 +342,7 @@ export default class WidgetTypeAround extends Plugin {
 			this._currentFakeCaretModelElement = selectedModelElement;
 		} );
 
-		this.listenTo( editor.ui.focusTracker, 'change:isFocused', ( evt, name, isFocused ) => {
+		this._listenToIfEnabled( editor.ui.focusTracker, 'change:isFocused', ( evt, name, isFocused ) => {
 			if ( !isFocused ) {
 				editor.model.change( writer => {
 					writer.removeSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE );
@@ -450,7 +496,7 @@ export default class WidgetTypeAround extends Plugin {
 		const editor = this.editor;
 		const editingView = editor.editing.view;
 
-		editingView.document.on( 'mousedown', ( evt, domEventData ) => {
+		this._listenToIfEnabled( editingView.document, 'mousedown', ( evt, domEventData ) => {
 			const button = getClosestTypeAroundDomButton( domEventData.domTarget );
 
 			if ( !button ) {
@@ -487,7 +533,7 @@ export default class WidgetTypeAround extends Plugin {
 		const editor = this.editor;
 		const editingView = editor.editing.view;
 
-		this.listenTo( editingView.document, 'enter', ( evt, domEventData ) => {
+		this._listenToIfEnabled( editingView.document, 'enter', ( evt, domEventData ) => {
 			const selectedViewElement = editingView.document.selection.getSelectedElement();
 			const selectedModelElement = editor.editing.mapper.toModelElement( selectedViewElement );
 			const schema = editor.model.schema;
@@ -543,7 +589,7 @@ export default class WidgetTypeAround extends Plugin {
 
 		// Note: The priority must precede the default Widget class keydown handler ("high") and the
 		// TableKeyboard keydown handler ("high + 1").
-		editingView.document.on( 'keydown', ( evt, domEventData ) => {
+		this._listenToIfEnabled( editingView.document, 'keydown', ( evt, domEventData ) => {
 			// Don't handle enter/backspace/delete here. They are handled in dedicated listeners.
 			if ( !keyCodesHandledSomewhereElse.includes( domEventData.keyCode ) && !isNonTypingKeystroke( domEventData ) ) {
 				this._insertParagraphAccordingToFakeCaretPosition();
@@ -568,7 +614,7 @@ export default class WidgetTypeAround extends Plugin {
 		const schema = model.schema;
 
 		// Note: The priority must precede the default Widget class delete handler.
-		this.listenTo( editingView.document, 'delete', ( evt, domEventData ) => {
+		this._listenToIfEnabled( editingView.document, 'delete', ( evt, domEventData ) => {
 			const typeAroundFakeCaretPosition = getTypeAroundFakeCaretPosition( model.document.selection );
 
 			// This listener handles only these cases when the "fake caret" is active.
@@ -647,7 +693,7 @@ export default class WidgetTypeAround extends Plugin {
 		const model = this.editor.model;
 		const documentSelection = model.document.selection;
 
-		this.listenTo( editor.model, 'insertContent', ( evt, [ content, selectable ] ) => {
+		this._listenToIfEnabled( editor.model, 'insertContent', ( evt, [ content, selectable ] ) => {
 			if ( selectable && !selectable.is( 'documentSelection' ) ) {
 				return;
 			}

+ 6 - 0
packages/ckeditor5-widget/tests/widgettypearound/utils.js

@@ -16,6 +16,12 @@ describe( 'widget type around utils', () => {
 		selection = new Selection();
 	} );
 
+	describe( 'TYPE_AROUND_SELECTION_ATTRIBUTE', () => {
+		it( 'should be defined', () => {
+			expect( TYPE_AROUND_SELECTION_ATTRIBUTE ).to.equal( 'widget-type-around' );
+		} );
+	} );
+
 	describe( 'getTypeAroundFakeCaretPosition()', () => {
 		it( 'should return "before" if the model selection attribute is "before"', () => {
 			selection.setAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );

+ 216 - 105
packages/ckeditor5-widget/tests/widgettypearound/widgettypearound.js

@@ -12,6 +12,7 @@ import ViewText from '@ckeditor/ckeditor5-engine/src/view/text';
 
 import Widget from '../../src/widget';
 import WidgetTypeAround from '../../src/widgettypearound/widgettypearound';
+import { TYPE_AROUND_SELECTION_ATTRIBUTE } from '../../src/widgettypearound/utils';
 import { toWidget } from '../../src/utils';
 
 import { setData as setModelData, getData as getModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
@@ -56,6 +57,30 @@ describe( 'WidgetTypeAround', () => {
 		it( 'should have a name', () => {
 			expect( WidgetTypeAround.pluginName ).to.equal( 'WidgetTypeAround' );
 		} );
+
+		describe( '#isEnabled support', () => {
+			it( 'should add class to the editing view root when becoming disabled', () => {
+				editor.plugins.get( WidgetTypeAround ).isEnabled = false;
+				expect( viewRoot.hasClass( 'ck-widget__type-around_disabled' ) ).to.be.true;
+
+				editor.plugins.get( WidgetTypeAround ).isEnabled = true;
+				expect( viewRoot.hasClass( 'ck-widget__type-around_disabled' ) ).to.be.false;
+			} );
+
+			it( 'should remove the model selection attribute when becoming disabled', () => {
+				setModelData( editor.model, '<blockWidget></blockWidget>' );
+
+				editor.model.change( writer => {
+					writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'foo' );
+				} );
+
+				expect( editor.model.document.selection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'foo' );
+
+				editor.plugins.get( WidgetTypeAround ).isEnabled = false;
+
+				expect( editor.model.document.selection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
+			} );
+		} );
 	} );
 
 	describe( '_insertParagraph()', () => {
@@ -299,7 +324,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowright' );
 
 				expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 				const viewWidget = viewRoot.getChild( 1 );
 
@@ -316,7 +341,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowleft' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]<paragraph>foo</paragraph>' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 				const viewWidget = viewRoot.getChild( 0 );
 
@@ -333,7 +358,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowright' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 				const viewWidget = viewRoot.getChild( 0 );
 
@@ -350,7 +375,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowleft' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 				const viewWidget = viewRoot.getChild( 0 );
 
@@ -367,7 +392,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowright', { shiftKey: true } );
 
 				expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -379,7 +404,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'a' );
 				fireMutation( 'a' );
 
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 				expect( getModelData( model ) ).to.equal( '<paragraph>fooa[]</paragraph><blockWidget></blockWidget>' );
 
 				const viewWidget = viewRoot.getChild( 1 );
@@ -396,7 +421,7 @@ describe( 'WidgetTypeAround', () => {
 
 				fireKeyboardEvent( 'arrowright' );
 
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 				const viewWidget = viewRoot.getChild( 1 );
 
@@ -412,7 +437,7 @@ describe( 'WidgetTypeAround', () => {
 
 				fireKeyboardEvent( 'arrowleft' );
 
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 				const viewWidget = viewRoot.getChild( 0 );
 
@@ -428,7 +453,7 @@ describe( 'WidgetTypeAround', () => {
 
 				fireKeyboardEvent( 'arrowright' );
 
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 				const viewWidget = viewRoot.getChild( 1 );
 
@@ -444,7 +469,7 @@ describe( 'WidgetTypeAround', () => {
 
 				fireKeyboardEvent( 'arrowleft' );
 
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 				const viewWidget = viewRoot.getChild( 0 );
 
@@ -480,7 +505,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowleft' );
 
 				expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -488,7 +513,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowleft' );
 
 				expect( getModelData( model ) ).to.equal( '<paragraph>foo[]</paragraph><blockWidget></blockWidget>' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 				const viewWidget = viewRoot.getChild( 1 );
 
@@ -505,7 +530,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowright' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]<paragraph>foo</paragraph>' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -513,7 +538,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowright' );
 
 				expect( getModelData( model ) ).to.equal( '<blockWidget></blockWidget><paragraph>[]foo</paragraph>' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 				const viewWidget = viewRoot.getChild( 0 );
 
@@ -530,7 +555,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowleft', { shiftKey: true } );
 
 				expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -538,7 +563,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowleft', { shiftKey: true } );
 
 				expect( getModelData( model ) ).to.equal( '<paragraph>foo[]</paragraph><blockWidget></blockWidget>' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -550,7 +575,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowleft' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -558,7 +583,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowleft' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -578,7 +603,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowright' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -586,7 +611,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowright' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -606,7 +631,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowleft' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -614,7 +639,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowright' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 				const viewWidget = viewRoot.getChild( 0 );
 
@@ -631,7 +656,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowright' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 				sinon.assert.calledOnce( eventInfoStub.stop );
 				sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -639,7 +664,7 @@ describe( 'WidgetTypeAround', () => {
 				fireKeyboardEvent( 'arrowleft' );
 
 				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-				expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 				const viewWidget = viewRoot.getChild( 0 );
 
@@ -651,28 +676,44 @@ describe( 'WidgetTypeAround', () => {
 			} );
 		} );
 
+		it( 'should not work when the plugin is disabled', () => {
+			editor.plugins.get( WidgetTypeAround ).isEnabled = false;
+
+			setModelData( editor.model, '<paragraph>foo[]</paragraph><blockWidget></blockWidget><paragraph>bar</paragraph>' );
+
+			fireKeyboardEvent( 'arrowright' );
+
+			expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]<paragraph>bar</paragraph>' );
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
+
+			fireKeyboardEvent( 'arrowdown' );
+
+			expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph><blockWidget></blockWidget><paragraph>[]bar</paragraph>' );
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
+		} );
+
 		it( 'should activate and deactivate the "fake caret" using all 4 arrow keys', () => {
 			setModelData( editor.model, '<paragraph>foo[]</paragraph><blockWidget></blockWidget>' );
 
 			fireKeyboardEvent( 'arrowright' );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 			fireKeyboardEvent( 'arrowdown' );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 			fireKeyboardEvent( 'arrowup' );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 			fireKeyboardEvent( 'arrowleft' );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo[]</paragraph><blockWidget></blockWidget>' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 		} );
 
 		it( 'should quit the "fake caret" mode when the editor loses focus', () => {
@@ -683,13 +724,13 @@ describe( 'WidgetTypeAround', () => {
 			fireKeyboardEvent( 'arrowright' );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 			editor.ui.focusTracker.isFocused = false;
 
 			const viewWidget = viewRoot.getChild( 1 );
 
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 			expect( viewWidget.hasClass( 'ck-widget_type-around_show-fake-caret_before' ) ).to.be.false;
 			expect( viewWidget.hasClass( 'ck-widget_type-around_show-fake-caret_after' ) ).to.be.false;
 		} );
@@ -700,7 +741,7 @@ describe( 'WidgetTypeAround', () => {
 			fireKeyboardEvent( 'arrowright' );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 			model.change( writer => {
 				writer.setSelection( model.document.getRoot().getChild( 0 ), 'in' );
@@ -708,7 +749,7 @@ describe( 'WidgetTypeAround', () => {
 
 			const viewWidget = viewRoot.getChild( 1 );
 
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 			expect( viewWidget.hasClass( 'ck-widget_type-around_show-fake-caret_before' ) ).to.be.false;
 			expect( viewWidget.hasClass( 'ck-widget_type-around_show-fake-caret_after' ) ).to.be.false;
 		} );
@@ -719,14 +760,14 @@ describe( 'WidgetTypeAround', () => {
 			fireKeyboardEvent( 'arrowright' );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 			// This could happen in collaboration.
 			model.document.selection.fire( 'change:range', {
 				directChange: false
 			} );
 
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 			const viewWidget = viewRoot.getChild( 1 );
 
@@ -740,14 +781,14 @@ describe( 'WidgetTypeAround', () => {
 			const selection = model.createSelection( modelSelection );
 
 			model.change( writer => {
-				writer.setSelectionAttribute( 'widget-type-around', 'before' );
+				writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );
 				model.deleteContent( selection );
 			} );
 
 			const viewWidget = viewRoot.getChild( 1 );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo[]</paragraph><paragraph></paragraph><paragraph>baz</paragraph>' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 			expect( viewWidget.hasClass( 'ck-widget_type-around_show-fake-caret_before' ) ).to.be.false;
 			expect( viewWidget.hasClass( 'ck-widget_type-around_show-fake-caret_after' ) ).to.be.false;
 		} );
@@ -756,12 +797,12 @@ describe( 'WidgetTypeAround', () => {
 			setModelData( editor.model, '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]<paragraph>baz</paragraph>' );
 
 			model.change( writer => {
-				writer.setSelectionAttribute( 'widget-type-around', 'before' );
+				writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );
 				writer.remove( editor.model.document.getRoot().getChild( 1 ) );
 			} );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo[]</paragraph><paragraph>baz</paragraph>' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 		} );
 
 		describe( 'inserting a new paragraph', () => {
@@ -770,7 +811,7 @@ describe( 'WidgetTypeAround', () => {
 					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
 
 					fireKeyboardEvent( 'arrowleft' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					fireKeyboardEvent( 'enter' );
 					expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph><blockWidget></blockWidget>' );
@@ -780,7 +821,7 @@ describe( 'WidgetTypeAround', () => {
 					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
 
 					fireKeyboardEvent( 'arrowright' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					fireKeyboardEvent( 'enter' );
 					expect( getModelData( model ) ).to.equal( '<blockWidget></blockWidget><paragraph>[]</paragraph>' );
@@ -797,49 +838,62 @@ describe( 'WidgetTypeAround', () => {
 					editor.execute( 'undo' );
 
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
+				} );
+
+				it( 'should not work when the plugin is disabled', () => {
+					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
+
+					editor.plugins.get( WidgetTypeAround ).isEnabled = false;
+
+					model.change( writer => {
+						writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'after' );
+					} );
+
+					fireKeyboardEvent( 'enter' );
+					expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph>' );
 				} );
 			} );
 
 			describe( 'on Enter key press when the widget is selected (no "fake caret", though)', () => {
 				it( 'should insert a new paragraph after the widget if Enter was pressed', () => {
 					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					fireKeyboardEvent( 'enter' );
 
 					expect( getModelData( model ) ).to.equal( '<blockWidget></blockWidget><paragraph>[]</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 				} );
 
 				it( 'should insert a new paragraph before the widget if Shift+Enter was pressed', () => {
 					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					fireKeyboardEvent( 'enter', { shiftKey: true } );
 
 					expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph><blockWidget></blockWidget>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 				} );
 
 				it( 'should insert a new paragraph only if an entire widget is selected (selected nested editable content)', () => {
 					setModelData( editor.model, '<blockWidget><nested>[foo] bar</nested></blockWidget>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					fireKeyboardEvent( 'enter' );
 
 					expect( getModelData( model ) ).to.equal( '<blockWidget><nested>[] bar</nested></blockWidget>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 				} );
 
 				it( 'should insert a new paragraph only if an entire widget is selected (selected widget siblings)', () => {
 					setModelData( editor.model, '<paragraph>f[oo</paragraph><blockWidget></blockWidget><paragraph>o]o</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					fireKeyboardEvent( 'enter' );
 
 					expect( getModelData( model ) ).to.equal( '<paragraph>f</paragraph><paragraph>[]o</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 				} );
 
 				it( 'should split ancestors to find a place that allows a widget', () => {
@@ -879,27 +933,37 @@ describe( 'WidgetTypeAround', () => {
 
 				it( 'should integrate with the undo feature', () => {
 					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					fireKeyboardEvent( 'enter' );
 
 					expect( getModelData( model ) ).to.equal( '<blockWidget></blockWidget><paragraph>[]</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					editor.execute( 'undo' );
 
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 				} );
 
 				it( 'should do nothing if a non-type-around-friendly content is selected', () => {
 					setModelData( editor.model, '<paragraph>foo[<inlineWidget></inlineWidget>]</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					fireKeyboardEvent( 'enter' );
 
 					expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph><paragraph>[]</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
+				} );
+
+				it( 'should not work when the plugin is disabled', () => {
+					editor.plugins.get( WidgetTypeAround ).isEnabled = false;
+
+					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
+
+					fireKeyboardEvent( 'enter' );
+
+					expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph>' );
 				} );
 			} );
 
@@ -908,40 +972,40 @@ describe( 'WidgetTypeAround', () => {
 					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
 
 					fireKeyboardEvent( 'arrowleft' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					fireKeyboardEvent( 'a' );
 					fireMutation( 'a' );
 
 					expect( getModelData( model ) ).to.equal( '<paragraph>a[]</paragraph><blockWidget></blockWidget>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 				} );
 
 				it( 'should insert a character inside a new paragraph after a widget if the caret was "after" it', () => {
 					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
 
 					fireKeyboardEvent( 'arrowright' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					fireKeyboardEvent( 'a' );
 					fireMutation( 'a' );
 
 					expect( getModelData( model ) ).to.equal( '<blockWidget></blockWidget><paragraph>a[]</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 				} );
 
 				it( 'should do nothing if a "safe" keystroke was pressed', () => {
 					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
 
 					fireKeyboardEvent( 'arrowright' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					fireKeyboardEvent( 'esc' );
 					fireKeyboardEvent( 'tab' );
 					fireKeyboardEvent( 'd', { ctrlKey: true } );
 
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 				} );
 
 				it( 'should integrate with the undo feature', () => {
@@ -955,11 +1019,27 @@ describe( 'WidgetTypeAround', () => {
 
 					editor.execute( 'undo' );
 					expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph><blockWidget></blockWidget>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					editor.execute( 'undo' );
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
+				} );
+
+				it( 'should not work when the plugin is disabled', () => {
+					setModelData( editor.model, '[<blockWidget></blockWidget>]' );
+
+					editor.plugins.get( WidgetTypeAround ).isEnabled = false;
+
+					model.change( writer => {
+						writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );
+					} );
+
+					fireKeyboardEvent( 'a' );
+					fireMutation( 'a' );
+
+					expect( getModelData( model ) ).to.equal( '<paragraph>a[]</paragraph>' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 				} );
 			} );
 		} );
@@ -974,11 +1054,11 @@ describe( 'WidgetTypeAround', () => {
 					fireKeyboardEvent( 'arrowleft' );
 
 					expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					fireDeleteEvent();
 					expect( getModelData( model ) ).to.equal( '<paragraph>fo[]</paragraph><blockWidget></blockWidget>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -990,11 +1070,11 @@ describe( 'WidgetTypeAround', () => {
 					fireKeyboardEvent( 'arrowleft' );
 
 					expect( getModelData( model ) ).to.equal( '<paragraph></paragraph>[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					fireDeleteEvent();
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1011,11 +1091,11 @@ describe( 'WidgetTypeAround', () => {
 						'</blockQuote>' +
 						'[<blockWidget></blockWidget>]'
 					);
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					fireDeleteEvent();
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1041,7 +1121,7 @@ describe( 'WidgetTypeAround', () => {
 						'</blockQuote>' +
 						'[<blockWidget></blockWidget>]'
 					);
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					// Assert that the paragraph is merged rather than deleted because
 					// it is safer for collaboration.
@@ -1056,7 +1136,7 @@ describe( 'WidgetTypeAround', () => {
 						'</blockQuote>' +
 						'<blockWidget></blockWidget>'
 					);
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 					expect( operationType ).to.equal( 'merge' );
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
@@ -1069,11 +1149,11 @@ describe( 'WidgetTypeAround', () => {
 					fireKeyboardEvent( 'arrowleft' );
 
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					fireDeleteEvent();
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1085,11 +1165,11 @@ describe( 'WidgetTypeAround', () => {
 					fireKeyboardEvent( 'arrowright' );
 
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					fireDeleteEvent();
 					expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1101,11 +1181,11 @@ describe( 'WidgetTypeAround', () => {
 					fireKeyboardEvent( 'arrowright' );
 
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]<paragraph>foo</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					fireDeleteEvent();
 					expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph><paragraph>foo</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1120,11 +1200,11 @@ describe( 'WidgetTypeAround', () => {
 						'<blockWidget><nested>foo</nested></blockWidget>' +
 						'[<blockWidget></blockWidget>]'
 					);
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					fireDeleteEvent();
 					expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph><blockWidget></blockWidget>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1138,11 +1218,11 @@ describe( 'WidgetTypeAround', () => {
 					fireKeyboardEvent( 'arrowright' );
 
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]<paragraph>foo</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					fireDeleteEvent( true );
 					expect( getModelData( model ) ).to.equal( '<blockWidget></blockWidget><paragraph>[]oo</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1154,11 +1234,11 @@ describe( 'WidgetTypeAround', () => {
 					fireKeyboardEvent( 'arrowright' );
 
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]<paragraph></paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					fireDeleteEvent( true );
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1173,11 +1253,11 @@ describe( 'WidgetTypeAround', () => {
 						'[<blockWidget></blockWidget>]' +
 						'<blockQuote><paragraph></paragraph></blockQuote>'
 					);
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					fireDeleteEvent( true );
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1203,7 +1283,7 @@ describe( 'WidgetTypeAround', () => {
 							'<paragraph>foo</paragraph>' +
 						'</blockQuote>'
 					);
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					// Assert that the paragraph is merged rather than deleted because
 					// it is safer for collaboration.
@@ -1218,7 +1298,7 @@ describe( 'WidgetTypeAround', () => {
 							'<paragraph>[]foo</paragraph>' +
 						'</blockQuote>'
 					);
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 					expect( operationType ).to.equal( 'merge' );
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
@@ -1231,11 +1311,11 @@ describe( 'WidgetTypeAround', () => {
 					fireKeyboardEvent( 'arrowright' );
 
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					fireDeleteEvent( true );
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1247,11 +1327,11 @@ describe( 'WidgetTypeAround', () => {
 					fireKeyboardEvent( 'arrowleft' );
 
 					expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					fireDeleteEvent( true );
 					expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1263,11 +1343,11 @@ describe( 'WidgetTypeAround', () => {
 					fireKeyboardEvent( 'arrowleft' );
 
 					expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph>[<blockWidget></blockWidget>]' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'before' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
 
 					fireDeleteEvent( true );
 					expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph><paragraph>[]</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
@@ -1282,17 +1362,34 @@ describe( 'WidgetTypeAround', () => {
 						'[<blockWidget></blockWidget>]' +
 						'<blockWidget><nested>foo</nested></blockWidget>'
 					);
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.equal( 'after' );
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'after' );
 
 					fireDeleteEvent( true );
 					expect( getModelData( model ) ).to.equal( '<blockWidget></blockWidget><paragraph>[]</paragraph>' );
-					expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+					expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 					sinon.assert.calledOnce( eventInfoStub.stop );
 					sinon.assert.calledOnce( domEventDataStub.domEvent.preventDefault );
 				} );
 			} );
 
+			it( 'should not work when the plugin is disabled', () => {
+				setModelData( editor.model, '[<blockWidget></blockWidget>]' );
+				fireKeyboardEvent( 'arrowleft' );
+
+				expect( getModelData( model ) ).to.equal( '[<blockWidget></blockWidget>]' );
+				expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.equal( 'before' );
+
+				editor.plugins.get( WidgetTypeAround ).isEnabled = false;
+
+				model.change( writer => {
+					writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );
+				} );
+
+				fireDeleteEvent();
+				expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph>' );
+			} );
+
 			function fireDeleteEvent( isForward = false ) {
 				eventInfoStub = new EventInfo( viewDocument, 'delete' );
 				sinon.spy( eventInfoStub, 'stop' );
@@ -1362,7 +1459,7 @@ describe( 'WidgetTypeAround', () => {
 			const selection = model.createSelection( modelSelection );
 
 			model.change( writer => {
-				writer.setSelectionAttribute( 'widget-type-around', 'before' );
+				writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );
 				model.insertContent( createParagraph( 'bar' ), selection );
 			} );
 
@@ -1375,12 +1472,12 @@ describe( 'WidgetTypeAround', () => {
 
 			const batchSet = setupBatchWatch();
 
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 
 			model.insertContent( createParagraph( 'bar' ) );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph><paragraph>bar[]</paragraph><paragraph>baz</paragraph>' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 			expect( batchSet.size ).to.be.equal( 1 );
 		} );
 
@@ -1390,13 +1487,13 @@ describe( 'WidgetTypeAround', () => {
 			const batchSet = setupBatchWatch();
 
 			model.change( writer => {
-				writer.setSelectionAttribute( 'widget-type-around', 'before' );
+				writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );
 			} );
 
 			model.insertContent( createParagraph( 'bar' ) );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>bar[]</paragraph><blockWidget></blockWidget>' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 			expect( batchSet.size ).to.be.equal( 1 );
 		} );
 
@@ -1406,13 +1503,13 @@ describe( 'WidgetTypeAround', () => {
 			const batchSet = setupBatchWatch();
 
 			model.change( writer => {
-				writer.setSelectionAttribute( 'widget-type-around', 'after' );
+				writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'after' );
 			} );
 
 			model.insertContent( createParagraph( 'bar' ) );
 
 			expect( getModelData( model ) ).to.equal( '<blockWidget></blockWidget><paragraph>bar[]</paragraph>' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 			expect( batchSet.size ).to.be.equal( 1 );
 		} );
 
@@ -1422,13 +1519,13 @@ describe( 'WidgetTypeAround', () => {
 			const batchSet = setupBatchWatch();
 
 			model.change( writer => {
-				writer.setSelectionAttribute( 'widget-type-around', 'before' );
+				writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );
 			} );
 
 			model.insertContent( createParagraph( 'bar' ) );
 
 			expect( getModelData( model ) ).to.equal( '<paragraph>foo</paragraph><paragraph>bar[]</paragraph><blockWidget></blockWidget>' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 			expect( batchSet.size ).to.be.equal( 1 );
 		} );
 
@@ -1438,16 +1535,30 @@ describe( 'WidgetTypeAround', () => {
 			const batchSet = setupBatchWatch();
 
 			model.change( writer => {
-				writer.setSelectionAttribute( 'widget-type-around', 'after' );
+				writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'after' );
 			} );
 
 			model.insertContent( createParagraph( 'bar' ) );
 
 			expect( getModelData( model ) ).to.equal( '<blockWidget></blockWidget><paragraph>bar[]</paragraph><paragraph>foo</paragraph>' );
-			expect( modelSelection.getAttribute( 'widget-type-around' ) ).to.be.undefined;
+			expect( modelSelection.getAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE ) ).to.be.undefined;
 			expect( batchSet.size ).to.be.equal( 1 );
 		} );
 
+		it( 'should not work when the plugin is disabled', () => {
+			setModelData( editor.model, '[<blockWidget></blockWidget>]' );
+
+			editor.plugins.get( WidgetTypeAround ).isEnabled = false;
+
+			model.change( writer => {
+				writer.setSelectionAttribute( TYPE_AROUND_SELECTION_ATTRIBUTE, 'before' );
+			} );
+
+			model.insertContent( createParagraph( 'bar' ) );
+
+			expect( getModelData( model ) ).to.equal( '<paragraph>bar[]</paragraph>' );
+		} );
+
 		function createParagraph( text ) {
 			return model.change( writer => {
 				const paragraph = writer.createElement( 'paragraph' );

+ 7 - 0
packages/ckeditor5-widget/theme/widgettypearound.css

@@ -104,3 +104,10 @@
 .ck.ck-editor__editable.ck-restricted-editing_mode_restricted .ck-widget__type-around {
 	display: none;
 }
+
+/*
+ * Integration with the #isEnabled property of the WidgetTypeAround plugin.
+ */
+.ck.ck-editor__editable.ck-widget__type-around_disabled .ck-widget__type-around {
+	display: none;
+}