Skip to content
This repository was archived by the owner on Jun 26, 2020. It is now read-only.

Commit f2dd63f

Browse files
authored
Feature: Introduced notification plugin. Closes #189.
2 parents 3ceb6a6 + d06f85e commit f2dd63f

File tree

2 files changed

+347
-0
lines changed

2 files changed

+347
-0
lines changed

src/notification/notification.js

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
/**
2+
* @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
3+
* For licensing, see LICENSE.md.
4+
*/
5+
6+
/**
7+
* @module ui/notification
8+
*/
9+
10+
/* globals window */
11+
12+
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
13+
14+
/**
15+
* The Notification plugin.
16+
*
17+
* This plugin sends few base types of notifications: `success`, `info` and `warning`. This notifications need to be
18+
* handled and displayed by plugin responsible for showing UI of the notifications. Using this plugin for dispatching
19+
* notifications makes possible to switch the notifications UI.
20+
*
21+
* Note that every unhandled and not stopped `warning` notification will be displayed as system alert.
22+
* See {@link module:ui/notification~Notification#showWarning}.
23+
*
24+
* @extends module:core/plugin~Plugin
25+
*/
26+
export default class Notification extends Plugin {
27+
/**
28+
* @inheritDoc
29+
*/
30+
static get pluginName() {
31+
return 'notification';
32+
}
33+
34+
/**
35+
* @inheritDoc
36+
*/
37+
init() {
38+
// Each unhandled and not stopped `show:warning` event is displayed as system alert.
39+
this.on( 'show:warning', ( evt, data ) => {
40+
window.alert( data.message );
41+
}, { priority: 'lowest' } );
42+
}
43+
44+
/**
45+
* Shows success notification.
46+
*
47+
* At default it fires `show:success` event with given data but event namespace can be extended
48+
* by `data.namespace` option e.g.
49+
*
50+
* showSuccess( 'Image is uploaded.', {
51+
* namespace: 'upload:image'
52+
* } );
53+
*
54+
* will fire `show:success:upload:image` event.
55+
*
56+
* @param {String} message Content of the notification.
57+
* @param {Object} [data={}] Additional data.
58+
* @param {String} [data.namespace] Additional event namespace.
59+
*/
60+
showSuccess( message, data = {} ) {
61+
this._showNotification( {
62+
message: message,
63+
type: 'success',
64+
namespace: data.namespace
65+
} );
66+
}
67+
68+
/**
69+
* Shows info notification.
70+
*
71+
* At default it fires `show:info` event with given data but event namespace can be extended
72+
* by `data.namespace` option e.g.
73+
*
74+
* showInfo( 'Editor is offline.', {
75+
* namespace: 'editor:status'
76+
* } );
77+
*
78+
* will fire `show:info:editor:status` event.
79+
*
80+
* @param {String} message Content of the notification.
81+
* @param {Object} [data={}] Additional data.
82+
* @param {String} [data.namespace] Additional event namespace.
83+
*/
84+
showInfo( message, data = {} ) {
85+
this._showNotification( {
86+
message: message,
87+
type: 'info',
88+
namespace: data.namespace
89+
} );
90+
}
91+
92+
/**
93+
* Shows warning notification.
94+
*
95+
* At default it fires `show:warning` event with given data but event namespace can be extended
96+
* by `data.namespace` option e.g.
97+
*
98+
* showWarning( 'Image upload error.', {
99+
* namespace: 'upload:image'
100+
* } );
101+
*
102+
* will fire `show:warning:upload:image` event.
103+
*
104+
* Note that each unhandled and not stopped `warning` notification will be displayed as system alert.
105+
* Plugin responsible for displaying warnings should `stop()` the event to prevent of displaying it as alert:
106+
*
107+
* notifications.on( 'show:warning', ( evt, data ) => {
108+
* // Do something with data.
109+
*
110+
* // Stop this event to prevent of displaying as alert.
111+
* evt.stop();
112+
* } );
113+
*
114+
* You can attach many listeners to the same event and `stop()` this event in the listener with the low priority:
115+
*
116+
* notifications.on( 'show:warning', ( evt, data ) => {
117+
* // Show warning in the UI, but not stop it.
118+
* } );
119+
*
120+
* notifications.on( 'show:warning', ( evt, data ) => {
121+
* // Log warning to some error tracker.
122+
*
123+
* // Stop this event to prevent of displaying as alert.
124+
* evt.stop();
125+
* }, { priority: 'low' } );
126+
*
127+
* @param {String} message Content of the notification.
128+
* @param {Object} [data={}] Additional data.
129+
* @param {String} [data.namespace] Additional event namespace.
130+
*/
131+
showWarning( message, data = {} ) {
132+
this._showNotification( {
133+
message: message,
134+
type: 'warning',
135+
namespace: data.namespace
136+
} );
137+
}
138+
139+
/**
140+
* Fires `show` event with specified type, namespace and message.
141+
*
142+
* @private
143+
* @param {Object} data Message data.
144+
* @param {String} data.message Content of the notification.
145+
* @param {'success'|'info'|'warning'} data.type Type of message.
146+
* @param {String} [data.namespace] Additional event namespace.
147+
*/
148+
_showNotification( data ) {
149+
const event = `show:${ data.type }` + ( data.namespace ? `:${ data.namespace }` : '' );
150+
151+
this.fire( event, {
152+
message: data.message,
153+
type: data.type
154+
} );
155+
}
156+
157+
/**
158+
* Fired when one of `showSuccess`, `showInfo`, `showWarning` methods is called.
159+
*
160+
* @event show
161+
* @param {Object} data Notification data.
162+
* @param {String} data.message Content of the notification.
163+
* @param {'success'|'info'|'warning'} data.type Type of notification.
164+
*/
165+
166+
/**
167+
* Fired when `showSuccess` method is called.
168+
*
169+
* @event show:success
170+
* @param {Object} data Notification data.
171+
* @param {String} data.message Content of the notification.
172+
* @param {'success'} data.type Type of notification.
173+
*/
174+
175+
/**
176+
* Fired when `showInfo` method is called.
177+
*
178+
* @event show:info
179+
* @param {Object} data Notification data.
180+
* @param {String} data.message Content of the notification.
181+
* @param {'info'} data.type Type of notification.
182+
*/
183+
184+
/**
185+
* Fired when `showWarning` method is called.
186+
*
187+
* When this event won't be handled and stopped by `event.stop()` then data.message of this event will
188+
* be automatically displayed as system alert.
189+
*
190+
* @event show:warning
191+
* @param {Object} data Notification data.
192+
* @param {String} data.message Content of the notification.
193+
* @param {'warning'} data.type Type of notification.
194+
*/
195+
}

tests/notification/notification.js

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/**
2+
* Copyright (c) 2016, CKSource - Frederico Knabben. All rights reserved.
3+
*/
4+
5+
/* globals window */
6+
7+
import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
8+
import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
9+
import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
10+
import Notification from '../../src/notification/notification';
11+
12+
describe( 'Notification', () => {
13+
let editor, notification;
14+
15+
testUtils.createSinonSandbox();
16+
17+
beforeEach( () => {
18+
return VirtualTestEditor.create( {
19+
plugins: [ Notification ]
20+
} )
21+
.then( newEditor => {
22+
editor = newEditor;
23+
notification = editor.plugins.get( Notification );
24+
} );
25+
} );
26+
27+
describe( 'init()', () => {
28+
it( 'should create notification plugin', () => {
29+
expect( notification ).to.instanceof( Notification );
30+
expect( notification ).to.instanceof( Plugin );
31+
} );
32+
} );
33+
34+
describe( 'showSuccess()', () => {
35+
it( 'should fire `show:success` event with given data', () => {
36+
const spy = testUtils.sinon.spy();
37+
38+
notification.on( 'show:success', spy );
39+
40+
notification.showSuccess( 'foo bar' );
41+
42+
sinon.assert.calledOnce( spy );
43+
expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
44+
message: 'foo bar',
45+
type: 'success'
46+
} );
47+
} );
48+
49+
it( 'should fire `show:success` event with additional namespace', () => {
50+
const spy = testUtils.sinon.spy();
51+
52+
notification.on( 'show:success:something:else', spy );
53+
54+
notification.showSuccess( 'foo bar', {
55+
namespace: 'something:else'
56+
} );
57+
58+
sinon.assert.calledOnce( spy );
59+
expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
60+
message: 'foo bar',
61+
type: 'success'
62+
} );
63+
} );
64+
} );
65+
66+
describe( 'showInfo()', () => {
67+
it( 'should fire `show:info` event with given data', () => {
68+
const spy = testUtils.sinon.spy();
69+
70+
notification.on( 'show:info', spy );
71+
72+
notification.showInfo( 'foo bar' );
73+
74+
sinon.assert.calledOnce( spy );
75+
expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
76+
message: 'foo bar',
77+
type: 'info'
78+
} );
79+
} );
80+
81+
it( 'should fire `show:info` event with additional namespace', () => {
82+
const spy = testUtils.sinon.spy();
83+
84+
notification.on( 'show:info:something:else', spy );
85+
86+
notification.showInfo( 'foo bar', {
87+
namespace: 'something:else'
88+
} );
89+
90+
sinon.assert.calledOnce( spy );
91+
expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
92+
message: 'foo bar',
93+
type: 'info'
94+
} );
95+
} );
96+
} );
97+
98+
describe( 'showWarning()', () => {
99+
let alertStub;
100+
101+
beforeEach( () => {
102+
alertStub = testUtils.sinon.stub( window, 'alert' );
103+
} );
104+
105+
it( 'should fire `show:warning` event with given data', () => {
106+
const spy = testUtils.sinon.spy();
107+
108+
notification.on( 'show:warning', spy );
109+
110+
notification.showWarning( 'foo bar' );
111+
112+
sinon.assert.calledOnce( spy );
113+
expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
114+
message: 'foo bar',
115+
type: 'warning'
116+
} );
117+
} );
118+
119+
it( 'should fire `show:warning` event with additional namespace', () => {
120+
const spy = testUtils.sinon.spy();
121+
122+
notification.on( 'show:warning:something:else', spy );
123+
124+
notification.showWarning( 'foo bar', {
125+
namespace: 'something:else'
126+
} );
127+
128+
sinon.assert.calledOnce( spy );
129+
expect( spy.firstCall.args[ 1 ] ).to.deep.equal( {
130+
message: 'foo bar',
131+
type: 'warning'
132+
} );
133+
} );
134+
135+
it( 'should display `warning` message as system alert if is not cached and stopped by other plugins', () => {
136+
notification.showWarning( 'foo bar' );
137+
138+
sinon.assert.calledOnce( alertStub );
139+
expect( alertStub.firstCall.args[ 0 ] ).to.equal( 'foo bar' );
140+
} );
141+
142+
it( 'should not display alert when `warning` message is cached and stopped by other plugins', () => {
143+
notification.on( 'show:warning', evt => {
144+
evt.stop();
145+
} );
146+
147+
notification.showWarning( 'foo bar' );
148+
149+
sinon.assert.notCalled( alertStub );
150+
} );
151+
} );
152+
} );

0 commit comments

Comments
 (0)