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, ' '),
- "",
- "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, ' '),
- "",
- "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, ' '),
- "",
- "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