Ver código fonte

Merge branch 'master' into i/6319

Marek Lewandowski 5 anos atrás
pai
commit
7d852f85ac

+ 96 - 0
packages/ckeditor5-ui/src/formheader/formheaderview.js

@@ -0,0 +1,96 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module ui/formheader/formheaderview
+ */
+
+import View from '../view';
+
+import '../../theme/components/formheader/formheader.css';
+
+/**
+ * The class component representing a form header view. It should be used in more advanced forms to
+ * describe the main purpose of the form.
+ *
+ * By default the component contains a bolded label view that has to be set. The label is usually a short (at most 3-word) string.
+ * The component can also be extended by any other elements, like: icons, dropdowns, etc.
+ *
+ * It is used i.a.
+ * by {@link module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView}
+ * and {@link module:special-characters/ui/specialcharactersnavigationview~SpecialCharactersNavigationView}.
+ *
+ * The latter is an example, where the component has been extended by {@link module:ui/dropdown/dropdownview~DropdownView} view.
+ *
+ * @extends module:ui/view~View
+ */
+export default class FormHeaderView extends View {
+	/**
+	 * Creates an instance of the form header class.
+	 *
+	 * @param {module:utils/locale~Locale} locale The locale instance.
+	 * @param {Object} options
+	 * @param {String} options.label A label.
+	 * @param {String} [options.class] An additional class.
+	 */
+	constructor( locale, options = {} ) {
+		super( locale );
+
+		const bind = this.bindTemplate;
+
+		/**
+		 * The label of the header.
+		 *
+		 * @observable
+		 * @member {String} #label
+		 */
+		this.set( 'label', options.label || '' );
+
+		/**
+		 * An additional CSS class added to the {@link #element}.
+		 *
+		 * @observable
+		 * @member {String} #class
+		 */
+		this.set( 'class', options.class || null );
+
+		/**
+		 * A collection of header items.
+		 *
+		 * @readonly
+		 * @member {module:ui/viewcollection~ViewCollection}
+		 */
+		this.children = this.createCollection();
+
+		this.setTemplate( {
+			tag: 'div',
+			attributes: {
+				class: [
+					'ck',
+					'ck-form__header',
+					bind.to( 'class' )
+				]
+			},
+			children: this.children
+		} );
+
+		const label = new View( locale );
+
+		label.setTemplate( {
+			tag: 'span',
+			attributes: {
+				class: [
+					'ck',
+					'ck-form__header__label'
+				]
+			},
+			children: [
+				{ text: bind.to( 'label' ) }
+			]
+		} );
+
+		this.children.add( label );
+	}
+}

+ 25 - 25
packages/ckeditor5-ui/src/labeledview/labeledview.js → packages/ckeditor5-ui/src/labeledfield/labeledfieldview.js

@@ -4,16 +4,16 @@
  */
 
 /**
- * @module ui/labeledview/labeledview
+ * @module ui/labeledfield/labeledfieldview
  */
 
 import View from '../view';
 import uid from '@ckeditor/ckeditor5-utils/src/uid';
 import LabelView from '../label/labelview';
-import '../../theme/components/labeledview/labeledview.css';
+import '../../theme/components/labeledfield/labeledfieldview.css';
 
 /**
- * The labeled view class. It can be used to enhance any view with the following features:
+ * The labeled field view class. It can be used to enhance any view with the following features:
  *
  * * a label,
  * * (optional) an error message,
@@ -25,16 +25,16 @@ import '../../theme/components/labeledview/labeledview.css';
  * The constructor of this class requires a callback that returns a view to be labeled. The callback
  * is called with unique ids that allow binding of DOM properties:
  *
- *		const labeledInputView = new LabeledView( locale, ( labeledView, viewUid, statusUid ) => {
- *			const inputView = new InputTextView( labeledView.locale );
+ *		const labeledInputView = new LabeledFieldView( locale, ( labeledFieldView, viewUid, statusUid ) => {
+ *			const inputView = new InputTextView( labeledFieldView.locale );
  *
  *			inputView.set( {
  *				id: viewUid,
  *				ariaDescribedById: statusUid
  *			} );
  *
- *			inputView.bind( 'isReadOnly' ).to( labeledView, 'isEnabled', value => !value );
- *			inputView.bind( 'hasError' ).to( labeledView, 'errorText', value => !!value );
+ *			inputView.bind( 'isReadOnly' ).to( labeledFieldView, 'isEnabled', value => !value );
+ *			inputView.bind( 'hasError' ).to( labeledFieldView, 'errorText', value => !!value );
  *
  *			return inputView;
  *		} );
@@ -45,23 +45,23 @@ import '../../theme/components/labeledview/labeledview.css';
  *
  *		document.body.append( labeledInputView.element );
  *
- * See {@link module:ui/labeledview/utils} to discover ready–to–use labeled input helpers for common
+ * See {@link module:ui/labeledfield/utils} to discover ready–to–use labeled input helpers for common
  * UI components.
  *
  * @extends module:ui/view~View
  */
-export default class LabeledView extends View {
+export default class LabeledFieldView extends View {
 	/**
-	 * Creates an instance of the labeled view class using a provided creator function
+	 * Creates an instance of the labeled field view class using a provided creator function
 	 * that provides the view to be labeled.
 	 *
 	 * @param {module:utils/locale~Locale} locale The locale instance.
 	 * @param {Function} viewCreator A function that returns a {@link module:ui/view~View}
 	 * that will be labeled. The following arguments are passed to the creator function:
 	 *
-	 * * an instance of the `LabeledView` to allow binding observable properties,
-	 * * an UID string that connects the {@link #labelView label} and the labeled view in DOM,
-	 * * an UID string that connects the {@link #statusView status} and the labeled view in DOM.
+	 * * an instance of the `LabeledFieldView` to allow binding observable properties,
+	 * * an UID string that connects the {@link #labelView label} and the labeled field view in DOM,
+	 * * an UID string that connects the {@link #statusView status} and the labeled field view in DOM.
 	 */
 	constructor( locale, viewCreator ) {
 		super( locale );
@@ -70,11 +70,11 @@ export default class LabeledView extends View {
 		const statusUid = `ck-labeled-view-status-${ uid() }`;
 
 		/**
-		 * The view that gets labeled.
+		 * The field view that gets labeled.
 		 *
-		 * @member {module:ui/view~View} #view
+		 * @member {module:ui/view~View} #fieldView
 		 */
-		this.view = viewCreator( this, viewUid, statusUid );
+		this.fieldView = viewCreator( this, viewUid, statusUid );
 
 		/**
 		 * The text of the label.
@@ -94,11 +94,11 @@ export default class LabeledView extends View {
 
 		/**
 		 * The validation error text. When set, it will be displayed
-		 * next to the {@link #view} as a typical validation error message.
+		 * next to the {@link #fieldView} as a typical validation error message.
 		 * Set it to `null` to hide the message.
 		 *
 		 * **Note:** Setting this property to anything but `null` will automatically
-		 * make the `hasError` of the {@link #view} `true`.
+		 * make the `hasError` of the {@link #fieldView} `true`.
 		 *
 		 * @observable
 		 * @member {String|null} #errorText
@@ -106,7 +106,7 @@ export default class LabeledView extends View {
 		this.set( 'errorText', null );
 
 		/**
-		 * The additional information text displayed next to the {@link #view} which can
+		 * The additional information text displayed next to the {@link #fieldView} which can
 		 * be used to inform the user about its purpose, provide help or hints.
 		 *
 		 * Set it to `null` to hide the message.
@@ -136,7 +136,7 @@ export default class LabeledView extends View {
 		this.labelView = this._createLabelView( viewUid );
 
 		/**
-		 * The status view for the {@link #view}. It displays {@link #errorText} and
+		 * The status view for the {@link #fieldView}. It displays {@link #errorText} and
 		 * {@link #infoText}.
 		 *
 		 * @member {module:ui/view~View} #statusView
@@ -175,7 +175,7 @@ export default class LabeledView extends View {
 			},
 			children: [
 				this.labelView,
-				this.view,
+				this.fieldView,
 				this.statusView
 			]
 		} );
@@ -199,10 +199,10 @@ export default class LabeledView extends View {
 
 	/**
 	 * Creates the status view instance. It displays {@link #errorText} and {@link #infoText}
-	 * next to the {@link #view}. See {@link #_statusText}.
+	 * next to the {@link #fieldView}. See {@link #_statusText}.
 	 *
 	 * @private
-	 * @param {String} statusUid Unique id of the status, shared with the {@link #view view's}
+	 * @param {String} statusUid Unique id of the status, shared with the {@link #fieldView view's}
 	 * `aria-describedby` attribute.
 	 * @returns {module:ui/view~View}
 	 */
@@ -233,9 +233,9 @@ export default class LabeledView extends View {
 	}
 
 	/**
-	 * Focuses the {@link #view}.
+	 * Focuses the {@link #fieldView}.
 	 */
 	focus() {
-		this.view.focus();
+		this.fieldView.focus();
 	}
 }

+ 19 - 19
packages/ckeditor5-ui/src/labeledview/utils.js → packages/ckeditor5-ui/src/labeledfield/utils.js

@@ -4,7 +4,7 @@
  */
 
 /**
- * @module ui/labeledview/utils
+ * @module ui/labeledfield/utils
  */
 
 import InputTextView from '../inputtext/inputtextview';
@@ -14,7 +14,7 @@ import { createDropdown } from '../dropdown/utils';
  * A helper for creating labeled inputs.
  *
  * It creates an instance of a {@link module:ui/inputtext/inputtextview~InputTextView input text} that is
- * logically related to a {@link module:ui/labeledview/labeledview~LabeledView labeled view} in DOM.
+ * logically related to a {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView labeled view} in DOM.
  *
  * The helper does the following:
  *
@@ -25,31 +25,31 @@ import { createDropdown } from '../dropdown/utils';
  *
  * Usage:
  *
- *		const labeledInputView = new LabeledView( locale, createLabeledDropdown );
+ *		const labeledInputView = new LabeledFieldView( locale, createLabeledDropdown );
  *		console.log( labeledInputView.view ); // An input instance.
  *
- * @param {module:ui/labeledview/labeledview~LabeledView} labeledView The instance of the labeled view.
+ * @param {module:ui/labeledfield/labeledfieldview~LabeledFieldView} labeledFieldView The instance of the labeled field view.
  * @param {String} viewUid An UID string that allows DOM logical connection between the
- * {@link module:ui/labeledview/labeledview~LabeledView#labelView labeled view's label} and the input.
+ * {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#labelView labeled view's label} and the input.
  * @param {String} statusUid An UID string that allows DOM logical connection between the
- * {@link module:ui/labeledview/labeledview~LabeledView#statusView labeled view's status} and the input.
+ * {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view's status} and the input.
  * @returns {module:ui/inputtext/inputtextview~InputTextView} The input text view instance.
  */
-export function createLabeledInputText( labeledView, viewUid, statusUid ) {
-	const inputView = new InputTextView( labeledView.locale );
+export function createLabeledInputText( labeledFieldView, viewUid, statusUid ) {
+	const inputView = new InputTextView( labeledFieldView.locale );
 
 	inputView.set( {
 		id: viewUid,
 		ariaDescribedById: statusUid
 	} );
 
-	inputView.bind( 'isReadOnly' ).to( labeledView, 'isEnabled', value => !value );
-	inputView.bind( 'hasError' ).to( labeledView, 'errorText', value => !!value );
+	inputView.bind( 'isReadOnly' ).to( labeledFieldView, 'isEnabled', value => !value );
+	inputView.bind( 'hasError' ).to( labeledFieldView, 'errorText', value => !!value );
 
 	inputView.on( 'input', () => {
 		// UX: Make the error text disappear and disable the error indicator as the user
 		// starts fixing the errors.
-		labeledView.errorText = null;
+		labeledFieldView.errorText = null;
 	} );
 
 	return inputView;
@@ -59,7 +59,7 @@ export function createLabeledInputText( labeledView, viewUid, statusUid ) {
  * A helper for creating labeled dropdowns.
  *
  * It creates an instance of a {@link module:ui/dropdown/dropdownview~DropdownView dropdown} that is
- * logically related to a {@link module:ui/labeledview/labeledview~LabeledView labeled view}.
+ * logically related to a {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView labeled field view}.
  *
  * The helper does the following:
  *
@@ -68,25 +68,25 @@ export function createLabeledInputText( labeledView, viewUid, statusUid ) {
  *
  * Usage:
  *
- *		const labeledInputView = new LabeledView( locale, createLabeledDropdown );
+ *		const labeledInputView = new LabeledFieldView( locale, createLabeledDropdown );
  *		console.log( labeledInputView.view ); // A dropdown instance.
  *
- * @param {module:ui/labeledview/labeledview~LabeledView} labeledView The instance of the labeled view.
+ * @param {module:ui/labeledfield/labeledfieldview~LabeledFieldView} labeledFieldView The instance of the labeled field view.
  * @param {String} viewUid An UID string that allows DOM logical connection between the
- * {@link module:ui/labeledview/labeledview~LabeledView#labelView labeled view label} and the dropdown.
+ * {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#labelView labeled view label} and the dropdown.
  * @param {String} statusUid An UID string that allows DOM logical connection between the
- * {@link module:ui/labeledview/labeledview~LabeledView#statusView labeled view status} and the dropdown.
+ * {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view status} and the dropdown.
  * @returns {module:ui/dropdown/dropdownview~DropdownView} The dropdown view instance.
  */
-export function createLabeledDropdown( labeledView, viewUid, statusUid ) {
-	const dropdownView = createDropdown( labeledView.locale );
+export function createLabeledDropdown( labeledFieldView, viewUid, statusUid ) {
+	const dropdownView = createDropdown( labeledFieldView.locale );
 
 	dropdownView.set( {
 		id: viewUid,
 		ariaDescribedById: statusUid
 	} );
 
-	dropdownView.bind( 'isEnabled' ).to( labeledView );
+	dropdownView.bind( 'isEnabled' ).to( labeledFieldView );
 
 	return dropdownView;
 }

+ 98 - 0
packages/ckeditor5-ui/tests/formheader/formheaderview.js

@@ -0,0 +1,98 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import View from '../../src/view';
+import ViewCollection from '../../src/viewcollection';
+import FormHeaderView from '../../src/formheader/formheaderview';
+
+describe( 'FormHeaderView', () => {
+	let view, locale;
+
+	beforeEach( () => {
+		locale = { t: val => val };
+		view = new FormHeaderView( locale );
+		view.render();
+	} );
+
+	afterEach( () => {
+		view.element.remove();
+	} );
+
+	describe( 'constructor()', () => {
+		it( 'should set view#locale', () => {
+			expect( view.locale ).to.equal( locale );
+		} );
+
+		it( 'should create view#children collection', () => {
+			expect( view.children ).to.be.instanceOf( ViewCollection );
+			expect( view.children ).to.have.length( 1 );
+		} );
+
+		it( 'should set view#label', () => {
+			expect( view.label ).to.equal( '' );
+		} );
+
+		it( 'should set view#class', () => {
+			expect( view.class ).to.be.null;
+		} );
+
+		it( 'should set the template', () => {
+			expect( view.element.classList.contains( 'ck' ) ).to.be.true;
+			expect( view.element.classList.contains( 'ck-form__header' ) ).to.be.true;
+		} );
+
+		describe( 'options', () => {
+			it( 'should set view#class when class was passed', () => {
+				const view = new FormHeaderView( locale, {
+					class: 'foo'
+				} );
+
+				expect( view.class ).to.equal( 'foo' );
+
+				view.destroy();
+			} );
+
+			it( 'should use a label text when passed', () => {
+				const view = new FormHeaderView( locale, {
+					label: 'foo'
+				} );
+
+				view.render();
+
+				expect( view.element.firstChild.classList.contains( 'ck' ) ).to.be.true;
+				expect( view.element.firstChild.classList.contains( 'ck-form__header__label' ) ).to.be.true;
+				expect( view.element.firstChild.textContent ).to.equal( 'foo' );
+
+				view.destroy();
+			} );
+		} );
+
+		describe( 'template bindings', () => {
+			it( 'should bind #class to the template', () => {
+				view.class = 'foo';
+				expect( view.element.classList.contains( 'foo' ) ).to.be.true;
+			} );
+
+			it( 'should bind #label to the template', () => {
+				view.label = 'bar';
+				expect( view.element.firstChild.textContent ).to.equal( 'bar' );
+
+				view.label = 'baz';
+				expect( view.element.firstChild.textContent ).to.equal( 'baz' );
+			} );
+
+			it( 'should bind #children to the template', () => {
+				const child = new View();
+				child.setTemplate( { tag: 'div' } );
+
+				view.children.add( child );
+
+				expect( view.element.childNodes[ 1 ] ).to.equal( child.element );
+
+				view.destroy();
+			} );
+		} );
+	} );
+} );

+ 166 - 0
packages/ckeditor5-ui/tests/labeledfield/labeledfieldview.js

@@ -0,0 +1,166 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import View from '../../src/view';
+import LabeledFieldView from '../../src/labeledfield/labeledfieldview';
+import LabelView from '../../src/label/labelview';
+
+describe( 'LabeledFieldView', () => {
+	const locale = {};
+
+	let labeledInput, view;
+
+	beforeEach( () => {
+		labeledInput = new LabeledFieldView( locale, ( labeledInput, viewUid, statusUid ) => {
+			view = new View( locale );
+			view.setTemplate( { tag: 'div' } );
+			view.focus = () => {};
+			view.viewUid = viewUid;
+			view.statusUid = statusUid;
+
+			return view;
+		} );
+
+		labeledInput.render();
+	} );
+
+	afterEach( () => {
+		labeledInput.destroy();
+	} );
+
+	describe( 'constructor()', () => {
+		it( 'should set labeledInput#locale', () => {
+			expect( labeledInput.locale ).to.deep.equal( locale );
+		} );
+
+		it( 'should set labeledInput#fieldView', () => {
+			expect( labeledInput.fieldView ).to.equal( view );
+		} );
+
+		it( 'should set labeledInput#label', () => {
+			expect( labeledInput.label ).to.be.undefined;
+		} );
+
+		it( 'should set labeledInput#isEnabled', () => {
+			expect( labeledInput.isEnabled ).to.be.true;
+		} );
+
+		it( 'should set labeledInput#errorText', () => {
+			expect( labeledInput.errorText ).to.be.null;
+		} );
+
+		it( 'should set labeledInput#infoText', () => {
+			expect( labeledInput.infoText ).to.be.null;
+		} );
+
+		it( 'should set labeledInput#class', () => {
+			expect( labeledInput.class ).to.be.undefined;
+		} );
+
+		it( 'should create labeledInput#labelView', () => {
+			expect( labeledInput.labelView ).to.instanceOf( LabelView );
+		} );
+
+		it( 'should create labeledInput#statusView', () => {
+			expect( labeledInput.statusView ).to.instanceOf( View );
+
+			expect( labeledInput.statusView.element.tagName ).to.equal( 'DIV' );
+			expect( labeledInput.statusView.element.classList.contains( 'ck' ) ).to.be.true;
+			expect( labeledInput.statusView.element.classList.contains( 'ck-labeled-view__status' ) ).to.be.true;
+		} );
+
+		it( 'should allow pairing #view and #labelView by unique id', () => {
+			expect( labeledInput.labelView.for ).to.equal( view.viewUid );
+		} );
+
+		it( 'should allow pairing #view and #statusView by unique id', () => {
+			expect( view.statusUid ).to.equal( labeledInput.statusView.element.id );
+		} );
+	} );
+
+	describe( 'template', () => {
+		it( 'should have the CSS class', () => {
+			expect( labeledInput.element.classList.contains( 'ck' ) ).to.be.true;
+			expect( labeledInput.element.classList.contains( 'ck-labeled-view' ) ).to.be.true;
+		} );
+
+		it( 'should have #labeledInput', () => {
+			expect( labeledInput.template.children[ 0 ] ).to.equal( labeledInput.labelView );
+		} );
+
+		it( 'should have #view', () => {
+			expect( labeledInput.template.children[ 1 ] ).to.equal( view );
+		} );
+
+		it( 'should have the #statusView container', () => {
+			expect( labeledInput.template.children[ 2 ] ).to.equal( labeledInput.statusView );
+		} );
+
+		describe( 'DOM bindings', () => {
+			describe( 'class', () => {
+				it( 'should react on labeledInput#class', () => {
+					labeledInput.class = 'foo';
+					expect( labeledInput.element.classList.contains( 'foo' ) ).to.be.true;
+
+					labeledInput.class = 'bar';
+					expect( labeledInput.element.classList.contains( 'foo' ) ).to.be.false;
+					expect( labeledInput.element.classList.contains( 'bar' ) ).to.be.true;
+				} );
+			} );
+
+			describe( 'status container', () => {
+				it( 'should react on labeledInput#errorText', () => {
+					const statusElement = labeledInput.statusView.element;
+
+					labeledInput.errorText = '';
+					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.true;
+					expect( statusElement.classList.contains( 'ck-labeled-view__status_error' ) ).to.be.false;
+					expect( statusElement.hasAttribute( 'role' ) ).to.be.false;
+					expect( statusElement.innerHTML ).to.equal( '' );
+
+					labeledInput.errorText = 'foo';
+					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.false;
+					expect( statusElement.classList.contains( 'ck-labeled-view__status_error' ) ).to.be.true;
+					expect( statusElement.getAttribute( 'role' ) ).to.equal( 'alert' );
+					expect( statusElement.innerHTML ).to.equal( 'foo' );
+				} );
+
+				it( 'should react on labeledInput#infoText', () => {
+					const statusElement = labeledInput.statusView.element;
+
+					labeledInput.infoText = '';
+					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.true;
+					expect( statusElement.classList.contains( 'ck-labeled-view__status_error' ) ).to.be.false;
+					expect( statusElement.hasAttribute( 'role' ) ).to.be.false;
+					expect( statusElement.innerHTML ).to.equal( '' );
+
+					labeledInput.infoText = 'foo';
+					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.false;
+					expect( statusElement.classList.contains( 'ck-labeled-view__status_error' ) ).to.be.false;
+					expect( statusElement.hasAttribute( 'role' ) ).to.be.false;
+					expect( statusElement.innerHTML ).to.equal( 'foo' );
+				} );
+			} );
+		} );
+	} );
+
+	describe( 'binding', () => {
+		it( 'should bind labeledInput#label to labeledInput.labelView#label', () => {
+			labeledInput.label = 'Foo bar';
+
+			expect( labeledInput.labelView.text ).to.equal( 'Foo bar' );
+		} );
+	} );
+
+	describe( 'focus()', () => {
+		it( 'should focus the #view in DOM', () => {
+			const spy = sinon.spy( view, 'focus' );
+
+			labeledInput.focus();
+
+			sinon.assert.calledOnce( spy );
+		} );
+	} );
+} );

+ 108 - 0
packages/ckeditor5-ui/tests/labeledfield/utils.js

@@ -0,0 +1,108 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+import {
+	createLabeledInputText,
+	createLabeledDropdown
+} from '../../src/labeledfield/utils';
+
+import LabeledFieldView from '../../src/labeledfield/labeledfieldview';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import InputTextView from '../../src/inputtext/inputtextview';
+import DropdownView from '../../src/dropdown/dropdownview';
+
+describe( 'LabeledFieldView utils', () => {
+	let locale;
+
+	testUtils.createSinonSandbox();
+
+	beforeEach( () => {
+		locale = { t: val => val };
+	} );
+
+	describe( 'createLabeledInputText()', () => {
+		let labeledInput;
+
+		beforeEach( () => {
+			labeledInput = new LabeledFieldView( locale, createLabeledInputText );
+		} );
+
+		afterEach( () => {
+			labeledInput.destroy();
+		} );
+
+		it( 'should create an InputTextView instance', () => {
+			expect( labeledInput.fieldView ).to.be.instanceOf( InputTextView );
+		} );
+
+		it( 'should pass the Locale to the input', () => {
+			expect( labeledInput.fieldView.locale ).to.equal( locale );
+		} );
+
+		it( 'should set input #id and #ariaDescribedById', () => {
+			labeledInput.render();
+
+			expect( labeledInput.fieldView.id ).to.equal( labeledInput.labelView.for );
+			expect( labeledInput.fieldView.ariaDescribedById ).to.equal( labeledInput.statusView.element.id );
+		} );
+
+		it( 'should bind input\'s #isReadOnly to labeledInput#isEnabled', () => {
+			labeledInput.isEnabled = true;
+			expect( labeledInput.fieldView.isReadOnly ).to.be.false;
+
+			labeledInput.isEnabled = false;
+			expect( labeledInput.fieldView.isReadOnly ).to.be.true;
+		} );
+
+		it( 'should bind input\'s #hasError to labeledInput#errorText', () => {
+			labeledInput.errorText = 'some error';
+			expect( labeledInput.fieldView.hasError ).to.be.true;
+
+			labeledInput.errorText = null;
+			expect( labeledInput.fieldView.hasError ).to.be.false;
+		} );
+
+		it( 'should clean labeledInput#errorText upon input\'s DOM "update" event', () => {
+			labeledInput.errorText = 'some error';
+			labeledInput.fieldView.fire( 'input' );
+			expect( labeledInput.errorText ).to.be.null;
+		} );
+	} );
+
+	describe( 'createLabeledDropdown', () => {
+		let labeledDropdown;
+
+		beforeEach( () => {
+			labeledDropdown = new LabeledFieldView( locale, createLabeledDropdown );
+		} );
+
+		afterEach( () => {
+			labeledDropdown.destroy();
+		} );
+
+		it( 'should create a DropdownView', () => {
+			expect( labeledDropdown.fieldView ).to.be.instanceOf( DropdownView );
+		} );
+
+		it( 'should pass the Locale to the dropdown', () => {
+			expect( labeledDropdown.fieldView.locale ).to.equal( locale );
+		} );
+
+		it( 'should set dropdown\'s #id and #ariaDescribedById', () => {
+			labeledDropdown.render();
+
+			expect( labeledDropdown.fieldView.id ).to.equal( labeledDropdown.labelView.for );
+			expect( labeledDropdown.fieldView.ariaDescribedById ).to.equal( labeledDropdown.statusView.element.id );
+		} );
+
+		it( 'should bind dropdown\'s #isEnabled to the labeled view', () => {
+			labeledDropdown.isEnabled = true;
+			expect( labeledDropdown.fieldView.isEnabled ).to.be.true;
+
+			labeledDropdown.isEnabled = false;
+			expect( labeledDropdown.fieldView.isEnabled ).to.be.false;
+		} );
+	} );
+} );

+ 0 - 166
packages/ckeditor5-ui/tests/labeledview/labeledview.js

@@ -1,166 +0,0 @@
-/**
- * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
- */
-
-import View from '../../src/view';
-import LabeledView from '../../src/labeledview/labeledview';
-import LabelView from '../../src/label/labelview';
-
-describe( 'LabeledView', () => {
-	const locale = {};
-
-	let labeledView, view;
-
-	beforeEach( () => {
-		labeledView = new LabeledView( locale, ( labeledView, viewUid, statusUid ) => {
-			view = new View( locale );
-			view.setTemplate( { tag: 'div' } );
-			view.focus = () => {};
-			view.viewUid = viewUid;
-			view.statusUid = statusUid;
-
-			return view;
-		} );
-
-		labeledView.render();
-	} );
-
-	afterEach( () => {
-		labeledView.destroy();
-	} );
-
-	describe( 'constructor()', () => {
-		it( 'should set labeledView#locale', () => {
-			expect( labeledView.locale ).to.deep.equal( locale );
-		} );
-
-		it( 'should set labeledView#view', () => {
-			expect( labeledView.view ).to.equal( view );
-		} );
-
-		it( 'should set labeledView#label', () => {
-			expect( labeledView.label ).to.be.undefined;
-		} );
-
-		it( 'should set labeledView#isEnabled', () => {
-			expect( labeledView.isEnabled ).to.be.true;
-		} );
-
-		it( 'should set labeledView#errorText', () => {
-			expect( labeledView.errorText ).to.be.null;
-		} );
-
-		it( 'should set labeledView#infoText', () => {
-			expect( labeledView.infoText ).to.be.null;
-		} );
-
-		it( 'should set labeledView#class', () => {
-			expect( labeledView.class ).to.be.undefined;
-		} );
-
-		it( 'should create labeledView#labelView', () => {
-			expect( labeledView.labelView ).to.instanceOf( LabelView );
-		} );
-
-		it( 'should create labeledView#statusView', () => {
-			expect( labeledView.statusView ).to.instanceOf( View );
-
-			expect( labeledView.statusView.element.tagName ).to.equal( 'DIV' );
-			expect( labeledView.statusView.element.classList.contains( 'ck' ) ).to.be.true;
-			expect( labeledView.statusView.element.classList.contains( 'ck-labeled-view__status' ) ).to.be.true;
-		} );
-
-		it( 'should allow pairing #view and #labelView by unique id', () => {
-			expect( labeledView.labelView.for ).to.equal( view.viewUid );
-		} );
-
-		it( 'should allow pairing #view and #statusView by unique id', () => {
-			expect( view.statusUid ).to.equal( labeledView.statusView.element.id );
-		} );
-	} );
-
-	describe( 'template', () => {
-		it( 'should have the CSS class', () => {
-			expect( labeledView.element.classList.contains( 'ck' ) ).to.be.true;
-			expect( labeledView.element.classList.contains( 'ck-labeled-view' ) ).to.be.true;
-		} );
-
-		it( 'should have #labeledView', () => {
-			expect( labeledView.template.children[ 0 ] ).to.equal( labeledView.labelView );
-		} );
-
-		it( 'should have #view', () => {
-			expect( labeledView.template.children[ 1 ] ).to.equal( view );
-		} );
-
-		it( 'should have the #statusView container', () => {
-			expect( labeledView.template.children[ 2 ] ).to.equal( labeledView.statusView );
-		} );
-
-		describe( 'DOM bindings', () => {
-			describe( 'class', () => {
-				it( 'should react on labeledView#class', () => {
-					labeledView.class = 'foo';
-					expect( labeledView.element.classList.contains( 'foo' ) ).to.be.true;
-
-					labeledView.class = 'bar';
-					expect( labeledView.element.classList.contains( 'foo' ) ).to.be.false;
-					expect( labeledView.element.classList.contains( 'bar' ) ).to.be.true;
-				} );
-			} );
-
-			describe( 'status container', () => {
-				it( 'should react on labeledView#errorText', () => {
-					const statusElement = labeledView.statusView.element;
-
-					labeledView.errorText = '';
-					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.true;
-					expect( statusElement.classList.contains( 'ck-labeled-view__status_error' ) ).to.be.false;
-					expect( statusElement.hasAttribute( 'role' ) ).to.be.false;
-					expect( statusElement.innerHTML ).to.equal( '' );
-
-					labeledView.errorText = 'foo';
-					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.false;
-					expect( statusElement.classList.contains( 'ck-labeled-view__status_error' ) ).to.be.true;
-					expect( statusElement.getAttribute( 'role' ) ).to.equal( 'alert' );
-					expect( statusElement.innerHTML ).to.equal( 'foo' );
-				} );
-
-				it( 'should react on labeledView#infoText', () => {
-					const statusElement = labeledView.statusView.element;
-
-					labeledView.infoText = '';
-					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.true;
-					expect( statusElement.classList.contains( 'ck-labeled-view__status_error' ) ).to.be.false;
-					expect( statusElement.hasAttribute( 'role' ) ).to.be.false;
-					expect( statusElement.innerHTML ).to.equal( '' );
-
-					labeledView.infoText = 'foo';
-					expect( statusElement.classList.contains( 'ck-hidden' ) ).to.be.false;
-					expect( statusElement.classList.contains( 'ck-labeled-view__status_error' ) ).to.be.false;
-					expect( statusElement.hasAttribute( 'role' ) ).to.be.false;
-					expect( statusElement.innerHTML ).to.equal( 'foo' );
-				} );
-			} );
-		} );
-	} );
-
-	describe( 'binding', () => {
-		it( 'should bind labeledView#label to labeledView.labelView#label', () => {
-			labeledView.label = 'Foo bar';
-
-			expect( labeledView.labelView.text ).to.equal( 'Foo bar' );
-		} );
-	} );
-
-	describe( 'focus()', () => {
-		it( 'should focus the #view in DOM', () => {
-			const spy = sinon.spy( view, 'focus' );
-
-			labeledView.focus();
-
-			sinon.assert.calledOnce( spy );
-		} );
-	} );
-} );

+ 0 - 108
packages/ckeditor5-ui/tests/labeledview/utils.js

@@ -1,108 +0,0 @@
-/**
- * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
- */
-
-import {
-	createLabeledInputText,
-	createLabeledDropdown
-} from '../../src/labeledview/utils';
-
-import LabeledView from '../../src/labeledview/labeledview';
-import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
-import InputTextView from '../../src/inputtext/inputtextview';
-import DropdownView from '../../src/dropdown/dropdownview';
-
-describe( 'LabeledView utils', () => {
-	let locale;
-
-	testUtils.createSinonSandbox();
-
-	beforeEach( () => {
-		locale = { t: val => val };
-	} );
-
-	describe( 'createLabeledInputText()', () => {
-		let labeledView;
-
-		beforeEach( () => {
-			labeledView = new LabeledView( locale, createLabeledInputText );
-		} );
-
-		afterEach( () => {
-			labeledView.destroy();
-		} );
-
-		it( 'should create an InputTextView instance', () => {
-			expect( labeledView.view ).to.be.instanceOf( InputTextView );
-		} );
-
-		it( 'should pass the Locale to the input', () => {
-			expect( labeledView.view.locale ).to.equal( locale );
-		} );
-
-		it( 'should set input #id and #ariaDescribedById', () => {
-			labeledView.render();
-
-			expect( labeledView.view.id ).to.equal( labeledView.labelView.for );
-			expect( labeledView.view.ariaDescribedById ).to.equal( labeledView.statusView.element.id );
-		} );
-
-		it( 'should bind input\'s #isReadOnly to LabeledView#isEnabled', () => {
-			labeledView.isEnabled = true;
-			expect( labeledView.view.isReadOnly ).to.be.false;
-
-			labeledView.isEnabled = false;
-			expect( labeledView.view.isReadOnly ).to.be.true;
-		} );
-
-		it( 'should bind input\'s #hasError to LabeledView#errorText', () => {
-			labeledView.errorText = 'some error';
-			expect( labeledView.view.hasError ).to.be.true;
-
-			labeledView.errorText = null;
-			expect( labeledView.view.hasError ).to.be.false;
-		} );
-
-		it( 'should clean LabeledView#errorText upon input\'s DOM "update" event', () => {
-			labeledView.errorText = 'some error';
-			labeledView.view.fire( 'input' );
-			expect( labeledView.errorText ).to.be.null;
-		} );
-	} );
-
-	describe( 'createLabeledDropdown', () => {
-		let labeledView;
-
-		beforeEach( () => {
-			labeledView = new LabeledView( locale, createLabeledDropdown );
-		} );
-
-		afterEach( () => {
-			labeledView.destroy();
-		} );
-
-		it( 'should create a DropdownView', () => {
-			expect( labeledView.view ).to.be.instanceOf( DropdownView );
-		} );
-
-		it( 'should pass the Locale to the dropdown', () => {
-			expect( labeledView.view.locale ).to.equal( locale );
-		} );
-
-		it( 'should set dropdown\'s #id and #ariaDescribedById', () => {
-			labeledView.render();
-
-			expect( labeledView.view.id ).to.equal( labeledView.labelView.for );
-			expect( labeledView.view.ariaDescribedById ).to.equal( labeledView.statusView.element.id );
-		} );
-
-		it( 'should bind dropdown\'s #isEnabled to the labeled view', () => {
-			labeledView.isEnabled = true;
-			expect( labeledView.view.isEnabled ).to.be.true;
-
-			labeledView.isEnabled = false;
-			expect( labeledView.view.isEnabled ).to.be.false;
-		} );
-	} );
-} );

+ 12 - 0
packages/ckeditor5-ui/theme/components/formheader/formheader.css

@@ -0,0 +1,12 @@
+/*
+ * Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+.ck.ck-form__header {
+	display: flex;
+	flex-direction: row;
+	flex-wrap: nowrap;
+	align-items: center;
+	justify-content: space-between;
+}

+ 0 - 0
packages/ckeditor5-ui/theme/components/labeledview/labeledview.css → packages/ckeditor5-ui/theme/components/labeledfield/labeledfieldview.css