diff --git a/addons/mail/static/src/js/field_char.js b/addons/mail/static/src/js/field_char.js deleted file mode 100644 index 2383f2d26bc..00000000000 --- a/addons/mail/static/src/js/field_char.js +++ /dev/null @@ -1,61 +0,0 @@ -/** @odoo-module **/ - -import { debounce } from "@web/core/utils/timing"; -import { FieldChar } from "web.basic_fields"; - -FieldChar.include({ - //-------------------------------------------------------------------------- - // Public - //------------------------------------------------------------------------- - - /** - * Support a key-based onchange in text field. - * The _triggerOnchange method is debounced to run after given debouce delay - * (or 2 seconds by default) on typing ends. - * - */ - init: function () { - this._super.apply(this, arguments); - if (this.nodeOptions.keydown_debounce_delay === undefined) { - this.nodeOptions.keydown_debounce_delay = 2000; - } - this._triggerOnchange = debounce( - this._triggerOnchange, - this.nodeOptions.keydown_debounce_delay - ); - }, - - //-------------------------------------------------------------------------- - // Handlers - //-------------------------------------------------------------------------- - - /** - * Trigger the 'change' event at key down. It allows to trigger an onchange - * while typing which may be interesting in some cases. Otherwise onchange - * is triggered only on blur. - * - * @override - * @private - */ - _onKeydown: function () { - this._super.apply(this, arguments); - if (this.nodeOptions.onchange_on_keydown) { - this._triggerOnchange(); - } - }, - - //-------------------------------------------------------------------------- - // Private - //-------------------------------------------------------------------------- - - /** - * Triggers the 'change' event to refresh the value. - * This method is debounced to run after given debouce delay on typing ends. - * (to avoid spamming the server while the user is typing their message) - * - * @private - */ - _triggerOnchange: function () { - this.$input.trigger("change"); - }, -}); diff --git a/addons/mail/static/src/js/onchange_on_keydown.js b/addons/mail/static/src/js/onchange_on_keydown.js new file mode 100644 index 00000000000..a0dc6b94219 --- /dev/null +++ b/addons/mail/static/src/js/onchange_on_keydown.js @@ -0,0 +1,68 @@ +/** @odoo-module **/ + +import { patch } from "@web/core/utils/patch"; +import { debounce } from "@web/core/utils/timing"; +import { CharField } from "@web/views/fields/char/char_field"; +import { TextField } from '@web/views/fields/text/text_field'; +import { archParseBoolean } from "@web/views/utils"; + +const { useEffect } = owl; + +/** + * Support a key-based onchange in text fields. + * The triggerOnChange method is debounced to run after given debounce delay + * (or 2 seconds by default) when typing ends. + * + */ +const onchangeOnKeydownMixin = { + setup() { + this._super(...arguments); + + if (this.props.onchangeOnKeydown) { + const input = this.input || this.textareaRef; + + const triggerOnChange = debounce(this.triggerOnChange, this.props.keydownDebounceDelay); + useEffect(() => { + if (input.el) { + input.el.addEventListener('keydown', triggerOnChange.bind(this)); + } + }); + } + }, + + triggerOnChange() { + const input = this.input || this.textareaRef; + input.el.dispatchEvent(new Event('change')); + } +}; + +patch(CharField.prototype, 'char_field_onchange_on_keydown', onchangeOnKeydownMixin); +patch(TextField.prototype, 'text_field_onchange_on_keydown', onchangeOnKeydownMixin); + +CharField.props = { + ...CharField.props, + onchangeOnKeydown: { type: Boolean, optional: true }, + keydownDebounceDelay: { type: Number, optional: true }, +}; + +TextField.props = { + ...TextField.props, + onchangeOnKeydown: { type: Boolean, optional: true }, + keydownDebounceDelay: { type: Number, optional: true }, +}; + +const charExtractProps = CharField.extractProps; +CharField.extractProps = ({ attrs, field }) => { + return Object.assign(charExtractProps({ attrs, field }), { + onchangeOnKeydown: archParseBoolean(attrs.onchange_on_keydown), + keydownDebounceDelay: attrs.keydown_debounce_delay ? Number(attrs.keydown_debounce_delay) : 2000, + }); +}; + +const textExtractProps = TextField.extractProps; +TextField.extractProps = ({ attrs, field }) => { + return Object.assign(textExtractProps({ attrs, field }), { + onchangeOnKeydown: archParseBoolean(attrs.onchange_on_keydown), + keydownDebounceDelay: attrs.keydown_debounce_delay ? Number(attrs.keydown_debounce_delay) : 2000, + }); +}; diff --git a/addons/mail/static/tests/qunit_suite_tests/onchange_on_keydown_tests.js b/addons/mail/static/tests/qunit_suite_tests/onchange_on_keydown_tests.js new file mode 100644 index 00000000000..e8d6f999e31 --- /dev/null +++ b/addons/mail/static/tests/qunit_suite_tests/onchange_on_keydown_tests.js @@ -0,0 +1,70 @@ +/** @odoo-module **/ + +import { getFixture, nextTick } from "@web/../tests/helpers/utils"; +import { makeView, setupViewRegistries } from "@web/../tests/views/helpers" +import { dom } from 'web.test_utils'; + +let target; +let serverData; + +QUnit.module('mail', {}, function () { + QUnit.module('onchange on keydown', { + async beforeEach() { + target = getFixture(); + serverData = { + models: { + 'res.partner': { + fields: { + id: {type: 'integer'}, + description: {type: 'text'}, + }, + records: [{ + id: 1, + description: '', + }], + onchanges: { + description: () => {}, + }, + } + } + }; + setupViewRegistries(); + } + }, function () { + QUnit.test('Test that onchange_on_keydown option triggers the onchange properly', async function (assert) { + assert.expect(3); + + await makeView({ + type: "form", + resModel: 'res.partner', + serverData, + arch: '
', + mockRPC(route, params) { + if (params.method === 'onchange') { + // the onchange will be called twice: at record creation & when keydown is detected + // the second call should have our description value completed. + assert.ok(true); + if (params.args[1] && params.args[1].description === 'testing the keydown event') { + assert.ok(true); + } + return {}; + } + } + }); + + const textarea = target.querySelector('textarea[id="description"]'); + await dom.click(textarea); + for (let key of 'testing the keydown event') { + // trigger each key separately to simulate a user typing + textarea.value = textarea.value + key; + await dom.triggerEvent(textarea, 'input', { key: key }); + }; + + // only trigger the keydown when typing ends to avoid getting a lot of onchange since the + // delay is set to 0 for test purposes + // for real use cases there will be a debounce delay set to avoid spamming the event + await dom.triggerEvent(textarea, 'keydown'); + await nextTick(); + }); + }); +}); diff --git a/addons/sms/wizard/sms_composer_views.xml b/addons/sms/wizard/sms_composer_views.xml index 17e9d2ac25e..e76cb0f8918 100644 --- a/addons/sms/wizard/sms_composer_views.xml +++ b/addons/sms/wizard/sms_composer_views.xml @@ -43,7 +43,7 @@ attrs="{'invisible': [('comment_single_recipient', '=', False)]}"/>