[FIX] mail,sms: restore "onchange_on_keydown" feature

Previously, we had an option on char/text fields called "onchange_on_keydown",
which allowed to trigger the onchange while the user was typing (instead of
when the field is blurred).

It was notably used when typing a phone number in the sms composer, to warn
the user that the phone number is invalid.

This feature was lost during the port to OWL, this commit restores it.

Task-3033107

closes odoo/odoo#106193

X-original-commit: 54def8969d49d6e5284cd97ab8df75e08043cd4e
Related: odoo/enterprise#34211
Signed-off-by: Thibault Delavallee (tde) <tde@openerp.com>
Signed-off-by: Warnon Aurélien (awa) <awa@odoo.com>
This commit is contained in:
Aurélien Warnon
2022-11-22 10:24:40 +01:00
parent 66bbb82705
commit 965132df6f
4 changed files with 139 additions and 62 deletions
-61
View File
@@ -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");
},
});
@@ -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,
});
};
@@ -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: '<form><field name="description" onchange_on_keydown="True" keydown_debounce_delay="0"/></form>',
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();
});
});
});
+1 -1
View File
@@ -43,7 +43,7 @@
attrs="{'invisible': [('comment_single_recipient', '=', False)]}"/>
<div attrs="{'invisible': [('comment_single_recipient', '=', False)]}">
<field name="recipient_single_description" class="oe_inline"/>
<field name="recipient_single_number_itf" class="oe_inline" nolabel="1" options="{'onchange_on_keydown': True}" placeholder="e.g. +1 415 555 0100"/>
<field name="recipient_single_number_itf" class="oe_inline" nolabel="1" onchange_on_keydown="True" placeholder="e.g. +1 415 555 0100"/>
</div>
<field name="body" widget="sms_widget" attrs="{'invisible': ['|', ('comment_single_recipient', '=', False), ('recipient_single_valid', '=', True)]}"/>
<field name="body" widget="sms_widget" attrs="{'invisible': [('comment_single_recipient', '=', True), ('recipient_single_valid', '=', False)]}" default_focus="1"/>