From 7bc53ac30ecadbb1e94051edd29c701679cd6de0 Mon Sep 17 00:00:00 2001
From: Aaron Bohy <aab@odoo.com>
Date: Thu, 11 Apr 2019 13:35:49 +0000
Subject: [PATCH] [FIX] web: crash in editable lists with date field

Assume an editable list view with a date(time) widget, click to
edit a line, focus the date field (the datepicker opens), press
ESC: there is a crash in the lib, and the datepicker remains open
forever.

This fix is twofold:
 - we added a check in the lib to prevent it from crashing (it is
 easily reproducible: it crashes when an opened datepicker is
 removed from the DOM).
 - we added an event handler of ESC keydown event to hide the
 datepicker before the field is removed from the DOM.
---
 .../static/lib/tempusdominus/tempusdominus.js |  3 +-
 .../web/static/src/js/widgets/date_picker.js  | 12 ++++++
 addons/web/static/tests/views/list_tests.js   | 37 ++++++++++++++++++-
 3 files changed, 50 insertions(+), 2 deletions(-)

diff --git a/addons/web/static/lib/tempusdominus/tempusdominus.js b/addons/web/static/lib/tempusdominus/tempusdominus.js
index ffca0c8e6325..4eaf0df2a600 100644
--- a/addons/web/static/lib/tempusdominus/tempusdominus.js
+++ b/addons/web/static/lib/tempusdominus/tempusdominus.js
@@ -2754,7 +2754,8 @@ var TempusDominusBootstrap4 = function ($) {
         if ($target.length === 0) {
             return;
         }
-        if (config._options.debug || window.debug) {
+        // /!\ ODOO FIX: check on 'config' existence added by odoo
+        if (config && config._options.debug || window.debug) {
             return;
         }
         TempusDominusBootstrap4._jQueryInterface.call($target, 'hide', event);
diff --git a/addons/web/static/src/js/widgets/date_picker.js b/addons/web/static/src/js/widgets/date_picker.js
index 367ab23178d7..57c935905e5e 100644
--- a/addons/web/static/src/js/widgets/date_picker.js
+++ b/addons/web/static/src/js/widgets/date_picker.js
@@ -15,6 +15,7 @@ var DateWidget = Widget.extend({
         'change.datetimepicker': 'changeDatetime',
         'change .o_datepicker_input': 'changeDatetime',
         'input input': '_onInput',
+        'keydown': '_onKeydown',
         'show.datetimepicker': '_onDateTimePickerShow',
     },
     /**
@@ -239,6 +240,17 @@ var DateWidget = Widget.extend({
             this.$input.select();
         }
     },
+    /**
+     * @private
+     * @param {KeyEvent} ev
+     */
+    _onKeydown: function (ev) {
+        if (ev.which === $.ui.keyCode.ESCAPE) {
+            this.__libInput++;
+            this.$el.datetimepicker('hide');
+            this.__libInput--;
+        }
+    },
     /**
      * Prevents 'input' events triggered by the library to bubble up, as they
      * might have unwanted effects (like triggering 'field_changed' events in
diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js
index c81a4e48db2b..5a8e19b8fb73 100644
--- a/addons/web/static/tests/views/list_tests.js
+++ b/addons/web/static/tests/views/list_tests.js
@@ -298,7 +298,42 @@ QUnit.module('Views', {
         list.destroy();
     });
 
-    QUnit.test('editable list datetimepicker destroy widget', async function (assert) {
+    QUnit.test('editable list datetimepicker destroy widget (edition)', async function (assert) {
+        assert.expect(6);
+        var eventPromise = testUtils.makeTestPromise();
+
+        var list = await createView({
+            View: ListView,
+            model: 'foo',
+            data: this.data,
+            arch: '<tree editable="top">' +
+                    '<field name="date"/>' +
+                '</tree>',
+        });
+        list.$el.on({
+            'show.datetimepicker': async function () {
+                assert.containsOnce(list, '.o_selected_row');
+                assert.containsOnce($('body'), '.bootstrap-datetimepicker-widget');
+
+                await testUtils.fields.triggerKeydown(list.$('.o_datepicker_input'), 'escape');
+
+                assert.containsNone(list, '.o_selected_row');
+                assert.containsNone($('body'), '.bootstrap-datetimepicker-widget');
+
+                eventPromise.resolve();
+            }
+        });
+
+        assert.containsN(list, '.o_data_row', 4);
+        assert.containsNone(list, '.o_selected_row');
+
+        await testUtils.dom.click(list.$('.o_data_cell:first'));
+
+        await eventPromise;
+        list.destroy();
+    });
+
+    QUnit.test('editable list datetimepicker destroy widget (new line)', async function (assert) {
         assert.expect(7);
         var eventPromise = testUtils.makeTestPromise();
 
-- 
GitLab