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

Merge branch 'master' into t/ckeditor5/1151

Aleksander Nowodzinski 6 лет назад
Родитель
Сommit
9e8d7cc080

+ 2 - 2
packages/ckeditor5-ui/docs/features/blocktoolbar.md

@@ -5,12 +5,12 @@ category: features
 
 
 The {@link module:ui/toolbar/block/blocktoolbar~BlockToolbar} plugin provides an additional [configurable](#configuration) toolbar on the left-hand side of the content area (the gutter). The toolbar is represented by a button with a pilcrow (or a paragraph mark — ¶). It is positioned next to the block element that the selection is anchored to (e.g. a paragraph), following the caret as the user edits the content and navigates the document.
 The {@link module:ui/toolbar/block/blocktoolbar~BlockToolbar} plugin provides an additional [configurable](#configuration) toolbar on the left-hand side of the content area (the gutter). The toolbar is represented by a button with a pilcrow (or a paragraph mark — ¶). It is positioned next to the block element that the selection is anchored to (e.g. a paragraph), following the caret as the user edits the content and navigates the document.
 
 
-The block toolbar comes in handy when the main editor toolbar cannot be accessed. It complements the {@link builds/guides/overview#balloon-editor Balloon editor} when it falls short, for example when some content must be inserted (like an image) but the selection is collapsed, leaving the user unable to access the toolbar.
+The block toolbar comes in handy when the main editor toolbar cannot be accessed. It complements the {@link builds/guides/overview#balloon-editor Balloon editor} when it falls short, for example when some content must be inserted (like an image) but the selection is collapsed, leaving the user unable to access the toolbar. You can read more about it in the {@link builds/guides/overview#balloon-block-editor balloon block editor overview}.
 
 
 ## Example
 ## Example
 
 
 <info-box hint>
 <info-box hint>
-	Move the caret around the content with the balloon editor below to see the block toolbar button following the selection. Click the button (&#182;) to use the toolbar, for example to create a header or insert an image.
+	Move the caret around the content with the balloon editor below to see the block toolbar button following the selection. Click the button (&#182;) to use the toolbar, for example, to create a header or insert an image. See the {@link examples/builds/balloon-block-editor balloon block editor example} page, too.
 </info-box>
 </info-box>
 
 
 {@snippet features/blocktoolbar}
 {@snippet features/blocktoolbar}

+ 2 - 1
packages/ckeditor5-ui/src/labeledinput/labeledinputview.js

@@ -210,7 +210,8 @@ export default class LabeledInputView extends View {
 					bind.if( 'errorText', 'ck-labeled-input__status_error' ),
 					bind.if( 'errorText', 'ck-labeled-input__status_error' ),
 					bind.if( '_statusText', 'ck-hidden', value => !value )
 					bind.if( '_statusText', 'ck-hidden', value => !value )
 				],
 				],
-				id: statusUid
+				id: statusUid,
+				role: bind.if( 'errorText', 'alert' )
 			},
 			},
 			children: [
 			children: [
 				{
 				{

+ 5 - 3
packages/ckeditor5-ui/src/template.js

@@ -15,7 +15,6 @@ import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
 import View from './view';
 import View from './view';
 import ViewCollection from './viewcollection';
 import ViewCollection from './viewcollection';
 import isNode from '@ckeditor/ckeditor5-utils/src/dom/isnode';
 import isNode from '@ckeditor/ckeditor5-utils/src/dom/isnode';
-import log from '@ckeditor/ckeditor5-utils/src/log';
 import { isObject, cloneDeepWith } from 'lodash-es';
 import { isObject, cloneDeepWith } from 'lodash-es';
 
 
 const xhtmlNs = 'http://www.w3.org/1999/xhtml';
 const xhtmlNs = 'http://www.w3.org/1999/xhtml';
@@ -39,7 +38,7 @@ const xhtmlNs = 'http://www.w3.org/1999/xhtml';
  *			},
  *			},
  *			on: {
  *			on: {
  *				click: bind.to( 'clicked' )
  *				click: bind.to( 'clicked' )
- *			}
+ *			},
  *			children: [
  *			children: [
  *				'A paragraph.'
  *				'A paragraph.'
  *			]
  *			]
@@ -381,7 +380,10 @@ export default class Template {
 			 *
 			 *
 			 * @error template-extend-render
 			 * @error template-extend-render
 			 */
 			 */
-			log.warn( 'template-extend-render: Attempting to extend a template which has already been rendered.' );
+			throw new CKEditorError(
+				'template-extend-render: Attempting to extend a template which has already been rendered.',
+				[ this, template ]
+			);
 		}
 		}
 
 
 		extendTemplate( template, normalize( clone( def ) ) );
 		extendTemplate( template, normalize( clone( def ) ) );

+ 5 - 5
packages/ckeditor5-ui/src/toolbar/toolbarview.js

@@ -7,15 +7,17 @@
  * @module ui/toolbar/toolbarview
  * @module ui/toolbar/toolbarview
  */
  */
 
 
+/* globals console */
+
 import View from '../view';
 import View from '../view';
 import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
 import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
 import FocusCycler from '../focuscycler';
 import FocusCycler from '../focuscycler';
 import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
 import KeystrokeHandler from '@ckeditor/ckeditor5-utils/src/keystrokehandler';
 import ToolbarSeparatorView from './toolbarseparatorview';
 import ToolbarSeparatorView from './toolbarseparatorview';
 import preventDefault from '../bindings/preventdefault.js';
 import preventDefault from '../bindings/preventdefault.js';
-import log from '@ckeditor/ckeditor5-utils/src/log';
 
 
 import '../../theme/components/toolbar/toolbar.css';
 import '../../theme/components/toolbar/toolbar.css';
+import { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 
 /**
 /**
  * The toolbar view class.
  * The toolbar view class.
@@ -180,10 +182,8 @@ export default class ToolbarView extends View {
 				 * @error toolbarview-item-unavailable
 				 * @error toolbarview-item-unavailable
 				 * @param {String} name The name of the component.
 				 * @param {String} name The name of the component.
 				 */
 				 */
-				log.warn(
-					'toolbarview-item-unavailable: The requested toolbar item is unavailable.',
-					{ name }
-				);
+				console.warn( attachLinkToDocumentation(
+					'toolbarview-item-unavailable: The requested toolbar item is unavailable.' ), { name } );
 			}
 			}
 		} );
 		} );
 	}
 	}

+ 4 - 0
packages/ckeditor5-ui/tests/labeledinput/labeledinputview.js

@@ -93,11 +93,13 @@ describe( 'LabeledInputView', () => {
 					view.errorText = '';
 					view.errorText = '';
 					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.true;
 					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.true;
 					expect( statusElement.classList.contains( 'ck-labeled-input__status_error' ) ).to.be.false;
 					expect( statusElement.classList.contains( 'ck-labeled-input__status_error' ) ).to.be.false;
+					expect( statusElement.hasAttribute( 'role' ) ).to.be.false;
 					expect( statusElement.innerHTML ).to.equal( '' );
 					expect( statusElement.innerHTML ).to.equal( '' );
 
 
 					view.errorText = 'foo';
 					view.errorText = 'foo';
 					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.false;
 					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.false;
 					expect( statusElement.classList.contains( 'ck-labeled-input__status_error' ) ).to.be.true;
 					expect( statusElement.classList.contains( 'ck-labeled-input__status_error' ) ).to.be.true;
+					expect( statusElement.getAttribute( 'role' ) ).to.equal( 'alert' );
 					expect( statusElement.innerHTML ).to.equal( 'foo' );
 					expect( statusElement.innerHTML ).to.equal( 'foo' );
 				} );
 				} );
 
 
@@ -107,11 +109,13 @@ describe( 'LabeledInputView', () => {
 					view.infoText = '';
 					view.infoText = '';
 					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.true;
 					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.true;
 					expect( statusElement.classList.contains( 'ck-labeled-input__status_error' ) ).to.be.false;
 					expect( statusElement.classList.contains( 'ck-labeled-input__status_error' ) ).to.be.false;
+					expect( statusElement.hasAttribute( 'role' ) ).to.be.false;
 					expect( statusElement.innerHTML ).to.equal( '' );
 					expect( statusElement.innerHTML ).to.equal( '' );
 
 
 					view.infoText = 'foo';
 					view.infoText = 'foo';
 					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.false;
 					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.false;
 					expect( statusElement.classList.contains( 'ck-labeled-input__status_error' ) ).to.be.false;
 					expect( statusElement.classList.contains( 'ck-labeled-input__status_error' ) ).to.be.false;
+					expect( statusElement.hasAttribute( 'role' ) ).to.be.false;
 					expect( statusElement.innerHTML ).to.equal( 'foo' );
 					expect( statusElement.innerHTML ).to.equal( 'foo' );
 				} );
 				} );
 			} );
 			} );

+ 33 - 38
packages/ckeditor5-ui/tests/template.js

@@ -5,7 +5,6 @@
 
 
 /* globals HTMLElement, Event, document */
 /* globals HTMLElement, Event, document */
 
 
-import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 import { default as Template, TemplateToBinding, TemplateIfBinding } from '../src/template';
 import { default as Template, TemplateToBinding, TemplateIfBinding } from '../src/template';
 import View from '../src/view';
 import View from '../src/view';
 import ViewCollection from '../src/viewcollection';
 import ViewCollection from '../src/viewcollection';
@@ -14,7 +13,6 @@ import Model from '../src/model';
 import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
 import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
 import DomEmitterMixin from '@ckeditor/ckeditor5-utils/src/dom/emittermixin';
 import DomEmitterMixin from '@ckeditor/ckeditor5-utils/src/dom/emittermixin';
 import normalizeHtml from '@ckeditor/ckeditor5-utils/tests/_utils/normalizehtml';
 import normalizeHtml from '@ckeditor/ckeditor5-utils/tests/_utils/normalizehtml';
-import log from '@ckeditor/ckeditor5-utils/src/log';
 
 
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
 import { expectToThrowCKEditorError } from '@ckeditor/ckeditor5-utils/tests/_utils/utils';
 
 
@@ -22,10 +20,10 @@ let el, text;
 const injectedElements = [];
 const injectedElements = [];
 
 
 describe( 'Template', () => {
 describe( 'Template', () => {
-	testUtils.createSinonSandbox();
-
 	// Clean-up document.body from the rendered elements.
 	// Clean-up document.body from the rendered elements.
 	afterEach( () => {
 	afterEach( () => {
+		sinon.restore();
+
 		for ( const el of injectedElements ) {
 		for ( const el of injectedElements ) {
 			el.remove();
 			el.remove();
 		}
 		}
@@ -1026,9 +1024,9 @@ describe( 'Template', () => {
 					'<div><a class="a1 a2">a</a><b class="b1 b2">b</b></div>'
 					'<div><a class="a1 a2">a</a><b class="b1 b2">b</b></div>'
 				);
 				);
 
 
-				const spy1 = testUtils.sinon.spy();
-				const spy2 = testUtils.sinon.spy();
-				const spy3 = testUtils.sinon.spy();
+				const spy1 = sinon.spy();
+				const spy2 = sinon.spy();
+				const spy3 = sinon.spy();
 
 
 				observable.on( 'ku', spy1 );
 				observable.on( 'ku', spy1 );
 				observable.on( 'kd', spy2 );
 				observable.on( 'kd', spy2 );
@@ -1593,7 +1591,7 @@ describe( 'Template', () => {
 			} );
 			} );
 
 
 			it( 'accepts plain binding', () => {
 			it( 'accepts plain binding', () => {
-				const spy = testUtils.sinon.spy();
+				const spy = sinon.spy();
 
 
 				setElement( {
 				setElement( {
 					tag: 'p',
 					tag: 'p',
@@ -1612,8 +1610,8 @@ describe( 'Template', () => {
 			} );
 			} );
 
 
 			it( 'accepts an array of event bindings', () => {
 			it( 'accepts an array of event bindings', () => {
-				const spy1 = testUtils.sinon.spy();
-				const spy2 = testUtils.sinon.spy();
+				const spy1 = sinon.spy();
+				const spy2 = sinon.spy();
 
 
 				setElement( {
 				setElement( {
 					tag: 'p',
 					tag: 'p',
@@ -1640,9 +1638,9 @@ describe( 'Template', () => {
 			} );
 			} );
 
 
 			it( 'accepts DOM selectors', () => {
 			it( 'accepts DOM selectors', () => {
-				const spy1 = testUtils.sinon.spy();
-				const spy2 = testUtils.sinon.spy();
-				const spy3 = testUtils.sinon.spy();
+				const spy1 = sinon.spy();
+				const spy2 = sinon.spy();
+				const spy3 = sinon.spy();
 
 
 				setElement( {
 				setElement( {
 					tag: 'p',
 					tag: 'p',
@@ -1721,8 +1719,8 @@ describe( 'Template', () => {
 			} );
 			} );
 
 
 			it( 'accepts function callbacks', () => {
 			it( 'accepts function callbacks', () => {
-				const spy1 = testUtils.sinon.spy();
-				const spy2 = testUtils.sinon.spy();
+				const spy1 = sinon.spy();
+				const spy2 = sinon.spy();
 
 
 				setElement( {
 				setElement( {
 					tag: 'p',
 					tag: 'p',
@@ -1753,7 +1751,7 @@ describe( 'Template', () => {
 			} );
 			} );
 
 
 			it( 'supports event delegation', () => {
 			it( 'supports event delegation', () => {
-				const spy = testUtils.sinon.spy();
+				const spy = sinon.spy();
 
 
 				setElement( {
 				setElement( {
 					tag: 'p',
 					tag: 'p',
@@ -1777,7 +1775,7 @@ describe( 'Template', () => {
 			} );
 			} );
 
 
 			it( 'works for future elements', () => {
 			it( 'works for future elements', () => {
-				const spy = testUtils.sinon.spy();
+				const spy = sinon.spy();
 
 
 				setElement( {
 				setElement( {
 					tag: 'p',
 					tag: 'p',
@@ -1811,7 +1809,7 @@ describe( 'Template', () => {
 
 
 			describe( 'to', () => {
 			describe( 'to', () => {
 				it( 'returns an instance of TemplateToBinding', () => {
 				it( 'returns an instance of TemplateToBinding', () => {
-					const spy = testUtils.sinon.spy();
+					const spy = sinon.spy();
 					const binding = bind.to( 'foo', spy );
 					const binding = bind.to( 'foo', spy );
 
 
 					expect( binding ).to.be.instanceof( TemplateToBinding );
 					expect( binding ).to.be.instanceof( TemplateToBinding );
@@ -2034,7 +2032,7 @@ describe( 'Template', () => {
 
 
 			describe( 'if', () => {
 			describe( 'if', () => {
 				it( 'returns an object which describes the binding', () => {
 				it( 'returns an object which describes the binding', () => {
-					const spy = testUtils.sinon.spy();
+					const spy = sinon.spy();
 					const binding = bind.if( 'foo', 'whenTrue', spy );
 					const binding = bind.if( 'foo', 'whenTrue', spy );
 
 
 					expect( binding ).to.be.instanceof( TemplateIfBinding );
 					expect( binding ).to.be.instanceof( TemplateIfBinding );
@@ -2333,9 +2331,7 @@ describe( 'Template', () => {
 			expect( tpl.attributes.b[ 0 ] ).to.equal( 'bar' );
 			expect( tpl.attributes.b[ 0 ] ).to.equal( 'bar' );
 		} );
 		} );
 
 
-		it( 'logs a warning if an element has already been rendered', () => {
-			const spy = testUtils.sinon.stub( log, 'warn' );
-
+		it( 'throws an error if an element has already been rendered', () => {
 			const tpl = new Template( {
 			const tpl = new Template( {
 				tag: 'p'
 				tag: 'p'
 			} );
 			} );
@@ -2348,14 +2344,13 @@ describe( 'Template', () => {
 
 
 			tpl.render();
 			tpl.render();
 
 
-			Template.extend( tpl, {
-				attributes: {
-					class: 'bar'
-				}
-			} );
-
-			sinon.assert.calledOnce( spy );
-			sinon.assert.calledWithExactly( spy, sinon.match( /^template-extend-render/ ) );
+			expectToThrowCKEditorError( () => {
+				Template.extend( tpl, {
+					attributes: {
+						class: 'bar'
+					}
+				} );
+			}, /^template-extend-render/ );
 		} );
 		} );
 
 
 		describe( 'attributes', () => {
 		describe( 'attributes', () => {
@@ -2918,11 +2913,11 @@ describe( 'Template', () => {
 
 
 		describe( 'listeners', () => {
 		describe( 'listeners', () => {
 			it( 'extends existing', () => {
 			it( 'extends existing', () => {
-				const spy1 = testUtils.sinon.spy();
-				const spy2 = testUtils.sinon.spy();
-				const spy3 = testUtils.sinon.spy();
-				const spy4 = testUtils.sinon.spy();
-				const spy5 = testUtils.sinon.spy();
+				const spy1 = sinon.spy();
+				const spy2 = sinon.spy();
+				const spy3 = sinon.spy();
+				const spy4 = sinon.spy();
+				const spy5 = sinon.spy();
 
 
 				observable.on( 'A', spy1 );
 				observable.on( 'A', spy1 );
 				observable.on( 'C', spy2 );
 				observable.on( 'C', spy2 );
@@ -2973,9 +2968,9 @@ describe( 'Template', () => {
 			} );
 			} );
 
 
 			it( 'creates new', () => {
 			it( 'creates new', () => {
-				const spy1 = testUtils.sinon.spy();
-				const spy2 = testUtils.sinon.spy();
-				const spy3 = testUtils.sinon.spy();
+				const spy1 = sinon.spy();
+				const spy2 = sinon.spy();
+				const spy3 = sinon.spy();
 
 
 				observable.on( 'A', spy1 );
 				observable.on( 'A', spy1 );
 				observable.on( 'B', spy2 );
 				observable.on( 'B', spy2 );

+ 5 - 8
packages/ckeditor5-ui/tests/toolbar/toolbarview.js

@@ -3,7 +3,7 @@
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
  */
 
 
-/* global document, Event */
+/* global document, Event, console */
 
 
 import ToolbarView from '../../src/toolbar/toolbarview';
 import ToolbarView from '../../src/toolbar/toolbarview';
 import ToolbarSeparatorView from '../../src/toolbar/toolbarseparatorview';
 import ToolbarSeparatorView from '../../src/toolbar/toolbarseparatorview';
@@ -13,15 +13,11 @@ import FocusTracker from '@ckeditor/ckeditor5-utils/src/focustracker';
 import FocusCycler from '../../src/focuscycler';
 import FocusCycler from '../../src/focuscycler';
 import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
 import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
 import ViewCollection from '../../src/viewcollection';
 import ViewCollection from '../../src/viewcollection';
-import log from '@ckeditor/ckeditor5-utils/src/log';
 import View from '../../src/view';
 import View from '../../src/view';
-import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 
 describe( 'ToolbarView', () => {
 describe( 'ToolbarView', () => {
 	let locale, view;
 	let locale, view;
 
 
-	testUtils.createSinonSandbox();
-
 	beforeEach( () => {
 	beforeEach( () => {
 		locale = {};
 		locale = {};
 		view = new ToolbarView( locale );
 		view = new ToolbarView( locale );
@@ -29,6 +25,7 @@ describe( 'ToolbarView', () => {
 	} );
 	} );
 
 
 	afterEach( () => {
 	afterEach( () => {
+		sinon.restore();
 		view.destroy();
 		view.destroy();
 	} );
 	} );
 
 
@@ -306,7 +303,7 @@ describe( 'ToolbarView', () => {
 
 
 		it( 'warns if there is no such component in the factory', () => {
 		it( 'warns if there is no such component in the factory', () => {
 			const items = view.items;
 			const items = view.items;
-			testUtils.sinon.stub( log, 'warn' );
+			const consoleWarnStub = sinon.stub( console, 'warn' );
 
 
 			view.fillFromConfig( [ 'foo', 'bar', 'baz' ], factory );
 			view.fillFromConfig( [ 'foo', 'bar', 'baz' ], factory );
 
 
@@ -314,8 +311,8 @@ describe( 'ToolbarView', () => {
 			expect( items.get( 0 ).name ).to.equal( 'foo' );
 			expect( items.get( 0 ).name ).to.equal( 'foo' );
 			expect( items.get( 1 ).name ).to.equal( 'bar' );
 			expect( items.get( 1 ).name ).to.equal( 'bar' );
 
 
-			sinon.assert.calledOnce( log.warn );
-			sinon.assert.calledWithExactly( log.warn,
+			sinon.assert.calledOnce( consoleWarnStub );
+			sinon.assert.calledWithExactly( consoleWarnStub,
 				sinon.match( /^toolbarview-item-unavailable/ ),
 				sinon.match( /^toolbarview-item-unavailable/ ),
 				{ name: 'baz' }
 				{ name: 'baz' }
 			);
 			);