Kaynağa Gözat

Added tests to views placeholder utility.

Szymon Kupś 8 yıl önce
ebeveyn
işleme
4cd811cd8c

+ 3 - 2
packages/ckeditor5-engine/src/view/placeholder.js

@@ -21,7 +21,7 @@ extend( listener, EmitterMixin );
 const documentPlaceholders = new Map();
 
 /**
- * Attaches placeholder to provided element and updates it's visibility. To change placeholder simply call method
+ * Attaches placeholder to provided element and updates it's visibility. To change placeholder simply call this method
  * once again with new parameters.
  *
  * @param {module:engine/view/element~Element} element Element to attach placeholder to.
@@ -29,7 +29,7 @@ const documentPlaceholders = new Map();
  * @param {Function} [checkFunction] If provided it will be called before checking if placeholder should be displayed.
  * If function returns `false` placeholder will not be showed.
  */
-export default function attachPlaceholder( element, placeholderText, checkFunction ) {
+export function attachPlaceholder( element, placeholderText, checkFunction ) {
 	// Detach placeholder if was used before.
 	detachPlaceholder( element );
 
@@ -118,6 +118,7 @@ function updateSinglePlaceholder( element, checkFunction ) {
 	}
 
 	// It there are no child elements and selection is not placed inside element.
+	// TODO: check if selection is not deeper inside.
 	if ( !element.childCount && anchor && anchor.parent !== element ) {
 		element.addClass( CSS_CLASS );
 	} else {

+ 1 - 1
packages/ckeditor5-engine/tests/manual/placeholder.js

@@ -12,7 +12,7 @@ import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import Undo from '@ckeditor/ckeditor5-undo/src/undo';
 import Heading from '@ckeditor/ckeditor5-heading/src/heading';
 import global from '@ckeditor/ckeditor5-utils/src/dom/global';
-import attachPlaceholder from '../../src/view/placeholder';
+import { attachPlaceholder } from '../../src/view/placeholder';
 
 ClassicEditor.create( global.document.querySelector( '#editor' ), {
 	plugins: [ Enter, Typing, Paragraph, Undo, Heading ],

+ 8 - 0
packages/ckeditor5-engine/tests/manual/placeholder.md

@@ -1 +1,9 @@
 ### Placeholder creation
+
+* You should see two placeholders:
+  * for heading: `Type some header text...`,
+  * and for paragraph: `Type some paragraph text...`.
+* Clicking on header and paragraph should remove placeholder.
+* Clicking outside the editor should show both placeholders.
+* Type some text into paragraph, and click outside. Paragraph placeholder should be hidden.
+* Remove added text and click outside - paragraph placeholder should now be visible again.

+ 169 - 0
packages/ckeditor5-engine/tests/view/placeholder.js

@@ -0,0 +1,169 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import { attachPlaceholder, detachPlaceholder } from '../../src/view/placeholder';
+import ViewContainerElement from '../../src/view/containerelement';
+import ViewDocument from '../../src/view/document';
+import ViewRange from '../../src/view/range';
+import { setData } from '../../src/dev-utils/view';
+
+describe( 'placeholder', () => {
+	let viewDocument, viewRoot;
+
+	beforeEach( () => {
+		viewDocument = new ViewDocument();
+		viewRoot = viewDocument.createRoot( 'main' );
+		viewDocument.isFocused = true;
+	} );
+
+	describe( 'createPlaceholder', () => {
+		it( 'should throw if element is not inside document', () => {
+			const element = new ViewContainerElement( 'div' );
+
+			expect( () => {
+				attachPlaceholder( element, 'foo bar baz' );
+			} ).to.throw( 'view-placeholder-element-is-detached' );
+		} );
+
+		it( 'should attach proper CSS class and data attribute', () => {
+			setData( viewDocument, '<div></div><div>{another div}</div>' );
+			const element = viewRoot.getChild( 0 );
+
+			attachPlaceholder( element, 'foo bar baz' );
+
+			expect( element.getAttribute( 'data-placeholder' ) ).to.equal( 'foo bar baz' );
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.true;
+		} );
+
+		it( 'if element has children set only data attribute', () => {
+			setData( viewDocument, '<div>first div</div><div>{another div}</div>' );
+			const element = viewRoot.getChild( 0 );
+
+			attachPlaceholder( element, 'foo bar baz' );
+
+			expect( element.getAttribute( 'data-placeholder' ) ).to.equal( 'foo bar baz' );
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.false;
+		} );
+
+		it( 'if element has selection inside set only data attribute', () => {
+			setData( viewDocument, '<div>[]</div><div>another div</div>' );
+			const element = viewRoot.getChild( 0 );
+
+			attachPlaceholder( element, 'foo bar baz' );
+
+			expect( element.getAttribute( 'data-placeholder' ) ).to.equal( 'foo bar baz' );
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.false;
+		} );
+
+		it( 'if element has selection inside but document is blurred should contain placeholder CSS class', () => {
+			setData( viewDocument, '<div>[]</div><div>another div</div>' );
+			const element = viewRoot.getChild( 0 );
+			viewDocument.isFocused = false;
+
+			attachPlaceholder( element, 'foo bar baz' );
+
+			expect( element.getAttribute( 'data-placeholder' ) ).to.equal( 'foo bar baz' );
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.true;
+		} );
+
+		it( 'use check function if one is provided', () => {
+			setData( viewDocument, '<div></div><div>{another div}</div>' );
+			const element = viewRoot.getChild( 0 );
+			const spy = sinon.spy( () => false );
+
+			attachPlaceholder( element, 'foo bar baz', spy );
+
+			sinon.assert.calledOnce( spy );
+			expect( element.getAttribute( 'data-placeholder' ) ).to.equal( 'foo bar baz' );
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.false;
+		} );
+
+		it( 'should remove CSS class if selection is moved inside', () => {
+			setData( viewDocument, '<div></div><div>{another div}</div>' );
+			const element = viewRoot.getChild( 0 );
+
+			attachPlaceholder( element, 'foo bar baz' );
+
+			expect( element.getAttribute( 'data-placeholder' ) ).to.equal( 'foo bar baz' );
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.true;
+
+			viewDocument.selection.setRanges( [ ViewRange.createIn( element ) ] );
+			viewDocument.render();
+
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.false;
+		} );
+
+		it( 'should change placeholder settings when called twice', () => {
+			setData( viewDocument, '<div></div><div>{another div}</div>' );
+			const element = viewRoot.getChild( 0 );
+
+			attachPlaceholder( element, 'foo bar baz' );
+			attachPlaceholder( element, 'new text' );
+
+			expect( element.getAttribute( 'data-placeholder' ) ).to.equal( 'new text' );
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.true;
+		} );
+
+		it( 'should not throw when element is no longer in document', () => {
+			setData( viewDocument, '<div></div><div>{another div}</div>' );
+			const element = viewRoot.getChild( 0 );
+
+			attachPlaceholder( element, 'foo bar baz' );
+			setData( viewDocument, '<p>paragraph</p>' );
+
+			viewDocument.render();
+		} );
+
+		it( 'should allow to add placeholder to elements from different documents', () => {
+			setData( viewDocument, '<div></div><div>{another div}</div>' );
+			const element = viewRoot.getChild( 0 );
+			const secondDocument = new ViewDocument();
+			secondDocument.isFocused = true;
+			const secondRoot = secondDocument.createRoot( 'main' );
+			setData( secondDocument, '<div></div><div>{another div}</div>' );
+			const secondElement = secondRoot.getChild( 0 );
+
+			attachPlaceholder( element, 'first placeholder' );
+			attachPlaceholder( secondElement, 'second placeholder' );
+
+			expect( element.getAttribute( 'data-placeholder' ) ).to.equal( 'first placeholder' );
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.true;
+
+			expect( secondElement.getAttribute( 'data-placeholder' ) ).to.equal( 'second placeholder' );
+			expect( secondElement.hasClass( 'ck-placeholder' ) ).to.be.true;
+
+			// Move selection to the elements with placeholders.
+			viewDocument.selection.setRanges( [ ViewRange.createIn( element ) ] );
+			secondDocument.selection.setRanges( [ ViewRange.createIn( secondElement ) ] );
+
+			// Render changes.
+			viewDocument.render();
+			secondDocument.render();
+
+			expect( element.getAttribute( 'data-placeholder' ) ).to.equal( 'first placeholder' );
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.false;
+
+			expect( secondElement.getAttribute( 'data-placeholder' ) ).to.equal( 'second placeholder' );
+			expect( secondElement.hasClass( 'ck-placeholder' ) ).to.be.false;
+		} );
+	} );
+
+	describe( 'detachPlaceholder', () => {
+		it( 'should remove placeholder from element', () => {
+			setData( viewDocument, '<div></div><div>{another div}</div>' );
+			const element = viewRoot.getChild( 0 );
+
+			attachPlaceholder( element, 'foo bar baz' );
+
+			expect( element.getAttribute( 'data-placeholder' ) ).to.equal( 'foo bar baz' );
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.true;
+
+			detachPlaceholder( element );
+
+			expect( element.hasAttribute( 'data-placeholder' ) ).to.be.false;
+			expect( element.hasClass( 'ck-placeholder' ) ).to.be.false;
+		} );
+	} );
+} );

+ 3 - 1
packages/ckeditor5-engine/theme/theme.scss

@@ -1,8 +1,10 @@
 // Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
 // For licensing, see LICENSE.md or http://ckeditor.com/license
 
+@import '~@ckeditor/ckeditor5-theme-lark/theme/helpers/_colors.scss';
+
 .ck-placeholder::before {
 	content: attr( data-placeholder );
 	cursor: text;
-	color: #bababa;
+	color: ck-color( 'text', 70% );
 }