From 95504aa0e1556be391754caa85267a8e164dc7ee Mon Sep 17 00:00:00 2001 From: Samuel Degueldre Date: Mon, 5 Jul 2021 09:37:14 +0000 Subject: [PATCH] [REF] web: remove legacy notification service In previous commits, all references to the legacy notifications and notification service have been removed, this commit removes the now unused legacy notification and notification service Part of #72675 --- .../frontend/notification/notification.js | 180 ----------- .../frontend/notification/notification.scss | 25 -- .../frontend/notification/notification.xml | 32 -- .../notification/notification_service.js | 122 -------- .../frontend/notification_service_tests.js | 289 ------------------ .../static/tests/legacy/helpers/test_utils.js | 1 - 6 files changed, 649 deletions(-) delete mode 100644 addons/web/static/src/legacy/frontend/notification/notification.js delete mode 100644 addons/web/static/src/legacy/frontend/notification/notification.scss delete mode 100644 addons/web/static/src/legacy/frontend/notification/notification.xml delete mode 100644 addons/web/static/src/legacy/frontend/notification/notification_service.js delete mode 100644 addons/web/static/tests/legacy/frontend/notification_service_tests.js diff --git a/addons/web/static/src/legacy/frontend/notification/notification.js b/addons/web/static/src/legacy/frontend/notification/notification.js deleted file mode 100644 index b79abba1356..00000000000 --- a/addons/web/static/src/legacy/frontend/notification/notification.js +++ /dev/null @@ -1,180 +0,0 @@ -odoo.define('web.Notification', function (require) { -'use strict'; - -var Widget = require('web.Widget'); - -/** - * Widget which is used to display a warning/information message on the top - * right of the screen. - * - * If you want to display such a notification, you probably do not want to do it - * by importing this file. The proper way is to use the displayNotification - * method on the Widget class. - */ -var Notification = Widget.extend({ - xmlDependencies: ['/web/static/src/legacy/frontend/notification/notification.xml'], - template: 'Notification', - events: { - 'hidden.bs.toast': '_onClose', - 'click .o_notification_buttons button': '_onClickButton', - 'mouseenter': '_onMouseEnter', - 'mouseleave': '_onMouseLeave', - }, - _autoCloseDelay: 4000, - _animation: true, - - /** - * @override - * @param {Widget} parent - * @param {Object} params - * @param {string} [params.title] - * @param {string} [params.subtitle] - * @param {string} [params.message] - * @param {DOMElement|jQuery} [params.contentEl] element to insert inside the - * notification, useful to keep a reference to the notification content - * @param {string} [params.type='warning'] 'info', 'success', 'warning', 'danger' or '' - * @param {boolean} [params.sticky=false] if true, the notification will - * stay visible until the user clicks on it. - * @param {string} [params.className] - * @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 - * @param {boolean} [params.messageIsHtml=false] allows passing an html - * message. Strongly discouraged: other options should be considered before - * enabling this option. The responsibility is on the caller to properly - * escape the message if this option is enabled. - */ - init: function (parent, params) { - this._super.apply(this, arguments); - this.title = params.title; - this.subtitle = params.subtitle; - this.contentEl = params.contentEl; - this.message = params.message || this.contentEl && true; - this.buttons = params.buttons || []; - this.messageIsHtml = !!params.messageIsHtml; - this.sticky = !!this.buttons.length || !!params.sticky; - this.type = params.type === undefined ? 'warning' : params.type; - this.className = params.className || ''; - this._closeCallback = params.onClose; - - if (this.type === 'danger') { - this.className += ' bg-danger'; - } else if (this.type === 'warning') { - this.className += ' bg-warning'; - } else if (this.type === 'success') { - this.className += ' bg-success'; - } else if (this.type === 'info') { - this.className += ' bg-info'; - } - }, - /** - * @override - */ - start: function () { - if (this.contentEl) { - this.$('.o_notification_content').empty().append(this.contentEl); - } - this.$el.toast({ - animation: this._animation, - autohide: false, - }); - void this.$el[0].offsetWidth; // Force a paint refresh before showing the toast - if (!this.sticky) { - this.autohide = _.cancellableThrottleRemoveMeSoon(this.close, this._autoCloseDelay, {leading: false}); - this.$el.on('shown.bs.toast', () => { - this.autohide(); - }); - } - this.$el.toast('show'); - return this._super.apply(this, arguments); - }, - /** - * @override - */ - destroy: function () { - this.$el.toast('dispose'); - this._super.apply(this, arguments); - }, - - //-------------------------------------------------------------------------- - // Public - //-------------------------------------------------------------------------- - - /** - * Destroys the widget with a nice animation. - * - * @private - * @param {boolean} [silent=false] if true, the notification does not call - * _closeCallback method - */ - close: function (silent) { - this.silent = silent; - this.$el.toast('hide'); - - // Make 'close' work if the notification is not shown yet but will be. - // Should not be needed but the calendar notification system is an - // example of feature that does not work without this yet. - var self = this; - this.$el.one('shown.bs.toast', function () { - self.$el.toast('hide'); - }); - }, - - //-------------------------------------------------------------------------- - // 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 {Event} ev - */ - _onClose: function (ev) { - this.trigger_up('close'); - if (!this.silent && !this._buttonClicked) { - if (this._closeCallback) { - this._closeCallback(); - } - } - this.destroy(); - }, - /** - * @private - */ - _onMouseEnter: function () { - if (!this.sticky) { - this.autohide.cancel(); - } - }, - /** - * @private - */ - _onMouseLeave: function () { - if (!this.sticky) { - this.autohide(); - } - }, -}); - -return Notification; -}); diff --git a/addons/web/static/src/legacy/frontend/notification/notification.scss b/addons/web/static/src/legacy/frontend/notification/notification.scss deleted file mode 100644 index 83007fb653b..00000000000 --- a/addons/web/static/src/legacy/frontend/notification/notification.scss +++ /dev/null @@ -1,25 +0,0 @@ - -.o_notification_manager { - @include o-position-absolute(3.5rem, 0); - position: fixed; - z-index: ($zindex-modal + $zindex-popover) / 2; - width: $toast-max-width; - margin-right: 8px; - max-width: 100%; - - .toast:not(:last-child) { - margin-bottom: 5px; - } - .o_notification { - width: 100%; - - .toast-body { - padding: 0.75rem; - - .o_notification_close { - height: 20px; - margin-left: 1rem; - } - } - } -} diff --git a/addons/web/static/src/legacy/frontend/notification/notification.xml b/addons/web/static/src/legacy/frontend/notification/notification.xml deleted file mode 100644 index af44d65cef1..00000000000 --- a/addons/web/static/src/legacy/frontend/notification/notification.xml +++ /dev/null @@ -1,32 +0,0 @@ - - - - -
-
- -
- -
-
- -
-
-
- - - diff --git a/addons/web/static/src/legacy/frontend/notification/notification_service.js b/addons/web/static/src/legacy/frontend/notification/notification_service.js deleted file mode 100644 index c84678e2a72..00000000000 --- a/addons/web/static/src/legacy/frontend/notification/notification_service.js +++ /dev/null @@ -1,122 +0,0 @@ -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 displayNotification - * method on the Widget class. - */ -var NotificationService = AbstractService.extend({ - custom_events: { - close: '_onCloseNotification', - }, - - /** - * @override - */ - start: 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 warning the user about some - * condition (connection lost), but the condition does not apply anymore. - * - * @param {number} notificationId - * @param {boolean} [silent=false] if true, the notification does not call - * onClose callback - * @param {integer} [wait=0] seconds to wait before closing the notification - */ - close: function (notificationId, silent, wait = 0) { - var notification = this.notifications[notificationId]; - if (!notification) { - return; - } - if (wait) { - setTimeout(() => { - notification.close(silent); - }, wait); - } else { - 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 {function} [params.Notification] javascript class of a notification - * to instantiate by default use 'web.Notification' - * @param {string} params.title notification title - * @param {string} params.subtitle notification subtitle - * @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 - * @param {boolean} [params.messageIsHtml=false] allows passing an html - * message. Strongly discouraged: other options should be considered before - * enabling this option. The responsibility is on the caller to properly - * escape the message if this option is enabled. - * @returns {Number} notification id - */ - notify: function (params) { - if (!this.$el) { - this.$el = $('
'); - this.$el.prependTo('body'); - } - var NotificationWidget = params.Notification || Notification; - var notification = this.notifications[++id] = new NotificationWidget(this, params); - 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; -}); diff --git a/addons/web/static/tests/legacy/frontend/notification_service_tests.js b/addons/web/static/tests/legacy/frontend/notification_service_tests.js deleted file mode 100644 index 6cadb7f82bb..00000000000 --- a/addons/web/static/tests/legacy/frontend/notification_service_tests.js +++ /dev/null @@ -1,289 +0,0 @@ -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; - -var waitCloseNotification = function () { - return new Promise(function (resolve) { - setTimeout(resolve, 1); - }); -} - -QUnit.module('Services', { - beforeEach: function () { - // We need to use a delay above 0 ms because otherwise the notification will close right after it opens - // before we can perform any test. - testUtils.mock.patch(Notification, { - _autoCloseDelay: 1, - _animation: false, - }); - this.viewParams = { - View: AbstractView, - arch: '', - data: { - fake_model: { - fields: {}, - record: [], - }, - }, - model: 'fake_model', - services: { - notification: NotificationService, - }, - }; - }, - afterEach: function () { - // The Notification Service has a side effect: it adds a div inside - // document.body. We could implement a cleanup mechanism for services, - // but this seems a little overkill since services are not supposed to - // be destroyed anyway. - $('.o_notification_manager').remove(); - testUtils.mock.unpatch(Notification); - } -}, function () { - QUnit.module('Notification'); - - QUnit.test('Display a warning notification', async function (assert) { - assert.expect(4); - - var view = await createView(this.viewParams); - view.call('notification', 'notify', { - title: 'a', - message: 'b', - }); - await testUtils.nextMicrotaskTick(); - var $notification = $('body .o_notification_manager .o_notification'); - assert.strictEqual($notification.html().trim().replace(/\s+/g, ' '), - "
a
b
", - "should display notification"); - assert.containsOnce($notification, '.o_notification_close'); - await waitCloseNotification(); - 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(); - }); - - QUnit.test('Display a danger notification', async function (assert) { - assert.expect(1); - - var view = await createView(this.viewParams); - view.call('notification', 'notify', { - title: 'a', - message: 'b', - type: 'danger' - }); - await testUtils.nextMicrotaskTick(); - var $notification = $('body .o_notification_manager .o_notification'); - assert.strictEqual($notification.html().trim().replace(/\s+/g, ' '), - "
a
b
", - "should display notification"); - view.destroy(); - }); - - QUnit.test('Display a sticky notification', async function (assert) { - assert.expect(3); - - var view = await createView(this.viewParams); - view.call('notification', 'notify', { - title: 'a', - message: 'b', - sticky: true, - }); - await testUtils.nextTick(); - var $notification = $('body .o_notification_manager .o_notification'); - assert.containsOnce($notification, '.o_notification_close', "should display the close button in notification"); - - assert.strictEqual($notification.is(':hidden'), false, "should not hide the notification automatically"); - await testUtils.dom.click($notification.find('.o_notification_close')); - assert.strictEqual($('body .o_notification_manager .o_notification').length, - 0, "should destroy the notification"); - view.destroy(); - }); - - QUnit.test('Display a notification without title', async function (assert) { - assert.expect(3); - - const view = await createView(this.viewParams); - view.call('notification', 'notify', { - title: false, - message: 'b', - sticky: true, - }); - await testUtils.nextTick(); - const $notification = $('body .o_notification_manager .o_notification'); - assert.containsNone($notification, '.toast-header .o_notification_title'); - assert.containsOnce($notification, '.o_notification_content'); - assert.containsOnce($notification, '.toast-body .o_notification_close'); - - view.destroy(); - }); - - // FIXME skip because the feature is unused and do not understand why the test even worked before - QUnit.skip('Display a simple notification with onClose callback when automatically close', async function (assert) { - assert.expect(2); - - var close = 0; - var view = await createView(this.viewParams); - view.call('notification', 'notify', { - title: 'a', - message: 'b', - onClose: function () { - close++; - } - }); - await testUtils.nextMicrotaskTick(); - view.destroy(); - assert.strictEqual(close, 0, "should wait to call onClose method once"); - await testUtils.nextTick(); - assert.strictEqual(close, 1, "should call onClose method once"); - }); - - QUnit.test('Display a sticky notification with onClose callback', async function (assert) { - assert.expect(2); - - testUtils.mock.unpatch(Notification); - testUtils.mock.patch(Notification, { - _autoCloseDelay: 2500, - _animation: false, - }); - var view = await createView(this.viewParams); - - var close = 0; - view.call('notification', 'notify', { - title: 'a', - message: 'b', - sticky: true, - onClose: function () { - close++; - } - }); - await testUtils.nextMicrotaskTick(); - assert.strictEqual(close, 0, "should wait to call onClose method once"); - testUtils.dom.click($('body .o_notification_manager .o_notification .o_notification_close')); - assert.strictEqual(close, 1, "should call onClose method once"); - view.destroy(); - }); - - QUnit.test('Display a question', async function (assert) { - assert.expect(8); - - var view = await 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)); - await testUtils.nextTick(); - - var $notification = $('body .o_notification_manager .o_notification'); - assert.containsOnce($notification.eq(0), '.o_notification_close', - "should display the close button in notification"); - assert.strictEqual($notification.html().trim().replace(/\s+/g, ' '), - "
a0
b0
", - "should display notification"); - - testUtils.dom.click($notification.find('.o_notification_buttons button:contains(accept0)')); - testUtils.dom.click($notification.find('.o_notification_buttons button:contains(refuse1)')); - testUtils.dom.click($notification.eq(2).find('.o_notification_close')); - - 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(); - }); - - QUnit.test('call close notification service', async function (assert) { - assert.expect(2); - - testUtils.mock.unpatch(Notification); - testUtils.mock.patch(Notification, { - _autoCloseDelay: 2500, - _animation: false, - }); - var view = await 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++; - } - }); - await testUtils.nextTick(); - - view.call('notification', 'close', notificationId0); - view.call('notification', 'close', notificationId1); - await testUtils.nextTick(); - - 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(); - }); - - QUnit.test('Display a custom notification', async function (assert) { - assert.expect(3); - - var Custom = Notification.extend({ - init: function (parent, params) { - this._super.apply(this, arguments); - assert.ok(params.customParams, 'instantiate custom notification'); - }, - start: function () { - var self = this; - return this._super().then(function () { - self.$el.html('Custom'); - }); - }, - }); - - var view = await createView(this.viewParams); - view.call('notification', 'notify', { - Notification: Custom, - customParams: true, - }); - await testUtils.nextMicrotaskTick(); - assert.containsOnce($('body'), '.o_notification_manager .o_notification:contains(Custom)', - "should display the notification"); - view.destroy(); - assert.containsNone($('body'), '.o_notification_manager .o_notification', - "should destroy the notification"); - }); - -});}); diff --git a/addons/web/static/tests/legacy/helpers/test_utils.js b/addons/web/static/tests/legacy/helpers/test_utils.js index 06837d007ef..e8b5d23250c 100644 --- a/addons/web/static/tests/legacy/helpers/test_utils.js +++ b/addons/web/static/tests/legacy/helpers/test_utils.js @@ -126,7 +126,6 @@ odoo.define('web.test_utils', async function (require) { ajax.loadXML('/web/static/src/legacy/frontend/debug_manager/debug.xml', core.qweb), ajax.loadXML('/web/static/src/legacy/xml/dialog.xml', core.qweb), ajax.loadXML('/web/static/src/legacy/xml/translation_dialog.xml', core.qweb), - ajax.loadXML('/web/static/src/legacy/frontend/notification/notification.xml', core.qweb), ]); setTimeout(function () { // jquery autocomplete refines the search in a setTimeout() parameterized