[IMP] web: convert notification popup in service and add buttons params
this.call('web.Notification', 'notify', params)
Display a notification at the appropriate location, and returns the
reference id to the same widget. Note that this method does not wait
for the appendTo method to complete.
@param {Object} params
@param {string} params.title notification title
@param {string} params.message notification main message
@param {string} params.type 'notification' or 'warning'
@param {boolean} [params.sticky=false] if true, the notification will stay
visible until the user clicks on it.
@param {string} [params.className] className to add on the dom
@param {function} [params.onClose] callback when the user click on the x
or when the notification is auto close (no sticky)
@param {Array<Object>} params.buttons
@param {function} params.buttons[0].click callback on click
@param {Boolean} [params.buttons[0].primary] display the button as primary
@param {string} [params.buttons[0].text] button label
@param {string} [params.buttons[0].icon] font-awsome className or image src
@returns {Number} notification id
this.call('web.Notification', 'close', notificationId)
This commit is contained in:
committed by
Géry Debongnie
parent
67a7d1f2b5
commit
07cd24b1ec
@@ -21,7 +21,6 @@ var Dialog = require('web.Dialog');
|
||||
var dom = require('web.dom');
|
||||
var KeyboardNavigationMixin = require('web.KeyboardNavigationMixin');
|
||||
var Loading = require('web.Loading');
|
||||
var NotificationManager = require('web.NotificationManager');
|
||||
var RainbowMan = require('web.RainbowMan');
|
||||
var ServiceProviderMixin = require('web.ServiceProviderMixin');
|
||||
var session = require('web.session');
|
||||
@@ -31,6 +30,7 @@ var _t = core._t;
|
||||
var qweb = core.qweb;
|
||||
|
||||
var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMixin, {
|
||||
dependencies: ['notification'],
|
||||
events: _.extend(KeyboardNavigationMixin.events, {}),
|
||||
custom_events: {
|
||||
clear_uncommitted_changes: function (e) {
|
||||
@@ -46,13 +46,6 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
||||
// the next events are dedicated to generic services required by
|
||||
// downstream widgets. Mainly side effects, such as rpcs, notifications
|
||||
// or cache.
|
||||
|
||||
// notifications, warnings and effects
|
||||
notification: function (e) {
|
||||
if (this.notification_manager) {
|
||||
this.notification_manager.notify(e.data.title, e.data.message, e.data.sticky);
|
||||
}
|
||||
},
|
||||
warning: '_onDisplayWarning',
|
||||
load_action: '_onLoadAction',
|
||||
load_views: function (event) {
|
||||
@@ -120,7 +113,6 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
||||
self.bind_events();
|
||||
return $.when(
|
||||
self.set_action_manager(),
|
||||
self.set_notification_manager(),
|
||||
self.set_loading()
|
||||
);
|
||||
}).then(function () {
|
||||
@@ -174,8 +166,8 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
||||
}
|
||||
this._hideAccessKeyOverlay();
|
||||
});
|
||||
core.bus.on('connection_lost', this, this.on_connection_lost);
|
||||
core.bus.on('connection_restored', this, this.on_connection_restored);
|
||||
core.bus.on('connection_lost', this, this._onConnectionLost);
|
||||
core.bus.on('connection_restored', this, this._onConnectionRestored);
|
||||
|
||||
// crash manager integration
|
||||
session.on('error', crash_manager, crash_manager.rpc_error);
|
||||
@@ -222,10 +214,6 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
||||
});
|
||||
});
|
||||
},
|
||||
set_notification_manager: function () {
|
||||
this.notification_manager = new NotificationManager(this);
|
||||
return this.notification_manager.appendTo(this.$el);
|
||||
},
|
||||
set_loading: function () {
|
||||
this.loading = new Loading(this);
|
||||
return this.loading.appendTo(this.$el);
|
||||
@@ -309,24 +297,6 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
||||
// --------------------------------------------------------------
|
||||
// Connection notifications
|
||||
// --------------------------------------------------------------
|
||||
on_connection_lost: function () {
|
||||
this.connection_notification = this.notification_manager.notify(
|
||||
_t('Connection lost'),
|
||||
_t('Trying to reconnect...'),
|
||||
true
|
||||
);
|
||||
},
|
||||
on_connection_restored: function () {
|
||||
if (this.connection_notification) {
|
||||
this.connection_notification.destroy();
|
||||
this.notification_manager.notify(
|
||||
_t('Connection restored'),
|
||||
_t('You are back online'),
|
||||
false
|
||||
);
|
||||
this.connection_notification = false;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Handler to be overridden, called each time the UI is updated by the
|
||||
* ActionManager.
|
||||
@@ -347,6 +317,34 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Whenever the connection is lost, we need to notify the user.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onConnectionLost: function () {
|
||||
this.connectionNotificationID = this.call('notification', 'notify', {
|
||||
title: _t('Connection lost'),
|
||||
message: _t('Trying to reconnect...'),
|
||||
sticky: true
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Whenever the connection is restored, we need to notify the user.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
_onConnectionRestored: function () {
|
||||
if (this.connectionNotificationID) {
|
||||
this.call('notification', 'close', this.connectionNotificationID);
|
||||
this.call('notification', 'notify', {
|
||||
title: _t('Connection restored'),
|
||||
message: _t('You are back online'),
|
||||
sticky: false
|
||||
});
|
||||
this.connectionNotificationID = false;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {OdooEvent} e
|
||||
@@ -360,7 +358,7 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
||||
.then(e.data.on_success);
|
||||
},
|
||||
/**
|
||||
* Displays a warning in a dialog of with the NotificationManager
|
||||
* Displays a warning in a dialog or with the notification service
|
||||
*
|
||||
* @private
|
||||
* @param {OdooEvent} e
|
||||
@@ -368,7 +366,7 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
||||
* @param {string} e.data.title the warning's title
|
||||
* @param {string} [e.data.type] 'dialog' to display in a dialog
|
||||
* @param {boolean} [e.data.sticky] whether or not the warning should be
|
||||
* sticky (if displayed with the NotificationManager)
|
||||
* sticky (if displayed with the Notification)
|
||||
*/
|
||||
_onDisplayWarning: function (e) {
|
||||
var data = e.data;
|
||||
@@ -378,8 +376,8 @@ var AbstractWebClient = Widget.extend(ServiceProviderMixin, KeyboardNavigationMi
|
||||
title: data.title,
|
||||
$content: qweb.render("CrashManager.warning", data),
|
||||
}).open();
|
||||
} else if (this.notification_manager) {
|
||||
this.notification_manager.warn(e.data.title, e.data.message, e.data.sticky);
|
||||
} else {
|
||||
this.call('notification', 'notify', e.data);
|
||||
}
|
||||
},
|
||||
/**
|
||||
|
||||
@@ -185,10 +185,10 @@ var ServicesMixin = {
|
||||
return def;
|
||||
},
|
||||
do_notify: function (title, message, sticky, className) {
|
||||
this.trigger_up('notification', {title: title, message: message, sticky: sticky, className: className});
|
||||
return this.call('notification', 'notify', {title: title, message: message, sticky: sticky, className: className});
|
||||
},
|
||||
do_warn: function (title, message, sticky, className) {
|
||||
this.trigger_up('warning', {title: title, message: message, sticky: sticky, className: className});
|
||||
return this.call('notification', 'notify', {type: 'warning', title: title, message: message, sticky: sticky, className: className});
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
odoo.define('web.NotificationManager', function (require) {
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* NotificationManager
|
||||
*
|
||||
* The NotificationManager is simply a widget that will be instantiated by the
|
||||
* web client to display notifications in the top/right part of the screen.
|
||||
*
|
||||
* If you want to display such a notification, you probably do not want to do it
|
||||
* by using this file. The proper way is to use the do_warn or do_notify
|
||||
* methods on the Widget class.
|
||||
*
|
||||
* @todo This class should be converted into a proper service. Need the right
|
||||
* service API to do it.
|
||||
*/
|
||||
|
||||
var Notification = require('web.Notification');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var NotificationManager = Widget.extend({
|
||||
className: 'o_notification_manager',
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Display a notification at the appropriate location, and returns the
|
||||
* reference to the same widget (useful to get back a reference).
|
||||
*
|
||||
* Note that this method does not wait for the appendTo method to complete.
|
||||
*
|
||||
* @param {Notification} notification
|
||||
* @returns {Notification}
|
||||
*/
|
||||
display: function (notification) {
|
||||
notification.appendTo(this.$el);
|
||||
return notification;
|
||||
},
|
||||
/**
|
||||
* Will display a notification of type 'notification'
|
||||
*
|
||||
* @param {string} title
|
||||
* @param {string} text
|
||||
* @param {boolean} sticky
|
||||
* @returns {Notification}
|
||||
*/
|
||||
notify: function (title, text, sticky) {
|
||||
return this._display(title, text, sticky, 'notification');
|
||||
},
|
||||
/**
|
||||
* Will display a notification of type 'warning'
|
||||
*
|
||||
* @param {string} title
|
||||
* @param {string} text
|
||||
* @param {boolean} sticky
|
||||
* @returns {Notification}
|
||||
*/
|
||||
warn: function (title, text, sticky) {
|
||||
return this._display(title, text, sticky, 'warning');
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Display a notification
|
||||
*
|
||||
* @private
|
||||
* @param {string} title
|
||||
* @param {string} text
|
||||
* @param {boolean} sticky
|
||||
* @param {string} type either 'notification' or 'warning'
|
||||
* @returns {Notification}
|
||||
*/
|
||||
_display: function (title, text, sticky, type) {
|
||||
return this.display(new Notification(this, {
|
||||
title: title,
|
||||
text: text,
|
||||
sticky: sticky,
|
||||
type: type,
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
return NotificationManager;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
odoo.define('web.NotificationService', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractService = require('web.AbstractService');
|
||||
var Notification = require('web.Notification');
|
||||
var core = require('web.core');
|
||||
var id = 0;
|
||||
|
||||
/**
|
||||
* Notification Service
|
||||
*
|
||||
* The Notification Service is simply a service used to display notifications in
|
||||
* the top/right part of the screen.
|
||||
*
|
||||
* If you want to display such a notification, you probably do not want to do it
|
||||
* by using this file. The proper way is to use the do_warn or do_notify
|
||||
* methods on the Widget class.
|
||||
*/
|
||||
|
||||
var NotificationService = AbstractService.extend({
|
||||
name: 'notification',
|
||||
|
||||
custom_events: {
|
||||
close: '_onCloseNotification',
|
||||
},
|
||||
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
this.notifications = {};
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* It may sometimes be useful to close programmatically a notification. For
|
||||
* example, when there is a sticky notification that warns the user about
|
||||
* some condition (connection lost), but the condition do not apply anymore.
|
||||
*
|
||||
* @param {number} notificationId
|
||||
* @param {boolean} [silent=false] if true, the notification does not call
|
||||
* onClose callback
|
||||
*/
|
||||
close: function (notificationId, silent) {
|
||||
var notification = this.notifications[notificationId];
|
||||
if (!notification) {
|
||||
return;
|
||||
}
|
||||
notification.close(silent);
|
||||
},
|
||||
/**
|
||||
* Display a notification at the appropriate location, and returns the
|
||||
* reference id to the same widget.
|
||||
*
|
||||
* Note that this method does not wait for the appendTo method to complete.
|
||||
*
|
||||
* @param {Object} params
|
||||
* @param {string} params.title notification title
|
||||
* @param {string} params.message notification main message
|
||||
* @param {string} params.type 'notification' or 'warning'
|
||||
* @param {boolean} [params.sticky=false] if true, the notification will stay
|
||||
* visible until the user clicks on it.
|
||||
* @param {string} [params.className] className to add on the dom
|
||||
* @param {function} [params.onClose] callback when the user click on the x
|
||||
* or when the notification is auto close (no sticky)
|
||||
* @param {Object[]} params.buttons
|
||||
* @param {function} params.buttons[0].click callback on click
|
||||
* @param {Boolean} [params.buttons[0].primary] display the button as primary
|
||||
* @param {string} [params.buttons[0].text] button label
|
||||
* @param {string} [params.buttons[0].icon] font-awsome className or image src
|
||||
* @returns {Number} notification id
|
||||
*/
|
||||
notify: function (options) {
|
||||
if (!this.$el) {
|
||||
this.$el = $('<div class="o_notification_manager"/>');
|
||||
this.$el.prependTo('body');
|
||||
}
|
||||
var notification = this.notifications[++id] = new Notification(this, options);
|
||||
notification.appendTo(this.$el);
|
||||
return id;
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {OdooEvent} ev
|
||||
*/
|
||||
_onCloseNotification: function (ev) {
|
||||
ev.stopPropagation();
|
||||
for (var notificationId in this.notifications) {
|
||||
if (this.notifications[notificationId] === ev.target) {
|
||||
delete this.notifications[notificationId];
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
core.serviceRegistry.add('notification', NotificationService);
|
||||
|
||||
|
||||
return NotificationService;
|
||||
});
|
||||
@@ -17,26 +17,45 @@ var Widget = require('web.Widget');
|
||||
var Notification = Widget.extend({
|
||||
template: 'Notification',
|
||||
events: {
|
||||
'click .o_close': '_onClose',
|
||||
'click > .o_close': '_onClose',
|
||||
'click .o_buttons button': '_onClickButton'
|
||||
},
|
||||
_autoCloseDelay: 2500,
|
||||
_animationDelay: 400,
|
||||
/**
|
||||
* @override
|
||||
* @param {Widget} parent
|
||||
* @param {Object} params
|
||||
* @param {string} params.title notification title
|
||||
* @param {string} params.text notification main text
|
||||
* @param {string} params.message notification main message
|
||||
* @param {string} params.type 'notification' or 'warning'
|
||||
* @param {boolean} [params.sticky=false] if true, the notification will stay
|
||||
* visible until the user clicks on it.
|
||||
* @param {string} [params.className] className to add on the dom
|
||||
* @param {function} [params.onClose] callback when the user click on the x
|
||||
* or when the notification is auto close (no sticky)
|
||||
* @param {Object[]} params.buttons
|
||||
* @param {function} params.buttons[0].click callback on click
|
||||
* @param {boolean} [params.buttons[0].primary] display the button as primary
|
||||
* @param {string} [params.buttons[0].text] button label
|
||||
* @param {string} [params.buttons[0].icon] font-awsome className or image src
|
||||
*/
|
||||
init: function (parent, params) {
|
||||
this._super.apply(this, arguments);
|
||||
this.title = params.title;
|
||||
this.text = params.text;
|
||||
this.sticky = !!params.sticky;
|
||||
this.type = params.type;
|
||||
this.message = params.message;
|
||||
this.buttons = params.buttons || [];
|
||||
this.sticky = !!this.buttons.length || !!params.sticky;
|
||||
this.type = params.type || 'notification';
|
||||
this.className = params.className || '';
|
||||
this._closeCallback = params.onClose;
|
||||
this.icon = 'fa-lightbulb-o';
|
||||
if (this.buttons && this.buttons.length) {
|
||||
this.icon = 'fa-question-circle-o';
|
||||
}
|
||||
if (this.type === 'warning') {
|
||||
this.template = 'Warning';
|
||||
this.icon = 'fa-exclamation';
|
||||
this.className += ' o_error';
|
||||
}
|
||||
},
|
||||
/**
|
||||
@@ -44,46 +63,68 @@ var Notification = Widget.extend({
|
||||
*/
|
||||
start: function () {
|
||||
var self = this;
|
||||
if (this.type === 'warning') {
|
||||
this.$el.addClass('o_error');
|
||||
}
|
||||
return this._super.apply(this, arguments).then(function () {
|
||||
self.$el.animate({opacity: 1.0}, 400, "swing", function () {
|
||||
self.$el.animate({opacity: 1.0}, self._animationDelay, "swing", function () {
|
||||
if(!self.sticky) {
|
||||
setTimeout(self._destroy.bind(self), 2500);
|
||||
setTimeout(function () {
|
||||
self.close();
|
||||
}, self._autoCloseDelay);
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* This method is used to destroy the widget with a nice animation. We
|
||||
* This method is used to destroy the widget with a nice animation. We
|
||||
* first perform an animation, then call the real destroy method.
|
||||
*
|
||||
* @private
|
||||
* @param {boolean} [silent=false] if true, the notification does not call
|
||||
* _closeCallback method
|
||||
*/
|
||||
_destroy: function () {
|
||||
close: function (silent) {
|
||||
var self = this;
|
||||
this.$el.animate({opacity: 0.0}, 400, "swing", function() {
|
||||
self.$el.animate({height: 0}, 400, "swing", self.destroy.bind(self));
|
||||
});
|
||||
this.trigger_up('close');
|
||||
if (!silent && !this._buttonClicked) {
|
||||
if (this._closeCallback) {
|
||||
this._closeCallback();
|
||||
}
|
||||
}
|
||||
this.$el.animate({opacity: 0.0, height: 0}, this._animationDelay, "swing", self.destroy.bind(self));
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Handlers
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onClickButton: function (ev) {
|
||||
ev.preventDefault();
|
||||
if (this._buttonClicked) {
|
||||
return;
|
||||
}
|
||||
this._buttonClicked = true;
|
||||
var index = $(ev.currentTarget).index();
|
||||
var button = this.buttons[index];
|
||||
if (button.click) {
|
||||
button.click();
|
||||
}
|
||||
this.close(true);
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
* @param {MouseEvent} ev
|
||||
*/
|
||||
_onClose: function (ev) {
|
||||
ev.preventDefault();
|
||||
this._destroy();
|
||||
this.close();
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -115,20 +115,24 @@
|
||||
</t>
|
||||
|
||||
<t t-name="Notification">
|
||||
<div t-attf-class="o_notification #{className}" t-translation="off">
|
||||
<div t-attf-class="o_notification #{widget.className}" t-translation="off">
|
||||
<a t-if="widget.sticky" class="fa fa-times o_close" href="#"/>
|
||||
<div class="o_notification_title">
|
||||
<span class="o_icon fa fa-3x fa-lightbulb-o"/>
|
||||
<span t-attf-class="o_icon fa fa-3x #{widget.icon}"/>
|
||||
<t t-raw="widget.title"/>
|
||||
</div>
|
||||
<div class="o_notification_content" t-if="widget.text"><t t-raw="widget.text"/></div>
|
||||
<div class="o_notification_content" t-if="widget.message"><t t-raw="widget.message"/></div>
|
||||
<div t-if="widget.buttons.length" class="o_buttons">
|
||||
<button t-foreach="widget.buttons" t-as="button" t-attf-class="btn btn-sm #{button.primary ? 'btn-primary' : 'btn-default'}" type="button">
|
||||
<t t-if="button.icon">
|
||||
<i t-if="button.icon.indexOf('fa-') === 0" t-attf-class="fa fa-fw o_button_icon #{button.icon}"/>
|
||||
<img t-else="" t-att-src="button.icon"/>
|
||||
</t>
|
||||
<span><t t-esc="button.text"/></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-name="Warning" t-extend="Notification">
|
||||
<t t-jquery=".o_icon" t-operation="replace">
|
||||
<span class="o_icon fa fa-3x fa-exclamation"/>
|
||||
</t>
|
||||
</t>
|
||||
|
||||
<div t-name="CrashManager.warning" class="o_dialog_warning">
|
||||
<t t-js="d">
|
||||
|
||||
@@ -2,7 +2,7 @@ odoo.define('web.action_manager_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var ReportClientAction = require('report.client_action');
|
||||
|
||||
var NotificationService = require('web.NotificationService');
|
||||
var AbstractAction = require('web.AbstractAction');
|
||||
var AbstractStorageService = require('web.AbstractStorageService');
|
||||
var BasicFields = require('web.basic_fields');
|
||||
@@ -1770,6 +1770,7 @@ QUnit.module('ActionManager', {
|
||||
assert.step(params.url);
|
||||
params.success();
|
||||
params.complete();
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1797,7 +1798,11 @@ QUnit.module('ActionManager', {
|
||||
actions: this.actions,
|
||||
archs: this.archs,
|
||||
data: this.data,
|
||||
services: [ReportService],
|
||||
services: [ReportService, NotificationService.extend({
|
||||
notify: function (params) {
|
||||
assert.step(params.type || 'notification');
|
||||
}
|
||||
})],
|
||||
mockRPC: function (route, args) {
|
||||
assert.step(args.method || route);
|
||||
if (route === '/report/check_wkhtmltopdf') {
|
||||
@@ -1810,11 +1815,7 @@ QUnit.module('ActionManager', {
|
||||
assert.step(params.url);
|
||||
params.success();
|
||||
params.complete();
|
||||
},
|
||||
},
|
||||
intercepts: {
|
||||
notification: function () {
|
||||
assert.step('notification');
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1851,7 +1852,11 @@ QUnit.module('ActionManager', {
|
||||
actions: this.actions,
|
||||
archs: this.archs,
|
||||
data: this.data,
|
||||
services: [ReportService],
|
||||
services: [ReportService, NotificationService.extend({
|
||||
notify: function (params) {
|
||||
assert.step(params.type || 'notification');
|
||||
}
|
||||
})],
|
||||
mockRPC: function (route, args) {
|
||||
assert.step(args.method || route);
|
||||
if (route === '/report/check_wkhtmltopdf') {
|
||||
@@ -1865,11 +1870,7 @@ QUnit.module('ActionManager', {
|
||||
session: {
|
||||
get_file: function (params) {
|
||||
assert.step(params.url); // should not be called
|
||||
},
|
||||
},
|
||||
intercepts: {
|
||||
notification: function () {
|
||||
assert.step('notification');
|
||||
return true;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -0,0 +1,247 @@
|
||||
odoo.define('web.notification_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var AbstractView = require('web.AbstractView');
|
||||
var Notification = require('web.Notification');
|
||||
var NotificationService = require('web.NotificationService');
|
||||
|
||||
var testUtils = require('web.test_utils');
|
||||
var createView = testUtils.createView;
|
||||
|
||||
|
||||
QUnit.module('Services', {
|
||||
beforeEach: function () {
|
||||
testUtils.patch(Notification, {
|
||||
_autoCloseDelay: 0,
|
||||
_animationDelay: 0,
|
||||
});
|
||||
this.viewParams = {
|
||||
View: AbstractView,
|
||||
arch: '<fake/>',
|
||||
data: {
|
||||
fake_model: {
|
||||
fields: {},
|
||||
record: [],
|
||||
},
|
||||
},
|
||||
model: 'fake_model',
|
||||
services: [NotificationService],
|
||||
};
|
||||
},
|
||||
afterEach: function () {
|
||||
testUtils.unpatch(Notification);
|
||||
}
|
||||
}, function () {
|
||||
QUnit.module('Notification');
|
||||
|
||||
QUnit.test('Display a simple notification', function (assert) {
|
||||
var done = assert.async();
|
||||
assert.expect(4);
|
||||
|
||||
var view = createView(this.viewParams);
|
||||
view.call('notification', 'notify', {
|
||||
title: 'a',
|
||||
message: 'b',
|
||||
});
|
||||
var $notification = $('body .o_notification_manager .o_notification');
|
||||
assert.strictEqual(_.str.trim($notification.html().replace(/\s+/g, ' ')),
|
||||
"<div class=\"o_notification_title\"> <span class=\"o_icon fa fa-3x fa-lightbulb-o\"></span> a </div> <div class=\"o_notification_content\">b</div>",
|
||||
"should display notification");
|
||||
assert.strictEqual($notification.find('.o_close').length, 0, "should not display the close button in ");
|
||||
setTimeout(function () {
|
||||
assert.strictEqual($notification.is(':hidden'), true, "should hide the notification");
|
||||
assert.strictEqual($('body .o_notification_manager .o_notification').length, 0, "should destroy the notification");
|
||||
view.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('Display a warning', function (assert) {
|
||||
var done = assert.async();
|
||||
assert.expect(1);
|
||||
|
||||
var view = createView(this.viewParams);
|
||||
view.call('notification', 'notify', {
|
||||
title: 'a',
|
||||
message: 'b',
|
||||
type: 'warning'
|
||||
});
|
||||
var $notification = $('body .o_notification_manager .o_notification');
|
||||
assert.strictEqual(_.str.trim($notification.html().replace(/\s+/g, ' ')),
|
||||
"<div class=\"o_notification_title\"> <span class=\"o_icon fa fa-3x fa-exclamation\"></span> a </div> <div class=\"o_notification_content\">b</div>",
|
||||
"should display notification");
|
||||
view.destroy();
|
||||
setTimeout(done);
|
||||
});
|
||||
|
||||
QUnit.test('Display a sticky notification', function (assert) {
|
||||
var done = assert.async();
|
||||
assert.expect(3);
|
||||
|
||||
var view = createView(this.viewParams);
|
||||
view.call('notification', 'notify', {
|
||||
title: 'a',
|
||||
message: 'b',
|
||||
sticky: true,
|
||||
});
|
||||
var $notification = $('body .o_notification_manager .o_notification');
|
||||
assert.strictEqual($notification.find('.o_close').length, 1, "should display the close button in notification");
|
||||
|
||||
setTimeout(function () {
|
||||
assert.strictEqual($notification.is(':hidden'), false, "should not hide the notification automatically");
|
||||
$notification.find('.o_close').click();
|
||||
setTimeout(function () {
|
||||
assert.strictEqual($('body .o_notification_manager .o_notification').length, 0, "should destroy the notification");
|
||||
view.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('Display a simple notification with onClose callback when automatically close', function (assert) {
|
||||
var done = assert.async();
|
||||
assert.expect(2);
|
||||
|
||||
var close = 0;
|
||||
var view = createView(this.viewParams);
|
||||
view.call('notification', 'notify', {
|
||||
title: 'a',
|
||||
message: 'b',
|
||||
onClose: function () {
|
||||
close++;
|
||||
}
|
||||
});
|
||||
view.destroy();
|
||||
assert.strictEqual(close, 0, "should wait to call onClose method once");
|
||||
setTimeout(function () {
|
||||
assert.strictEqual(close, 1, "should call onClose method once");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('Display a sticky notification with onClose callback', function (assert) {
|
||||
var done = assert.async();
|
||||
assert.expect(2);
|
||||
|
||||
testUtils.unpatch(Notification);
|
||||
testUtils.patch(Notification, {
|
||||
_autoCloseDelay: 2500,
|
||||
_animationDelay: 0,
|
||||
});
|
||||
var view = createView(this.viewParams);
|
||||
|
||||
var close = 0;
|
||||
view.call('notification', 'notify', {
|
||||
title: 'a',
|
||||
message: 'b',
|
||||
sticky: true,
|
||||
onClose: function () {
|
||||
close++;
|
||||
}
|
||||
});
|
||||
assert.strictEqual(close, 0, "should wait to call onClose method once");
|
||||
$('body .o_notification_manager .o_notification .o_close').click();
|
||||
setTimeout(function () {
|
||||
assert.strictEqual(close, 1, "should call onClose method once");
|
||||
view.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('Display a question', function (assert) {
|
||||
var done = assert.async();
|
||||
assert.expect(8);
|
||||
|
||||
var view = createView(this.viewParams);
|
||||
function notification (inc) {
|
||||
return {
|
||||
title: 'a' + inc,
|
||||
message: 'b' + inc,
|
||||
buttons: [
|
||||
{
|
||||
text: 'accept' + inc,
|
||||
primary: true,
|
||||
click: function () {
|
||||
assert.step('accept' + inc);
|
||||
},
|
||||
},
|
||||
{
|
||||
text: 'refuse' + inc,
|
||||
click: function () {
|
||||
assert.step('refuse' + inc);
|
||||
},
|
||||
}
|
||||
],
|
||||
onClose: function () {
|
||||
assert.step('close' + inc);
|
||||
}
|
||||
};
|
||||
};
|
||||
view.call('notification', 'notify', notification(0));
|
||||
view.call('notification', 'notify', notification(1));
|
||||
view.call('notification', 'notify', notification(2));
|
||||
|
||||
var $notification = $('body .o_notification_manager .o_notification');
|
||||
assert.strictEqual($notification.eq(0).find('.o_close').length, 1, "should display the close button in notification");
|
||||
assert.strictEqual(_.str.trim($notification.eq(0).html().replace(/\s+/g, ' ')),
|
||||
"<a class=\"fa fa-times o_close\" href=\"#\"></a> <div class=\"o_notification_title\"> <span class=\"o_icon fa fa-3x fa-question-circle-o\"></span> a0 </div> <div class=\"o_notification_content\">b0</div> <div class=\"o_buttons\"> <button type=\"button\" class=\"btn btn-sm btn-primary\"> <span>accept0</span> </button><button type=\"button\" class=\"btn btn-sm btn-default\"> <span>refuse0</span> </button> </div>",
|
||||
"should display notification");
|
||||
|
||||
$notification.find('.o_buttons button:contains(accept0)').click();
|
||||
$notification.find('.o_buttons button:contains(accept0)').click();
|
||||
$notification.find('.o_buttons button:contains(refuse0)').click();
|
||||
$notification.eq(0).find('.o_close').click();
|
||||
|
||||
$notification.find('.o_buttons button:contains(refuse1)').click();
|
||||
|
||||
$notification.eq(2).find('.o_close').click();
|
||||
|
||||
setTimeout(function () {
|
||||
assert.strictEqual($notification.is(':hidden'), true, "should hide the notification");
|
||||
assert.strictEqual($('body .o_notification_manager .o_notification').length, 0, "should destroy the notification");
|
||||
assert.verifySteps(['accept0', 'refuse1', 'close2']);
|
||||
view.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.test('call close notification service', function (assert) {
|
||||
var done = assert.async();
|
||||
assert.expect(2);
|
||||
|
||||
testUtils.unpatch(Notification);
|
||||
testUtils.patch(Notification, {
|
||||
_autoCloseDelay: 2500,
|
||||
_animationDelay: 0,
|
||||
});
|
||||
var view = createView(this.viewParams);
|
||||
|
||||
var close = 0;
|
||||
var notificationId0 = view.call('notification', 'notify', {
|
||||
title: 'a',
|
||||
message: 'b',
|
||||
onClose: function () {
|
||||
close++;
|
||||
}
|
||||
});
|
||||
var notificationId1 = view.call('notification', 'notify', {
|
||||
title: 'a',
|
||||
message: 'b',
|
||||
sticky: true,
|
||||
onClose: function () {
|
||||
close++;
|
||||
}
|
||||
});
|
||||
|
||||
view.call('notification', 'close', notificationId0);
|
||||
view.call('notification', 'close', notificationId1);
|
||||
|
||||
setTimeout(function () {
|
||||
assert.strictEqual($('body .o_notification_manager .o_notification').length, 0, "should destroy the notifications");
|
||||
assert.strictEqual(close, 2, "should call onClose method twice");
|
||||
view.destroy();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});});
|
||||
@@ -8,6 +8,7 @@ var core = require('web.core');
|
||||
var fieldRegistry = require('web.field_registry');
|
||||
var FormView = require('web.FormView');
|
||||
var mixins = require('web.mixins');
|
||||
var NotificationService = require('web.NotificationService');
|
||||
var pyeval = require('web.pyeval');
|
||||
var testUtils = require('web.test_utils');
|
||||
var widgetRegistry = require('web.widget_registry');
|
||||
@@ -2669,13 +2670,17 @@ QUnit.module('Views', {
|
||||
arch: '<form string="Partners">' +
|
||||
'<group><field name="foo"/></group>' +
|
||||
'</form>',
|
||||
});
|
||||
|
||||
testUtils.intercept(form, 'warning', function (event) {
|
||||
assert.strictEqual(event.data.title, 'The following fields are invalid:',
|
||||
"should have a warning with correct title");
|
||||
assert.strictEqual(event.data.message, '<ul><li>Foo</li></ul>',
|
||||
"should have a warning with correct message");
|
||||
services: [NotificationService.extend({
|
||||
notify: function (params) {
|
||||
if (params.type !== 'warning') {
|
||||
return;
|
||||
}
|
||||
assert.strictEqual(params.title, 'The following fields are invalid:',
|
||||
"should have a warning with correct title");
|
||||
assert.strictEqual(params.message, '<ul><li>Foo</li></ul>',
|
||||
"should have a warning with correct message");
|
||||
}
|
||||
})],
|
||||
});
|
||||
|
||||
form.$buttons.find('.o_form_button_save').click();
|
||||
@@ -4713,11 +4718,11 @@ QUnit.module('Views', {
|
||||
}
|
||||
return result;
|
||||
},
|
||||
intercepts: {
|
||||
warning: function () {
|
||||
assert.step('warning');
|
||||
},
|
||||
},
|
||||
services: [NotificationService.extend({
|
||||
notify: function (params) {
|
||||
assert.step(params.type);
|
||||
}
|
||||
})],
|
||||
});
|
||||
|
||||
form.$buttons.find('.o_form_button_edit').click();
|
||||
|
||||
@@ -8,6 +8,7 @@ var basicFields = require('web.basic_fields');
|
||||
var FormView = require('web.FormView');
|
||||
var ListView = require('web.ListView');
|
||||
var mixins = require('web.mixins');
|
||||
var NotificationService = require('web.NotificationService');
|
||||
var testUtils = require('web.test_utils');
|
||||
var widgetRegistry = require('web.widget_registry');
|
||||
var Widget = require('web.Widget');
|
||||
@@ -2095,11 +2096,13 @@ QUnit.module('Views', {
|
||||
'<field name="foo" required="1"/>' +
|
||||
'<field name="bar"/>' +
|
||||
'</tree>',
|
||||
intercepts: {
|
||||
warning: function () {
|
||||
warnings++;
|
||||
},
|
||||
},
|
||||
services: [NotificationService.extend({
|
||||
notify: function (params) {
|
||||
if (params.type === 'warning') {
|
||||
warnings++;
|
||||
}
|
||||
}
|
||||
})],
|
||||
});
|
||||
|
||||
// Start first line edition
|
||||
|
||||
@@ -192,7 +192,7 @@
|
||||
<script type="text/javascript" src="/web/static/src/js/services/data_manager.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/services/report_service.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/services/session.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/services/notification_manager.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/services/notification_service.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/auto_complete.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/change_password.js"></script>
|
||||
<script type="text/javascript" src="/web/static/src/js/widgets/debug_manager.js"></script>
|
||||
@@ -499,6 +499,8 @@
|
||||
|
||||
<script type="text/javascript" src="/base/static/tests/base_settings_tests.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/web/static/tests/services/notification_service_tests.js"></script>
|
||||
|
||||
<script type="text/javascript" src="/web/static/tests/fields/basic_fields_tests.js"></script>
|
||||
<script type="text/javascript" src="/web/static/tests/fields/field_utils_tests.js"></script>
|
||||
<script type="text/javascript" src="/web/static/tests/fields/relational_fields_tests.js"></script>
|
||||
|
||||
@@ -2,7 +2,7 @@ odoo.define('web_settings_dashboard.settings_dashboard_tests', function (require
|
||||
"use strict";
|
||||
|
||||
var webSettingsDashboard = require('web_settings_dashboard');
|
||||
|
||||
var NotificationService = require('web.NotificationService');
|
||||
var testUtils = require('web.test_utils');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
@@ -88,9 +88,11 @@ QUnit.module('settings_dashboard', function () {
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
intercepts: {
|
||||
warning: assert.step.bind(assert, 'warning'),
|
||||
},
|
||||
services: [NotificationService.extend({
|
||||
notify: function (params) {
|
||||
assert.step(params.type);
|
||||
}
|
||||
})],
|
||||
});
|
||||
|
||||
// enter an invalid email address to invite
|
||||
@@ -165,9 +167,11 @@ QUnit.module('settings_dashboard', function () {
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
intercepts: {
|
||||
warning: assert.step.bind(assert, 'warning'),
|
||||
},
|
||||
services: [NotificationService.extend({
|
||||
notify: function (params) {
|
||||
assert.step(params.type);
|
||||
}
|
||||
})],
|
||||
});
|
||||
|
||||
// simulate a copy paste of multiple email addresses
|
||||
|
||||
Reference in New Issue
Block a user