diff options
| author | Xavier Dubuc <xdu@odoo.com> | 2020-08-20 13:31:23 +0000 |
|---|---|---|
| committer | Alexandre Kühn <aku@odoo.com> | 2020-09-03 14:34:28 +0000 |
| commit | 586496abcf802794bb9eaa70cae3654644e01f86 (patch) | |
| tree | 1fe148ecee7a5611a0111cad9ccfb4398075f306 /addons/mail/static/src/components/composer | |
| parent | 09d5386a373c5a37aa2273188b1038deeef79648 (diff) | |
[FIX] mail: allow composer text input to send message with ctrl+enter
Also allow meta+enter
task-2296863
closes odoo/odoo#57045
X-original-commit: 6bad3666be3d3631eaadae409d0cabcdd02661a8
Signed-off-by: Sébastien Theys (seb) <seb@odoo.com>
Diffstat (limited to 'addons/mail/static/src/components/composer')
3 files changed, 138 insertions, 19 deletions
diff --git a/addons/mail/static/src/components/composer/composer.js b/addons/mail/static/src/components/composer/composer.js index 0b23ee4ec22..510a5ac6038 100644 --- a/addons/mail/static/src/components/composer/composer.js +++ b/addons/mail/static/src/components/composer/composer.js @@ -262,6 +262,13 @@ class Composer extends Component { } /** + * @private + */ + _onComposerTextInputSendShortcut() { + this._postMessage(); + } + + /** * Called when some files have been dropped in the dropzone. * * @private @@ -340,13 +347,6 @@ class Composer extends Component { await this._fileUploaderRef.comp.uploadFiles(ev.clipboardData.files); } - /** - * @private - */ - _onTextInputKeydownEnter() { - this._postMessage(); - } - } Object.assign(Composer, { @@ -357,7 +357,6 @@ Object.assign(Composer, { hasDiscardButton: false, hasFollowers: false, hasSendButton: true, - hasTextInputSendOnEnterEnabled: true, hasThreadName: false, hasThreadTyping: false, isCompact: true, @@ -381,7 +380,6 @@ Object.assign(Composer, { optional: true, }, hasSendButton: Boolean, - hasTextInputSendOnEnterEnabled: Boolean, hasThreadName: Boolean, hasThreadTyping: Boolean, showAttachmentsExtensions: { @@ -403,6 +401,15 @@ Object.assign(Composer, { }, isCompact: Boolean, isExpandable: Boolean, + /** + * If set, keyboard shortcuts from text input to send message. + * If not set, will use default values from `ComposerTextInput`. + */ + textInputSendShortcuts: { + type: Array, + element: String, + optional: true, + }, }, template: 'mail.Composer', }); diff --git a/addons/mail/static/src/components/composer/composer.xml b/addons/mail/static/src/components/composer/composer.xml index 30053cdd0f8..61694e5aaf1 100644 --- a/addons/mail/static/src/components/composer/composer.xml +++ b/addons/mail/static/src/components/composer/composer.xml @@ -75,9 +75,9 @@ }" composerLocalId="composer.localId" hasMentionSuggestionsBelowPosition="props.hasMentionSuggestionsBelowPosition" - hasSendOnEnterEnabled="props.hasTextInputSendOnEnterEnabled" isCompact="props.isCompact" - t-on-o-keydown-enter="_onTextInputKeydownEnter" + sendShortcuts="props.textInputSendShortcuts" + t-on-o-composer-text-input-send-shortcut="_onComposerTextInputSendShortcut" t-on-paste="_onPasteTextInput" t-ref="textInput" /> diff --git a/addons/mail/static/src/components/composer/composer_tests.js b/addons/mail/static/src/components/composer/composer_tests.js index 8e07affcccb..5d337e0ed0c 100644 --- a/addons/mail/static/src/components/composer/composer_tests.js +++ b/addons/mail/static/src/components/composer/composer_tests.js @@ -590,6 +590,116 @@ QUnit.test('composer: paste attachments', async function (assert) { ); }); +QUnit.test('send message when enter is pressed while holding ctrl key (this shortcut is available)', async function (assert) { + // Note that test doesn't assert ENTER makes no newline, because this + // default browser cannot be simulated with just dispatching + // programmatically crafted events... + assert.expect(5); + + this.data['mail.channel'].records.push({ id: 20 }); + await this.start({ + async mockRPC(route, args) { + if (args.method === 'message_post') { + assert.step('message_post'); + } + return this._super(...arguments); + }, + }); + const thread = this.env.models['mail.thread'].find(thread => + thread.id === 20 && + thread.model === 'mail.channel' + ); + await this.createComposerComponent(thread.composer, { + textInputSendShortcuts: ['ctrl-enter'], + }); + // Type message + document.querySelector(`.o_ComposerTextInput_textarea`).focus(); + document.execCommand('insertText', false, "test message"); + assert.strictEqual( + document.querySelector(`.o_ComposerTextInput_textarea`).value, + "test message", + "should have inserted text content in editable" + ); + + await afterNextRender(() => { + const enterEvent = new window.KeyboardEvent('keydown', { key: 'Enter' }); + document.querySelector(`.o_ComposerTextInput_textarea`) + .dispatchEvent(enterEvent); + }); + assert.strictEqual( + document.querySelector(`.o_ComposerTextInput_textarea`).value, + "test message", + "should have inserted text content in editable as message has not been posted" + ); + + // Send message with ctrl+enter + await afterNextRender(() => + document.querySelector(`.o_ComposerTextInput_textarea`) + .dispatchEvent(new window.KeyboardEvent('keydown', { ctrlKey: true, key: 'Enter' })) + ); + assert.verifySteps(['message_post']); + assert.strictEqual( + document.querySelector(`.o_ComposerTextInput_textarea`).value, + "", + "should have no content in composer input as message has been posted" + ); +}); + +QUnit.test('send message when enter is pressed while holding meta key (this shortcut is available)', async function (assert) { + // Note that test doesn't assert ENTER makes no newline, because this + // default browser cannot be simulated with just dispatching + // programmatically crafted events... + assert.expect(5); + + this.data['mail.channel'].records.push({ id: 20 }); + await this.start({ + async mockRPC(route, args) { + if (args.method === 'message_post') { + assert.step('message_post'); + } + return this._super(...arguments); + }, + }); + const thread = this.env.models['mail.thread'].find(thread => + thread.id === 20 && + thread.model === 'mail.channel' + ); + await this.createComposerComponent(thread.composer, { + textInputSendShortcuts: ['meta-enter'], + }); + // Type message + document.querySelector(`.o_ComposerTextInput_textarea`).focus(); + document.execCommand('insertText', false, "test message"); + assert.strictEqual( + document.querySelector(`.o_ComposerTextInput_textarea`).value, + "test message", + "should have inserted text content in editable" + ); + + await afterNextRender(() => { + const enterEvent = new window.KeyboardEvent('keydown', { key: 'Enter' }); + document.querySelector(`.o_ComposerTextInput_textarea`) + .dispatchEvent(enterEvent); + }); + assert.strictEqual( + document.querySelector(`.o_ComposerTextInput_textarea`).value, + "test message", + "should have inserted text content in editable as message has not been posted" + ); + + // Send message with meta+enter + await afterNextRender(() => + document.querySelector(`.o_ComposerTextInput_textarea`) + .dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter', metaKey: true })) + ); + assert.verifySteps(['message_post']); + assert.strictEqual( + document.querySelector(`.o_ComposerTextInput_textarea`).value, + "", + "should have no content in composer input as message has been posted" + ); +}); + QUnit.test('composer text input cleared on message post', async function (assert) { assert.expect(4); @@ -610,8 +720,10 @@ QUnit.test('composer text input cleared on message post', async function (assert ); await this.createComposerComponent(thread.composer); // Type message - document.querySelector(`.o_ComposerTextInput_textarea`).focus(); - document.execCommand('insertText', false, "test message"); + await afterNextRender(() => { + document.querySelector(`.o_ComposerTextInput_textarea`).focus(); + document.execCommand('insertText', false, "test message"); + }); assert.strictEqual( document.querySelector(`.o_ComposerTextInput_textarea`).value, "test message", @@ -620,8 +732,7 @@ QUnit.test('composer text input cleared on message post', async function (assert // Send message await afterNextRender(() => - document.querySelector(`.o_ComposerTextInput_textarea`) - .dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' })) + document.querySelector('.o_Composer_buttonSend').click() ); assert.verifySteps(['message_post']); assert.strictEqual( @@ -691,8 +802,7 @@ QUnit.test('composer inputs cleared on message post in composer of a mailing cha // Send message await afterNextRender(() => - document.querySelector(`.o_ComposerTextInput_textarea`) - .dispatchEvent(new window.KeyboardEvent('keydown', { key: 'Enter' })) + document.querySelector('.o_Composer_buttonSend').click() ); assert.verifySteps(['message_post']); assert.strictEqual( @@ -956,7 +1066,7 @@ QUnit.test('composer: send button is disabled if attachment upload is not finish ); }); -QUnit.test('warning when attempting to post message when attachments are still uploading', async function (assert) { +QUnit.test('warning on send with shortcut when attempting to post message with still-uploading attachments', async function (assert) { assert.expect(7); await this.start({ @@ -991,7 +1101,9 @@ QUnit.test('warning when attempting to post message when attachments are still u id: 20, model: 'res.partner', }); - await this.createComposerComponent(thread.composer); + await this.createComposerComponent(thread.composer, { + textInputSendShortcuts: ['enter'], + }); const file = await createFile({ content: 'hello, world', contentType: 'text/plain', |
