diff --git a/addons/mail/static/src/js/activity.js b/addons/mail/static/src/js/activity.js index 0b9cfbff599..fd3b13391ac 100644 --- a/addons/mail/static/src/js/activity.js +++ b/addons/mail/static/src/js/activity.js @@ -377,6 +377,7 @@ var BasicActivity = AbstractField.extend({ this.$('.o_activity_selected').removeClass('o_activity_selected'); $actLi.toggleClass('o_activity_selected'); $panel.collapse('toggle'); + $panel.find('#activity_feedback').focus(); } else if (!$markDoneBtn.data('bs.popover')) { $markDoneBtn.popover({ @@ -418,6 +419,11 @@ var BasicActivity = AbstractField.extend({ _onMarkActivityDoneActions: function ($btn, $form, activityID) { var self = this; $form.find('#activity_feedback').val(self._draftFeedback[activityID]); + $form.on('keydown', '#activity_feedback', function (ev) { + if (ev.key === 'Enter') { + ev.stopPropagation(); // Prevent list view actions + } + }); $form.on('click', '.o_activity_popover_done', function (ev) { ev.stopPropagation(); self._markActivityDone({ @@ -425,6 +431,16 @@ var BasicActivity = AbstractField.extend({ feedback: $form.find('#activity_feedback').val(), }); }); + $form.on('keydown', '.o_activity_popover_done', function (ev) { + if (ev.key === 'Enter') { + ev.stopPropagation(); // Prevent list view actions + ev.preventDefault(); + self._markActivityDone({ + activityID, + feedback: $form.find('#activity_feedback').val(), + }); + } + }); $form.on('click', '.o_activity_popover_done_next', function (ev) { ev.stopPropagation(); self._markActivityDoneAndScheduleNext({ @@ -432,6 +448,16 @@ var BasicActivity = AbstractField.extend({ feedback: $form.find('#activity_feedback').val(), }); }); + $form.on('keydown', '.o_activity_popover_done_next', function (ev) { + if (ev.key === 'Enter') { + ev.stopPropagation(); // Prevent list view actions + ev.preventDefault(); + self._markActivityDoneAndScheduleNext({ + activityID, + feedback: $form.find('#activity_feedback').val(), + }); + } + }); $form.on('click', '.o_activity_popover_discard', function (ev) { ev.stopPropagation(); if ($btn.data('bs.popover')) { diff --git a/addons/mail/static/tests/helpers/mock_server.js b/addons/mail/static/tests/helpers/mock_server.js index 58302ad8a60..72b78722633 100644 --- a/addons/mail/static/tests/helpers/mock_server.js +++ b/addons/mail/static/tests/helpers/mock_server.js @@ -154,6 +154,14 @@ MockServer.include({ return; } // mail.activity methods + if (args.model === 'mail.activity' && args.method === 'action_feedback') { + const ids = args.args[0]; + return this._mockMailActivityActionFeedback(ids); + } + if (args.model === 'mail.activity' && args.method === 'action_feedback_schedule_next') { + const ids = args.args[0]; + return this._mockMailActivityActionFeedbackScheduleNext(ids); + } if (args.model === 'mail.activity' && args.method === 'activity_format') { const ids = args.args[0]; return this._mockMailActivityActivityFormat(ids); @@ -635,6 +643,47 @@ MockServer.include({ return res; }); }, + /** + * Simulates `_action_done` on `mail.activity`. + * + * @private + * @param {string} model + * @param {integer[]} ids + * @returns {Object} + */ + _mockMailActivityActionDone(ids) { + const activities = this.getRecords('mail.activity', [['id', 'in', ids]]); + this._mockUnlink('mail.activity', [activities.map(activity => activity.id)]); + }, + /** + * Simulates `action_feedback` on `mail.activity`. + * + * @private + * @param {string} model + * @param {integer[]} ids + * @returns {Object} + */ + _mockMailActivityActionFeedback(ids) { + this._mockMailActivityActionDone(ids); + }, + /** + * Simulates `action_feedback_schedule_next` on `mail.activity`. + * + * @private + * @param {string} model + * @param {integer[]} ids + * @returns {Object} + */ + _mockMailActivityActionFeedbackScheduleNext(ids) { + this._mockMailActivityActionDone(ids); + return { + name: 'Schedule an Activity', + view_mode: 'form', + res_model: 'mail.activity', + views: [[false, 'form']], + type: 'ir.actions.act_window', + } + }, /** * Simulates `get_activity_data` on `mail.activity`. * diff --git a/addons/mail/static/tests/qunit_suite_tests/chatter_tests.js b/addons/mail/static/tests/qunit_suite_tests/chatter_tests.js index 3f320f48774..5ed2fc831bc 100644 --- a/addons/mail/static/tests/qunit_suite_tests/chatter_tests.js +++ b/addons/mail/static/tests/qunit_suite_tests/chatter_tests.js @@ -252,6 +252,100 @@ QUnit.test('list activity exception widget with activity', async function (asser "there is an exception on a record"); }); +QUnit.test('list activity widget: done the activity with "ENTER" keyboard shortcut', async function (assert) { + assert.expect(1); + + const pyEnv = await startServer(); + const mailActivityTypeId1 = pyEnv['mail.activity.type'].create(); + const mailActivityId1 = pyEnv['mail.activity'].create([ + { + display_name: "Call with Al", + date_deadline: moment().format("YYYY-MM-DD"), // now + can_write: true, + state: "today", + user_id: pyEnv.currentUserId, + create_uid: pyEnv.currentUserId, + activity_type_id: mailActivityTypeId1, + } + ]); + pyEnv['res.users'].write([pyEnv.currentUserId], { + activity_ids: [mailActivityId1], + activity_state: 'today', + activity_summary: 'Call with Al', + activity_type_id: mailActivityTypeId1, + }); + + const { target: list } = await start({ + hasView: true, + View: ListView, + model: 'res.users', + arch: ` + + + `, + }); + + await testUtils.dom.click(list.querySelector('.o_activity_btn span')); + await testUtils.dom.click(list.querySelector('.o_mark_as_done')); + pyEnv['res.users'].write([pyEnv.currentUserId], { + activity_state: '', + }); + await testUtils.dom.triggerEvent( + document.querySelector('.o_activity_popover_done'), + 'keydown', + { key: 'Enter' }, + ); + + assert.containsOnce(list, '.o_mail_activity .o_activity_color_default'); +}); + +QUnit.test('list activity widget: done and schedule the next activity with "ENTER" keyboard shortcut', async function (assert) { + assert.expect(1); + + const pyEnv = await startServer(); + const mailActivityTypeId1 = pyEnv['mail.activity.type'].create(); + const mailActivityId1 = pyEnv['mail.activity'].create([ + { + display_name: "Call with Al", + date_deadline: moment().format("YYYY-MM-DD"), // now + can_write: true, + state: "today", + user_id: pyEnv.currentUserId, + create_uid: pyEnv.currentUserId, + activity_type_id: mailActivityTypeId1, + } + ]); + pyEnv['res.users'].write([pyEnv.currentUserId], { + activity_ids: [mailActivityId1], + activity_state: 'today', + activity_summary: 'Call with Al', + activity_type_id: mailActivityTypeId1, + }); + + const { target: list } = await start({ + hasView: true, + View: ListView, + model: 'res.users', + arch: ` + + + `, + intercepts: { + do_action: function (ev) { + assert.strictEqual(ev.data.action.name, "Schedule an Activity", 'should do a do_action with correct parameters'); + }, + }, + }); + + await testUtils.dom.click(list.querySelector('.o_activity_btn span')); + await testUtils.dom.click(list.querySelector('.o_mark_as_done')); + await testUtils.dom.triggerEvent( + document.querySelector('.o_activity_popover_done_next'), + 'keydown', + { key: 'Enter' }, + ); +}); + QUnit.module('FieldMany2ManyTagsEmail'); QUnit.test('fieldmany2many tags email (edition)', async function (assert) {