diff --git a/addons/mail/static/tests/chatter_tests.js b/addons/mail/static/tests/chatter_tests.js
index 088a854333e..e4b720e19d5 100644
--- a/addons/mail/static/tests/chatter_tests.js
+++ b/addons/mail/static/tests/chatter_tests.js
@@ -857,7 +857,7 @@ QUnit.test('chatter: discard changes on message post with post_refresh "recipien
$input.trigger($.Event('keyup', {which: $.ui.keyCode.ENTER}));
// untick recipient as follower (prompts a res.partner form otherwise)
- form.$('.o_checkbox input').prop('checked', false);
+ form.$('input[type="checkbox"]').prop('checked', false);
// send message
form.$('.oe_chatter .o_composer_button_send').click();
diff --git a/addons/web/static/src/js/core/dom.js b/addons/web/static/src/js/core/dom.js
index ba3eebf9bea..d695cf18296 100644
--- a/addons/web/static/src/js/core/dom.js
+++ b/addons/web/static/src/js/core/dom.js
@@ -261,33 +261,41 @@ return {
return $button;
},
/**
- * Renders a checkbox with standard odoo template. This does not use any xml
- * template to avoid forcing the frontend part to lazy load a xml file for
- * each widget which might want to create a simple checkbox.
+ * Renders a checkbox with standard odoo/BS template. This does not use any
+ * xml template to avoid forcing the frontend part to lazy load a xml file
+ * for each widget which might want to create a simple checkbox.
*
* @param {Object} [options]
* @param {Object} [options.prop]
* Allows to set the input properties (disabled and checked states).
* @param {string} [options.text]
* The checkbox's associated text. If none is given then a simple
- * checkbox without label structure is rendered.
+ * checkbox is rendered.
* @returns {jQuery}
*/
renderCheckbox: function (options) {
- var $container = $('
');
+ var id = _.uniqueId('checkbox-');
+ var $container = $('', {
+ class: 'custom-control custom-checkbox',
+ });
+ var $input = $('', {
+ type: 'checkbox',
+ id: id,
+ class: 'custom-control-input',
+ });
+ var $label = $('', {
+ for: id,
+ class: 'custom-control-label',
+ text: options && options.text || '',
+ });
+ if (!options || !options.text) {
+ $label.html(''); // BS checkboxes need some label content (so
+ // add a zero-width space when there is no text)
+ }
if (options && options.prop) {
- $container.children('input').prop(options.prop);
+ $input.prop(options.prop);
}
- if (options && options.text) {
- $container = $('').append(
- $container,
- $('', {
- class: 'ml8',
- text: options.text,
- })
- );
- }
- return $container;
+ return $container.append($input, $label);
},
/**
* Sets the selection range of a given input or textarea
diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js
index eab0093ed50..7cf72791be5 100644
--- a/addons/web/static/src/js/fields/abstract_field.js
+++ b/addons/web/static/src/js/fields/abstract_field.js
@@ -304,7 +304,6 @@ var AbstractField = Widget.extend({
this._reset(record, event);
return this._render() || $.when();
},
-
/**
* Remove the invalid class on a field
*/
@@ -312,7 +311,15 @@ var AbstractField = Widget.extend({
this.$el.removeClass('o_field_invalid');
this.$el.removeAttr('aria-invalid');
},
-
+ /**
+ * Sets the given id on the focusable element of the field and as 'for'
+ * attribute of potential internal labels.
+ *
+ * @param {string} id
+ */
+ setIDForLabel: function (id) {
+ this.getFocusableElement().attr('id', id);
+ },
/**
* add the invalid class on a field
*/
@@ -320,7 +327,6 @@ var AbstractField = Widget.extend({
this.$el.addClass('o_field_invalid');
this.$el.attr('aria-invalid', 'true');
},
-
/**
* Update the modifiers with the newest value.
* Now this.attrs.modifiersValue can be used consistantly even with
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index b48ea9022f9..edea04e0e48 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -701,8 +701,8 @@ var FieldBoolean = AbstractField.extend({
// The formatValue of boolean fields renders HTML elements similar to
// the one rendered by the widget itself. Even though the event might
// have been fired on the non-widget version of this field, we can still
- // test the presence of its o_checkbox class.
- if (activated && options && options.event && $(options.event.target).parents('.o_checkbox').length) {
+ // test the presence of its custom class.
+ if (activated && options && options.event && $(options.event.target).closest('.custom-control.custom-checkbox').length) {
this._setValue(!this.value); // Toggle the checkbox
}
return activated;
@@ -737,6 +737,15 @@ var FieldBoolean = AbstractField.extend({
}
return rendered;
},
+ /**
+ * Associates the 'for' attribute of the internal label.
+ *
+ * @override
+ */
+ setIDForLabel: function (id) {
+ this._super.apply(this, arguments);
+ this.$('.custom-control-label').attr('for', id);
+ },
//--------------------------------------------------------------------------
// Private
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index 73bcc1f1f35..ba83de415e7 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -1887,7 +1887,7 @@ var FormFieldMany2ManyTags = FieldMany2ManyTags.extend({
this.$color_picker.dropdown('toggle');
this.$color_picker.attr("tabindex", 1).focus();
if (!tagColor) {
- this.$('.o_checkbox input').prop('checked', true);
+ this.$('.custom-checkbox input').prop('checked', true);
}
}
},
@@ -1909,7 +1909,7 @@ var FormFieldMany2ManyTags = FieldMany2ManyTags.extend({
var changes = {};
if ($target.is('.o_hide_in_kanban')) {
- var $checkbox = $('.o_hide_in_kanban .o_checkbox input');
+ var $checkbox = $('.o_hide_in_kanban .custom-checkbox input');
$checkbox.prop('checked', !$checkbox.prop('checked')); // toggle checkbox
this.prevColors = this.prevColors ? this.prevColors : {};
if ($checkbox.is(':checked')) {
diff --git a/addons/web/static/src/js/views/calendar/calendar_renderer.js b/addons/web/static/src/js/views/calendar/calendar_renderer.js
index 4b4050b67cc..ffb4f41ad6b 100644
--- a/addons/web/static/src/js/views/calendar/calendar_renderer.js
+++ b/addons/web/static/src/js/views/calendar/calendar_renderer.js
@@ -98,7 +98,7 @@ var SidebarFilter = Widget.extend(FieldManagerMixin, {
this.many2one.filter_ids = _.without(_.pluck(this.filters, 'value'), 'all');
}
this.$el.on('click', '.o_remove', this._onFilterRemove.bind(this));
- this.$el.on('click', '.o_checkbox input', this._onFilterActive.bind(this));
+ this.$el.on('click', '.custom-checkbox input', this._onFilterActive.bind(this));
},
//--------------------------------------------------------------------------
diff --git a/addons/web/static/src/js/views/form/form_renderer.js b/addons/web/static/src/js/views/form/form_renderer.js
index 779c6a48c17..276173c6f9a 100644
--- a/addons/web/static/src/js/views/form/form_renderer.js
+++ b/addons/web/static/src/js/views/form/form_renderer.js
@@ -902,7 +902,7 @@ var FormRenderer = BasicRenderer.extend({
// enterprise label will be displayed as many times as the field
// exists on settings.
var $widgets = self.$('.o_field_widget[name=' + widget.name + ']');
- var $label = idForLabel ? self.$('label[for=' + idForLabel + ']') : $();
+ var $label = idForLabel ? self.$('.o_form_label[for=' + idForLabel + ']') : $();
$label = $label.eq($widgets.index(widget.$el));
if (config.debug || widget.attrs.help || widget.field.help) {
self._addFieldTooltip(widget, $label);
@@ -922,7 +922,7 @@ var FormRenderer = BasicRenderer.extend({
* @param {idForLabel} string
*/
_setIDForLabel: function (widget, idForLabel) {
- widget.getFocusableElement().attr('id', idForLabel);
+ widget.setIDForLabel(idForLabel);
},
//--------------------------------------------------------------------------
diff --git a/addons/web/static/src/scss/list_view.scss b/addons/web/static/src/scss/list_view.scss
index 37093acfe96..ba5cd8a4dab 100644
--- a/addons/web/static/src/scss/list_view.scss
+++ b/addons/web/static/src/scss/list_view.scss
@@ -13,7 +13,7 @@
// Checkbox in editable list
// should be clickable and activate the row
&.o_editable_list .o_data_row:not(.o_selected_row) .o_data_cell {
- .o_checkbox:not(.o_readonly_modifier) {
+ .custom-checkbox:not(.o_readonly_modifier) {
pointer-events: none;
}
}
diff --git a/addons/web/static/src/scss/ui_extra.scss b/addons/web/static/src/scss/ui_extra.scss
index 0d97c09092e..e69de29bb2d 100644
--- a/addons/web/static/src/scss/ui_extra.scss
+++ b/addons/web/static/src/scss/ui_extra.scss
@@ -1,10 +0,0 @@
-//------------------------------------------------------------------------------
-// Checkboxes
-//------------------------------------------------------------------------------
-div.o_checkbox {
- display: inline-block;
- vertical-align: sub;
- > input + span {
- display: none;
- }
-}
diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml
index 02d976a2fe3..2a07ccaffeb 100644
--- a/addons/web/static/src/xml/base.xml
+++ b/addons/web/static/src/xml/base.xml
@@ -868,10 +868,10 @@