Selaa lähdekoodia

Added notification plugin.

Szymon Kupś 8 vuotta sitten
vanhempi
commit
548e2cbc0f

+ 195 - 0
packages/ckeditor5-ui/src/notification.js

@@ -0,0 +1,195 @@
+/**
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module ui/notification
+ */
+
+/* globals window */
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+
+/**
+ * The Notification plugin.
+ *
+ * This plugin sends few base types of notifications: `success`, `info` and `warning`. This notifications need to be
+ * handled and displayed by plugin responsible for showing UI of the notifications. Using this plugin for dispatching
+ * notifications makes possible to switch the notifications UI.
+ *
+ * Note that every unhandled and not stopped `warning` notification will be displayed as system alert.
+ * See {@link module:ui/notification~Notification#showWarning}.
+ *
+ * @extends module:core/plugin~Plugin
+ */
+export default class Notification extends Plugin {
+	/**
+	 * @inheritDoc
+	 */
+	static get pluginName() {
+		return 'notification';
+	}
+
+	/**
+	 * @inheritDoc
+	 */
+	init() {
+		// Each unhandled and not stopped `show:warning` event is displayed as system alert.
+		this.on( 'show:warning', ( evt, data ) => {
+			window.alert( data.message );
+		}, { priority: 'lowest' } );
+	}
+
+	/**
+	 * Shows success notification.
+	 *
+	 * At default it fires `show:success` event with given data but event namespace can be extended
+	 * by `data.namespace` option e.g.
+	 *
+	 * 		showSuccess( 'Image is uploaded.', {
+	 * 			namespace: 'upload:image'
+	 * 		} );
+	 *
+	 * will fire `show:success:upload:image` event.
+	 *
+	 * @param {String} message Content of the notification.
+	 * @param {Object} [data={}] Additional data.
+	 * @param {String} [data.namespace] Additional event namespace.
+	 */
+	showSuccess( message, data = {} ) {
+		this._showNotification( {
+			message: message,
+			type: 'success',
+			namespace: data.namespace
+		} );
+	}
+
+	/**
+	 * Shows info notification.
+	 *
+	 * At default it fires `show:info` event with given data but event namespace can be extended
+	 * by `data.namespace` option e.g.
+	 *
+	 * 		showInfo( 'Editor is offline.', {
+	 * 			namespace: 'editor:status'
+	 * 		} );
+	 *
+	 * will fire `show:info:editor:status` event.
+	 *
+	 * @param {String} message Content of the notification.
+	 * @param {Object} [data={}] Additional data.
+	 * @param {String} [data.namespace] Additional event namespace.
+	 */
+	showInfo( message, data = {} ) {
+		this._showNotification( {
+			message: message,
+			type: 'info',
+			namespace: data.namespace
+		} );
+	}
+
+	/**
+	 * Shows warning notification.
+	 *
+	 * At default it fires `show:warning` event with given data but event namespace can be extended
+	 * by `data.namespace` option e.g.
+	 *
+	 * 		showWarning( 'Image upload error.', {
+	 * 			namespace: 'upload:image'
+	 * 		} );
+	 *
+	 * will fire `show:warning:upload:image` event.
+	 *
+	 * Note that each unhandled and not stopped `warning` notification will be displayed as system alert.
+	 * Plugin responsible for displaying warnings should `stop()` the event to prevent of displaying it as alert:
+	 *
+	 * 		notifications.on( 'show:warning', ( evt, data ) => {
+	 * 			// Do something with data.
+	 *
+	 * 			// Stop this event to prevent of displaying as alert.
+	 * 			evt.stop();
+	 * 		} );
+	 *
+	 * You can attach many listeners to the same event and `stop()` this event in the listener with the low priority:
+	 *
+	 * 		notifications.on( 'show:warning', ( evt, data ) => {
+	 * 			// Show warning in the UI, but not stop it.
+	 * 		} );
+	 *
+	 * 		notifications.on( 'show:warning', ( evt, data ) => {
+	 * 			// Log warning to some error tracker.
+	 *
+	 * 			// Stop this event to prevent of displaying as alert.
+	 * 			evt.stop();
+	 * 		}, { priority: 'low' } );
+	 *
+	 * @param {String} message Content of the notification.
+	 * @param {Object} [data={}] Additional data.
+	 * @param {String} [data.namespace] Additional event namespace.
+	 */
+	showWarning( message, data = {} ) {
+		this._showNotification( {
+			message: message,
+			type: 'warning',
+			namespace: data.namespace
+		} );
+	}
+
+	/**
+	 * Fires `show` event with specified type, namespace and message.
+	 *
+	 * @private
+	 * @param {Object} data Message data.
+	 * @param {String} data.message Content of the notification.
+	 * @param {'success'|'info'|'warning'} data.type Type of message.
+	 * @param {String} [data.namespace] Additional event namespace.
+	 */
+	_showNotification( data ) {
+		const event = `show:${ data.type }` + ( data.namespace ? `:${ data.namespace }` : '' );
+
+		this.fire( event, {
+			message: data.message,
+			type: data.type
+		} );
+	}
+
+	/**
+	 * Fired when one of `showSuccess`, `showInfo`, `showWarning` methods is called.
+	 *
+	 * @event show
+	 * @param {Object} data Notification data.
+	 * @param {String} data.message Content of the notification.
+	 * @param {'success'|'info'|'warning'} data.type Type of notification.
+	 */
+
+	/**
+	 * Fired when `showSuccess` method is called.
+	 *
+	 * @event show:success
+	 * @param {Object} data Notification data.
+	 * @param {String} data.message Content of the notification.
+	 * @param {'success'} data.type Type of notification.
+	 */
+
+	/**
+	 * Fired when `showInfo` method is called.
+	 *
+	 * @event show:info
+	 * @param {Object} data Notification data.
+	 * @param {String} data.message Content of the notification.
+	 * @param {'info'} data.type Type of notification.
+	 */
+
+	/**
+	 * Fired when `showWarning` method is called.
+	 *
+	 * When this event won't be handled and stopped by `event.stop()` then data.message of this event will
+	 * be automatically displayed as system alert.
+	 *
+	 * @event show:warning
+	 * @param {Object} data Notification data.
+	 * @param {String} data.message Content of the notification.
+	 * @param {'warning'} data.type Type of notification.
+	 */
+}

+ 152 - 0
packages/ckeditor5-ui/tests/notification.js

@@ -0,0 +1,152 @@
+/**
+ * Copyright (c) 2016, CKSource - Frederico Knabben. All rights reserved.
+ */
+
+/* globals window */
+
+import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import Notification from '../src/notification';
+
+describe( 'Notification', () => {
+	let editor, notification;
+
+	testUtils.createSinonSandbox();
+
+	beforeEach( () => {
+		return VirtualTestEditor.create( {
+			plugins: [ Notification ]
+		} )
+		.then( newEditor => {
+			editor = newEditor;
+			notification = editor.plugins.get( Notification );
+		} );
+	} );
+
+	describe( 'init()', () => {
+		it( 'should create notification plugin', () => {
+			expect( notification ).to.instanceof( Notification );
+			expect( notification ).to.instanceof( Plugin );
+		} );
+	} );
+
+	describe( 'showSuccess()', () => {
+		it( 'should fire `show:success` event with given data', () => {
+			const spy = testUtils.sinon.spy();
+
+			notification.on( 'show:success', spy );
+
+			notification.showSuccess( 'foo bar' );
+
+			sinon.assert.calledOnce( spy );
+			expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
+				message: 'foo bar',
+				type: 'success'
+			} );
+		} );
+
+		it( 'should fire `show:success` event with additional namespace', () => {
+			const spy = testUtils.sinon.spy();
+
+			notification.on( 'show:success:something:else', spy );
+
+			notification.showSuccess( 'foo bar', {
+				namespace: 'something:else'
+			} );
+
+			sinon.assert.calledOnce( spy );
+			expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
+				message: 'foo bar',
+				type: 'success'
+			} );
+		} );
+	} );
+
+	describe( 'showInfo()', () => {
+		it( 'should fire `show:info` event with given data', () => {
+			const spy = testUtils.sinon.spy();
+
+			notification.on( 'show:info', spy );
+
+			notification.showInfo( 'foo bar' );
+
+			sinon.assert.calledOnce( spy );
+			expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
+				message: 'foo bar',
+				type: 'info'
+			} );
+		} );
+
+		it( 'should fire `show:info` event with additional namespace', () => {
+			const spy = testUtils.sinon.spy();
+
+			notification.on( 'show:info:something:else', spy );
+
+			notification.showInfo( 'foo bar', {
+				namespace: 'something:else'
+			} );
+
+			sinon.assert.calledOnce( spy );
+			expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
+				message: 'foo bar',
+				type: 'info'
+			} );
+		} );
+	} );
+
+	describe( 'showWarning()', () => {
+		let alertStub;
+
+		beforeEach( () => {
+			alertStub = testUtils.sinon.stub( window, 'alert' );
+		} );
+
+		it( 'should fire `show:warning` event with given data', () => {
+			const spy = testUtils.sinon.spy();
+
+			notification.on( 'show:warning', spy );
+
+			notification.showWarning( 'foo bar' );
+
+			sinon.assert.calledOnce( spy );
+			expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
+				message: 'foo bar',
+				type: 'warning'
+			} );
+		} );
+
+		it( 'should fire `show:warning` event with additional namespace', () => {
+			const spy = testUtils.sinon.spy();
+
+			notification.on( 'show:warning:something:else', spy );
+
+			notification.showWarning( 'foo bar', {
+				namespace: 'something:else'
+			} );
+
+			sinon.assert.calledOnce( spy );
+			expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
+				message: 'foo bar',
+				type: 'warning'
+			} );
+		} );
+
+		it( 'should display `warning` message as system alert if is not cached and stopped by other plugins', () => {
+			notification.showWarning( 'foo bar' );
+
+			sinon.assert.calledOnce( alertStub );
+			expect( alertStub.firstCall.args[ 0 ] ).to.equal( 'foo bar' );
+		} );
+
+		it( 'should not display alert when `warning` message is cached and stopped by other plugins', () => {
+			notification.on( 'show:warning', evt => {
+				evt.stop();
+			} );
+
+			notification.showWarning( 'foo bar' );
+
+			sinon.assert.notCalled( alertStub );
+		} );
+	} );
+} );