diff --git a/addons/mass_mailing_sms/views/mailing_mailing_views.xml b/addons/mass_mailing_sms/views/mailing_mailing_views.xml
index b4395824cee..b5cc0c20905 100644
--- a/addons/mass_mailing_sms/views/mailing_mailing_views.xml
+++ b/addons/mass_mailing_sms/views/mailing_mailing_views.xml
@@ -120,7 +120,7 @@
+ options="{'dynamic_placeholder': true}" enable_emojis="true"/>
diff --git a/addons/sms/static/src/components/sms_widget/fields_sms_widget.js b/addons/sms/static/src/components/sms_widget/fields_sms_widget.js
index 7bd29885547..fcb604c9880 100644
--- a/addons/sms/static/src/components/sms_widget/fields_sms_widget.js
+++ b/addons/sms/static/src/components/sms_widget/fields_sms_widget.js
@@ -1,39 +1,33 @@
/** @odoo-module **/
-import { _t } from 'web.core';
import basic_fields from 'web.basic_fields';
-import fieldRegistry from 'web.field_registry';
-import EmojisTextField from '@mail/views/fields/emojis_text_field';
+import { patch } from "@web/core/utils/patch";
+import { EmojisTextField} from '@mail/views/fields/emojis_text_field/emojis_text_field';
+import { useService } from "@web/core/utils/hooks";
+import { registry } from "@web/core/registry";
-var DynamicPlaceholderFieldMixin = basic_fields.DynamicPlaceholderFieldMixin;
+const DynamicPlaceholderFieldMixin = basic_fields.DynamicPlaceholderFieldMixin;
/**
* SmsWidget is a widget to display a textarea (the body) and a text representing
* the number of SMS and the number of characters. This text is computed every
* time the user changes the body.
*/
-var SmsWidget = EmojisTextField.extend(DynamicPlaceholderFieldMixin, {
- className: 'o_field_text',
- enableEmojis: false,
- /**
- * @constructor
- */
- init: function () {
- this._super.apply(this, arguments);
- this.nbrChar = 0;
- this.nbrSMS = 0;
- this.encoding = 'GSM7';
- this.enableEmojis = !!this.nodeOptions.enable_emojis;
- },
+export class SmsWidget extends EmojisTextField {
+ setup() {
+ super.setup();
+ this.notification = useService('notification');
+ }
- /**
- * @override
- *"This will add the emoji dropdown to a target field (controlled by the "enableEmojis" attribute)
- */
- on_attach_callback: function () {
- if (this.enableEmojis) {
- this._super.apply(this, arguments);
- }
- },
+ get encoding() {
+ return this._extractEncoding(this.props.value || '');
+ }
+ get nbrChar() {
+ const content = this.props.value || '';
+ return content.length + (content.match(/\n/g) || []).length;
+ }
+ get nbrSMS() {
+ return this._countSMS(this.nbrChar, this.encoding);
+ }
/**
* Open a Model Field Selector in order to select fields
@@ -45,67 +39,35 @@ var SmsWidget = EmojisTextField.extend(DynamicPlaceholderFieldMixin, {
* @param {Array} chain
*
*/
- openDynamicPlaceholder: async function (baseModel, chain = []) {
+ async openDynamicPlaceholder(baseModel, chain = []) {
const modelSelector = await this._openNewModelSelector(baseModel, chain);
modelSelector.$el.css('margin-top', 4);
- },
-
- //--------------------------------------------------------------------------
- // Private: override widget
- //--------------------------------------------------------------------------
-
- /**
- * @private
- * @override
- */
- _renderEdit: function () {
- var def = this._super.apply(this, arguments);
-
- this._compute();
- var $sms_container = $('');
- $sms_container.append(this._renderSMSInfo());
- $sms_container.append(this._renderIAPButton());
- this.$el = this.$el.add($sms_container);
-
- return def;
- },
+ }
//--------------------------------------------------------------------------
// Private: SMS
//--------------------------------------------------------------------------
- /**
- * Compute the number of characters and sms
- * @private
- */
- _compute: function () {
- var content = this._getValue();
- this.encoding = this._extractEncoding(content);
- this.nbrChar = content.length;
- this.nbrChar += (content.match(/\n/g) || []).length;
- this.nbrSMS = this._countSMS(this.nbrChar, this.encoding);
- },
-
/**
* Count the number of SMS of the content
* @private
* @returns {integer} Number of SMS
*/
- _countSMS: function () {
- if (this.nbrChar === 0) {
+ _countSMS(nbrChar, encoding) {
+ if (nbrChar === 0) {
return 0;
}
- if (this.encoding === 'UNICODE') {
- if (this.nbrChar <= 70) {
+ if (encoding === 'UNICODE') {
+ if (nbrChar <= 70) {
return 1;
}
- return Math.ceil(this.nbrChar / 67);
+ return Math.ceil(nbrChar / 67);
}
- if (this.nbrChar <= 160) {
+ if (nbrChar <= 160) {
return 1;
}
- return Math.ceil(this.nbrChar / 153);
- },
+ return Math.ceil(nbrChar / 153);
+ }
/**
* Extract the encoding depending on the characters in the content
@@ -113,49 +75,12 @@ var SmsWidget = EmojisTextField.extend(DynamicPlaceholderFieldMixin, {
* @param {String} content Content of the SMS
* @returns {String} Encoding of the content (GSM7 or UNICODE)
*/
- _extractEncoding: function (content) {
+ _extractEncoding(content) {
if (String(content).match(RegExp("^[@£$¥èéùìòÇ\\nØø\\rÅåΔ_ΦΓΛΩΠΨΣΘΞÆæßÉ !\\\"#¤%&'()*+,-./0123456789:;<=>?¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑܧ¿abcdefghijklmnopqrstuvwxyzäöñüà]*$"))) {
return 'GSM7';
}
return 'UNICODE';
- },
-
- /**
- * Render the IAP button to redirect to IAP pricing
- * @private
- */
- _renderIAPButton: function () {
- return $('', {
- 'href': 'https://iap-services.odoo.com/iap/sms/pricing',
- 'target': '_blank',
- 'title': _t('SMS Pricing'),
- 'aria-label': _t('SMS Pricing'),
- 'class': 'fa fa-lg fa-info-circle',
- });
- },
-
- /**
- * Render the number of characters, sms and the encoding.
- * @private
- */
- _renderSMSInfo: function () {
- var string = _.str.sprintf(_t('%s characters, fits in %s SMS (%s) '), this.nbrChar, this.nbrSMS, this.encoding);
- var $span = $('', {
- 'class': 'text-muted o_sms_count',
- });
- $span.text(string);
- return $span;
- },
-
- /**
- * Update widget SMS information with re-computed info about length, ...
- * @private
- */
- _updateSMSInfo: function () {
- this._compute();
- var string = _.str.sprintf(_t('%s characters, fits in %s SMS (%s) '), this.nbrChar, this.nbrSMS, this.encoding);
- this.$('.o_sms_count').text(string);
- },
+ }
//--------------------------------------------------------------------------
// Handlers
@@ -165,43 +90,34 @@ var SmsWidget = EmojisTextField.extend(DynamicPlaceholderFieldMixin, {
* @override
* @private
*/
- _onBlur: function () {
- var content = this._getValue();
+ async onBlur() {
+ var content = this.props.value || '';
if( !content.trim().length && content.length > 0) {
- this.displayNotification({ title: _t("Your SMS Text Message must include at least one non-whitespace character"), type: 'danger' });
- this.$input.val(content.trim());
- this._updateSMSInfo();
+ this.notification.add(
+ this.env._t("Your SMS Text Message must include at least one non-whitespace character"),
+ { type: 'danger' },
+ )
+ await this.props.update(content.trim());
}
- },
+ }
/**
* @override
* @private
*/
- _onChange: function () {
- this._super.apply(this, arguments);
- this._updateSMSInfo();
- },
-
- /**
- * @override
- * @private
- */
- _onInput: function (ev) {
- this._super.apply(this, arguments);
+ async onInput(ev) {
+ await this.props.update(this.targetEditElement.el.value);
+ super.onInput(...arguments);
const key = ev.originalEvent ? ev.originalEvent.data : '';
- if (this.nodeOptions &&
- this.nodeOptions.dynamic_placeholder &&
- key === this.DYNAMIC_PLACEHOLDER_TRIGGER_KEY) {
+ if (this.props.dynamicPlaceholder && key === this.DYNAMIC_PLACEHOLDER_TRIGGER_KEY) {
const baseModel = this.recordData && this.recordData.mailing_model_real ? this.recordData.mailing_model_real : undefined;
if (baseModel) {
this.openDynamicPlaceholder(baseModel);
}
}
- this._updateSMSInfo();
- },
-});
-
-fieldRegistry.add('sms_widget', SmsWidget);
-
-export default SmsWidget;
+ }
+};
+SmsWidget.template = 'sms.SmsWidget';
+SmsWidget.additionalClasses = [...(EmojisTextField.additionalClasses || []), 'o_field_text'];
+patch(SmsWidget.prototype, 'sms_widget_dynamic_placeholder_field_mixin', DynamicPlaceholderFieldMixin);
+registry.category("fields").add("sms_widget", SmsWidget);
diff --git a/addons/sms/static/src/components/sms_widget/fields_sms_widget.xml b/addons/sms/static/src/components/sms_widget/fields_sms_widget.xml
new file mode 100644
index 00000000000..e3fd7625570
--- /dev/null
+++ b/addons/sms/static/src/components/sms_widget/fields_sms_widget.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+ onBlur
+
+
+
+
+ characters, fits in SMS ()
+
+
+
+
+
+
diff --git a/addons/sms/static/tests/sms_widget_test.js b/addons/sms/static/tests/sms_widget_test.js
index 9d8d2c3001a..008d76de2e0 100644
--- a/addons/sms/static/tests/sms_widget_test.js
+++ b/addons/sms/static/tests/sms_widget_test.js
@@ -3,8 +3,10 @@
import FormView from 'web.FormView';
import ListView from 'web.ListView';
import testUtils from 'web.test_utils';
+import { makeView, setupViewRegistries } from "@web/../tests/views/helpers";
+import { getFixture } from "@web/../tests/helpers/utils";
-var createView = testUtils.createView;
+const createView = testUtils.createView;
QUnit.module('fields', {
beforeEach: function () {
@@ -36,6 +38,8 @@ QUnit.module('fields', {
}]
},
};
+ setupViewRegistries();
+ this.target = getFixture();
}
}, function () {
@@ -43,84 +47,79 @@ QUnit.module('fields', {
QUnit.test('Sms widgets are correctly rendered', async function (assert) {
assert.expect(9);
- var form = await createView({
- View: FormView,
- model: 'partner',
- data: this.data,
- arch: '',
+ await makeView({
+ type: "form",
+ resModel: "partner",
+ serverData: { models: this.data },
+ arch: /* xml */ ``,
});
- assert.containsOnce(form, '.o_sms_count', "Should have a sms counter");
- assert.strictEqual(form.$('.o_sms_count').text(), '0 characters, fits in 0 SMS (GSM7) ',
+ assert.containsOnce(this.target, '.o_sms_count', "Should have a sms counter");
+ assert.strictEqual(this.target.querySelector('.o_sms_count').textContent, '0 characters, fits in 0 SMS (GSM7) ',
'Should be "0 characters, fits in 0 SMS (GSM7) " by default');
// GSM-7
- await testUtils.fields.editAndTrigger(form.$('.o_input'), "Hello from Odoo", 'input');
- assert.strictEqual(form.$('.o_sms_count').text(), '15 characters, fits in 1 SMS (GSM7) ',
+ await testUtils.fields.editAndTrigger(this.target.querySelector('.o_input'), "Hello from Odoo", 'input');
+ assert.strictEqual(this.target.querySelector('.o_sms_count').textContent, '15 characters, fits in 1 SMS (GSM7) ',
'Should be "15 characters, fits in 1 SMS (GSM7) " for "Hello from Odoo"');
// GSM-7 with \n => this one count as 2 characters
- form.$('.o_input').val("Hello from Odoo\n").trigger('input');
- assert.strictEqual(form.$('.o_sms_count').text(), '17 characters, fits in 1 SMS (GSM7) ',
+ await testUtils.fields.editAndTrigger(this.target.querySelector('.o_input'), "Hello from Odoo\n", 'input');
+ assert.strictEqual(this.target.querySelector('.o_sms_count').textContent, '17 characters, fits in 1 SMS (GSM7) ',
'Should be "17 characters, fits in 1 SMS (GSM7) " for "Hello from Odoo\\n"');
// Unicode => ê
- form.$('.o_input').val("Hêllo from Odoo").trigger('input');
- assert.strictEqual(form.$('.o_sms_count').text(), '15 characters, fits in 1 SMS (UNICODE) ',
+ await testUtils.fields.editAndTrigger(this.target.querySelector('.o_input'), "Hêllo from Odoo", 'input');
+ assert.strictEqual(this.target.querySelector('.o_sms_count').textContent, '15 characters, fits in 1 SMS (UNICODE) ',
'Should be "15 characters, fits in 1 SMS (UNICODE) " for "Hêllo from Odoo"');
// GSM-7 with 160c
var text = Array(161).join('a');
- await testUtils.fields.editAndTrigger(form.$('.o_input'), text, 'input');
- assert.strictEqual(form.$('.o_sms_count').text(), '160 characters, fits in 1 SMS (GSM7) ',
+ await testUtils.fields.editAndTrigger(this.target.querySelector('.o_input'), text, 'input');
+ assert.strictEqual(this.target.querySelector('.o_sms_count').textContent, '160 characters, fits in 1 SMS (GSM7) ',
'Should be "160 characters, fits in 1 SMS (GSM7) " for 160 x "a"');
// GSM-7 with 161c
text = Array(162).join('a');
- await testUtils.fields.editAndTrigger(form.$('.o_input'), text, 'input');
- assert.strictEqual(form.$('.o_sms_count').text(), '161 characters, fits in 2 SMS (GSM7) ',
+ await testUtils.fields.editAndTrigger(this.target.querySelector('.o_input'), text, 'input');
+ assert.strictEqual(this.target.querySelector('.o_sms_count').textContent, '161 characters, fits in 2 SMS (GSM7) ',
'Should be "161 characters, fits in 2 SMS (GSM7) " for 161 x "a"');
// Unicode with 70c
text = Array(71).join('ê');
- await testUtils.fields.editAndTrigger(form.$('.o_input'), text, 'input');
- assert.strictEqual(form.$('.o_sms_count').text(), '70 characters, fits in 1 SMS (UNICODE) ',
+ await testUtils.fields.editAndTrigger(this.target.querySelector('.o_input'), text, 'input');
+ assert.strictEqual(this.target.querySelector('.o_sms_count').textContent, '70 characters, fits in 1 SMS (UNICODE) ',
'Should be "70 characters, fits in 1 SMS (UNICODE) " for 70 x "ê"');
// Unicode with 71c
text = Array(72).join('ê');
- await testUtils.fields.editAndTrigger(form.$('.o_input'), text, 'input');
- assert.strictEqual(form.$('.o_sms_count').text(), '71 characters, fits in 2 SMS (UNICODE) ',
+ await testUtils.fields.editAndTrigger(this.target.querySelector('.o_input'), text, 'input');
+ assert.strictEqual(this.target.querySelector('.o_sms_count').textContent, '71 characters, fits in 2 SMS (UNICODE) ',
'Should be "71 characters, fits in 2 SMS (UNICODE) " for 71 x "ê"');
- form.destroy();
});
QUnit.test('Sms widgets with non-empty initial value', async function (assert) {
assert.expect(1);
- var form = await createView({
- View: FormView,
- model: 'visitor',
- data: this.data,
- arch: ``,
- res_id: 1,
- res_ids: [1],
+ await makeView({
+ type: "form",
+ resModel: "visitor",
+ resId: 1,
+ serverData: { models: this.data },
+ arch: /* xml */ ``,
});
- assert.strictEqual(form.$('.o_field_text').text(), '+32494444444',
+ assert.strictEqual(this.target.querySelector('.o_field_text span').textContent, '+32494444444',
'Should have the initial value');
- form.destroy();
});
QUnit.test('Sms widgets with empty initial value', async function (assert) {
assert.expect(1);
- var form = await createView({
- View: FormView,
- model: 'partner',
- data: this.data,
- arch: ``,
- res_id: 1,
- res_ids: [1],
+ await makeView({
+ type: "form",
+ resModel: "partner",
+ resId: 1,
+ serverData: { models: this.data },
+ arch: /* xml */ ``,
});
- assert.strictEqual(form.$('.o_field_text').text(), '',
+ assert.strictEqual(this.target.querySelector('.o_field_text span').textContent, '',
'Should have the empty initial value');
- form.destroy();
});
QUnit.module('PhoneWidget');