[REF] *: BS5: Migrate modals and .btn-close for modals and alerts

- modal 'show' option doesn't exist anymore
  -> We need to call .show()

- by default, show is not the default
  -> we need to call .show() explicitly.

- generic close button for dismissing content like modals and alerts.

- BS5 modals needs `modal-dialog` class to work

- normally we need also to add `modal-content` class but as the original
XML don't have this nested level of div we don't use it, but instead
we add `pointer-events: auto` for all children `DIV` of `modal-dialog`
-> See DocumentViewer

Task ID: 2766483

Part-of: odoo/odoo#95450
This commit is contained in:
Romeo Fragomeli
2022-07-07 13:30:18 +02:00
parent ac584e171d
commit b82ba2a8fd
31 changed files with 56 additions and 50 deletions
@@ -47,6 +47,12 @@
position: relative;
width: 100%;
height: 100%;
padding: 0;
/* Act as `modal-content` */
& > div {
pointer-events: auto;
}
.o_viewer-header {
display: flex;
+1 -1
View File
@@ -5,7 +5,7 @@
@param {mail.DocumentViewer} widget
-->
<t t-name="DocumentViewer.Content">
<div class="o_viewer_content">
<div class="o_viewer_content modal-dialog modal-fullscreen">
<t t-set="model" t-value="widget.modelName"/>
<div class="o_viewer-header">
<span class="o_image_caption">
@@ -113,8 +113,11 @@ var OptionalProductsModal = Dialog.extend(ServicesMixin, VariantMixin, {
self.$modal.find(".modal-body").replaceWith(self.$el);
self.$modal.attr('open', true);
self.$modal.removeAttr("aria-hidden");
self.$modal.modal().appendTo(self.container);
self.$modal.focus();
self.$modal.appendTo(self.container);
const modal = new Modal(self.$modal[0], {
focus: true,
});
modal.show();
self._openedResolver();
// Notifies OwlDialog to adjust focus/active properties on owl dialogs
+1 -1
View File
@@ -18,7 +18,7 @@
<t t-esc="props.title"/>
</h4>
<t t-if="!isFullscreen">
<button type="button" class="close" aria-label="Close" tabindex="-1" t-on-click="data.close">×</button>
<button type="button" class="btn-close" aria-label="Close" tabindex="-1" t-on-click="data.close"></button>
</t>
</header>
<!-- FIXME: WOWL there is a bug on t-portal on owl, in which t-portal don't work on multinode.
@@ -204,11 +204,12 @@ var Dialog = Widget.extend({
if (self.$parentNode) {
self.$modal.appendTo(self.$parentNode);
}
self.$modal.modal({
show: true,
const modalNode = self.$modal[0];
const modal = new Modal(modalNode, {
backdrop: self.backdrop,
keyboard: false,
});
modal.show();
self._openedResolver();
if (options && options.shouldFocusButtons) {
self._onFocusControlButton();
@@ -18,7 +18,7 @@ var FormRenderer = BasicRenderer.extend({
className: "o_form_view",
events: _.extend({}, BasicRenderer.prototype.events, {
'click .o_notification_box .oe_field_translate': '_onTranslate',
'click .o_notification_box .close': '_onTranslateNotificationClose',
'click .o_notification_box .btn-close': '_onTranslateNotificationClose',
'shown.bs.tab a[data-toggle="tab"]': '_onNotebookTabChanged',
'click .o_form_label': '_onFieldLabelClicked',
}),
@@ -1367,6 +1367,8 @@ var FormRenderer = BasicRenderer.extend({
* @param {MouseEvent} ev
*/
_onTranslateNotificationClose: function(ev) {
const notificationElement = this.el.querySelector('.o_notification_box');
Alert.getOrCreateInstance(notificationElement).close();
delete this.alertFields[this.state.res_id];
},
});
+2 -4
View File
@@ -255,7 +255,7 @@
<t t-esc="props.title"/>
<span t-if="props.subtitle" class="o_subtitle text-muted small" t-esc="props.subtitle"/>
</h4>
<button type="button" class="close" aria-label="Close" tabindex="-1" t-on-click="_close">×</button>
<button type="button" class="btn-close" aria-label="Close" tabindex="-1" t-on-click="_close"></button>
</header>
<main class="modal-body">
<t t-slot="default"/>
@@ -1513,9 +1513,7 @@
<t t-name="notification-box">
<div t-attf-class="o_notification_box mb0 alert alert-dismissible alert-{{type}}" role="alertdialog">
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
<span class="fa fa-times"></span>
</button>
<button type="button" class="btn-close" aria-label="Close"></button>
</div>
</t>
+1 -1
View File
@@ -15,7 +15,7 @@
<div class="modal-content">
<header t-if="renderHeader" class="modal-header">
<h4 class="modal-title"><t t-out="title"/><span class="o_subtitle text-muted small" t-out="subtitle"/></h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close" tabindex="-1">×</button>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" tabindex="-1"></button>
</header>
<main class="modal-body"/>
<footer t-if="renderFooter" class="modal-footer justify-content-around justify-content-sm-start flex-wrap gap-1"/>
@@ -58,9 +58,7 @@
<t t-foreach="Array.from(this.translateAlertData[this.model.root.resId])" t-as="field" t-key="field.name">
<strong><a class="o_field_translate" t-att-name="field.name" href="#"><t t-esc="field.string"/><t t-if="!field_last">, </t></a></strong>
</t>
<button t-on-click="() => this.closeTranslateAlert()" type="button" class="close" aria-label="Close">
<span class="fa fa-times"></span>
</button>
<button t-on-click="() => this.closeTranslateAlert()" type="button" class="btn-close" aria-label="Close"></button>
</div>
</t>
+1 -1
View File
@@ -112,7 +112,7 @@ QUnit.module("Components", (hooks) => {
Parent.components = { Dialog };
parent = await mount(Parent, target, { env });
assert.containsOnce(target, ".o_dialog");
await click(target, ".o_dialog header button.close");
await click(target, ".o_dialog header button.btn-close");
assert.verifySteps(["close"]);
});
@@ -84,7 +84,7 @@ odoo.define('web.owl_dialog_tests', function (require) {
// Basic layout with default properties
assert.containsOnce(dialog, '.modal.o_technical_modal');
assert.hasClass(dialog.querySelector('.modal .modal-dialog'), 'modal-lg');
assert.containsOnce(dialog, '.modal-header > button.close');
assert.containsOnce(dialog, '.modal-header > button.btn-close');
assert.containsOnce(dialog, '.modal-footer > button.btn.btn-primary');
assert.strictEqual(dialog.querySelector('.modal-body').innerText.trim(), "sup",
"Subcomponent should match with its given text");
@@ -261,7 +261,7 @@ odoo.define('web.owl_dialog_tests', function (require) {
assert.containsOnce(document.body, '.o_dialog');
assert.containsOnce(document.body, '.o_legacy_dialog');
await testUtils.dom.click(modals[modals.length - 1].querySelector('.close'));
await testUtils.dom.click(modals[modals.length - 1].querySelector('.btn-close'));
modals = document.querySelectorAll('.modal');
assert.notOk(modals[modals.length - 1].classList.contains('o_inactive_modal'),
@@ -514,12 +514,12 @@ odoo.define('web.owl_dialog_tests', function (require) {
"wowl"
);
await testUtils.dom.click(target.querySelector(`.o_dialog .modal-header .close`));
await testUtils.dom.click(target.querySelector(`.o_dialog .modal-header .btn-close`));
assert.containsNone(target, ".o_dialog");
assert.containsOnce(target, ".modal");
assert.containsOnce(target, ".modal[tabindex='-1']");
await testUtils.dom.click(target.querySelector(`.modal-header .close`));
await testUtils.dom.click(target.querySelector(`.modal-header .btn-close`));
assert.containsNone(target, ".o_dialog");
assert.containsNone(target, ".modal");
});
@@ -339,7 +339,7 @@ QUnit.module('Legacy relational_fields', {
assert.ok($('body').hasClass('modal-open'), 'Modal is said opened');
// Close second modal
await testUtils.dom.click($modals.last().find('button[class="close"]'));
await testUtils.dom.click($modals.last().find('button[class="btn-close"]'));
var $modal = $('.modal');
$focusedModal = $(document.activeElement).closest('.modal');
@@ -349,7 +349,7 @@ QUnit.module('Legacy relational_fields', {
assert.ok($('body').hasClass('modal-open'), 'Modal is said opened');
// Close first modal
await testUtils.dom.click($modal.find('button[class="close"]'));
await testUtils.dom.click($modal.find('button[class="btn-close"]'));
$modal = $('.modal-dialog.modal-lg');
assert.equal($modal.length, 0, 'There should be no modal');
@@ -6392,7 +6392,7 @@ QUnit.module('LegacyViews', {
await testUtils.dom.click(form.$('.o_external_button'));
// Close modal
await testUtils.dom.click($('.modal').last().find('button[class="close"]'));
await testUtils.dom.click($('.modal').last().find('button[class="btn-close"]'));
assert.notStrictEqual($(".o_content").scrollTop(), 0,
"scroll position should not be 0 after closing modal");
assert.containsNone(document.body, '.modal', 'There should be no modal');
@@ -7318,7 +7318,7 @@ QUnit.module('LegacyViews', {
assert.containsOnce(form, '.o_form_view .alert > div', "should have a translation alert");
// remove translation alert by click X and check alert even after form reload
await testUtils.dom.click(form.$('.o_form_view .alert > .close'));
await testUtils.dom.click(form.$('.o_form_view .alert > .btn-close'));
assert.containsNone(form, '.o_form_view .alert > div', "should not have a translation alert");
await form.reload();
@@ -3986,7 +3986,7 @@ QUnit.module('LegacyViews', {
'the name should be "xmo"');
await testUtils.fields.editInput($('.modal .o_form_editable input'), 'ged'); // change the value
nbRPCs = 0;
await testUtils.dom.click($('.modal-header .close'));
await testUtils.dom.click($('.modal-header .btn-close'));
assert.containsNone(document.body, '.modal');
assert.strictEqual(kanban.$('.o_kanban_group[data-id=5] .o_column_title').text(), 'xmo',
'title of the column should still be "xmo"');
@@ -3892,7 +3892,7 @@ QUnit.module("Fields", (hooks) => {
assert.hasClass(document.body, "modal-open");
// Close second modal
await click(nextModal, "button[class='close']");
await click(nextModal, "button[class='btn-close']");
assert.containsOnce(target, ".modal");
assert.strictEqual(
@@ -3904,7 +3904,7 @@ QUnit.module("Fields", (hooks) => {
assert.hasClass(document.body, "modal-open");
// Close first modal
await click(originalModal, "button[class='close']");
await click(originalModal, "button[class='btn-close']");
assert.containsNone(target, ".modal");
assert.doesNotHaveClass(document.body, "modal-open");
});
@@ -6625,7 +6625,7 @@ QUnit.module("Views", (hooks) => {
await click(target.querySelector(".o_external_button"));
// Close modal
await click(target, '.modal-dialog button[class="close"]');
await click(target, '.modal-dialog button[class="btn-close"]');
assert.notStrictEqual(
$(".o_content").scrollTop(),
0,
@@ -4935,7 +4935,7 @@ QUnit.module("Views", (hooks) => {
await editInput(target, ".modal .o_form_editable input", "ged"); // change the value
nbRPCs = 0;
await click(target, ".modal-header .close");
await click(target, ".modal-header .btn-close");
assert.containsNone(target, ".modal");
assert.strictEqual(
+1 -3
View File
@@ -1139,9 +1139,7 @@ QUnit.module("Views", (hooks) => {
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Remove Configuration Tips</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-label="Close">×</span>
</button>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Do you want to remove this configuration panel?</p>
@@ -145,7 +145,7 @@ QUnit.module("ActionManager", (hooks) => {
// open a new dialog form
await doAction(webClient, 5, { onClose });
await click(target, ".modal-header button.close");
await click(target, ".modal-header button.btn-close");
await nextTick();
await legacyExtraNextTick();
assert.containsNone(target, ".modal");
@@ -997,7 +997,7 @@ QUnit.module("ActionManager", (hooks) => {
assert.verifySteps(["clientAction setup"]);
await nextTick();
assert.containsOnce(target, ".o_dialog_error");
await click(target, ".modal-header .close");
await click(target, ".modal-header .btn-close");
assert.containsNone(target, ".o_dialog_error");
await click(target, "nav .o_navbar_apps_menu .dropdown-toggle ");
assert.containsN(target, ".dropdown-item.o_app", 3);
@@ -1954,7 +1954,7 @@ QUnit.module("ActionManager", (hooks) => {
"Warning modal should be opened"
);
await click(document.querySelector(".modal.o_technical_modal button.close"));
await click(document.querySelector(".modal.o_technical_modal button.btn-close"));
assert.containsNone(
document.body,
".modal.o_technical_modal",
@@ -163,7 +163,7 @@ var ViewEditor = Widget.extend({
'click button[data-action=reset]': '_onResetClick',
'click button[data-action=format]': '_onFormatClick',
'click button[data-action=close]': '_onCloseClick',
'click #ace-view-id > .alert-warning .close': '_onCloseWarningClick'
'click #ace-view-id > .alert-warning .btn-close': '_onCloseWarningClick'
},
/**
+1 -3
View File
@@ -51,9 +51,7 @@
<span class="o_ace_editor_resource_info"/>
<div class="alert alert-warning alert-dismissible mt-2 mb-0" role="alert">
Editing a built-in file through this editor is not advised, as it will prevent it from being updated during future App upgrades.
<button type="button" class="close" aria-label="Close">
<span>×</span>
</button>
<button type="button" class="btn-close" aria-label="Close"></button>
</div>
</div>
<div id="ace-view-editor"/>
@@ -53,14 +53,16 @@ var EventRegistrationForm = Widget.extend({
$button.attr('disabled', true);
return ajax.jsonRpc($form.attr('action'), 'call', post).then(function (modal) {
var $modal = $(modal);
$modal.modal({backdrop: 'static', keyboard: false});
$modal.find('.modal-body > div').removeClass('container'); // retrocompatibility - REMOVE ME in master / saas-19
$modal.appendTo(document.body);
const modalBS = new Modal($modal[0], {backdrop: 'static', keyboard: false});
modalBS.show();
$modal.appendTo('body').modal();
$modal.on('click', '.js_goto_event', function () {
$modal.modal('hide');
$button.prop('disabled', false);
});
$modal.on('click', '.close', function () {
$modal.on('click', '.btn-close', function () {
$button.prop('disabled', false);
});
});
@@ -280,7 +280,7 @@
<div class="modal-content">
<div class="modal-header align-items-center">
<h4 class="modal-title">Attendees</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span>×</span></button>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
</div>
<t t-set="counter_type" t-value="1"/>
<t t-set="counter" t-value="0"/>
+1 -1
View File
@@ -1614,7 +1614,7 @@
<a class="nav-link spam_menu" id="character-tab" data-toggle="tab" href="#spam_character" role="tab" aria-controls="spam_character" aria-selected="false">圾 Text</a>
</li>
</ul>
<button type="button" class="close align-self-start" data-dismiss="modal"><span aria-label="Close">×</span></button>
<button type="button" class="btn-close align-self-start" data-dismiss="modal"></button>
</div>
<div class="modal-body bg-100">
<div class="tab-content" id="o_tab_content_spam">
@@ -380,10 +380,10 @@ publicWidget.registry.WebsiteSale = publicWidget.Widget.extend(VariantMixin, car
var cart_alert = $('.oe_cart').parent().find('#data_warning');
if (cart_alert.length === 0) {
$('.oe_cart').prepend('<div class="alert alert-danger alert-dismissable" role="alert" id="data_warning">'+
'<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> ' + data.warning + '</div>');
'<button type="button" class="btn-close" data-dismiss="alert" aria-hidden="true"></button> ' + data.warning + '</div>');
}
else {
cart_alert.html('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> ' + data.warning);
cart_alert.html('<button type="button" class="btn-close" data-dismiss="alert" aria-hidden="true"></button> ' + data.warning);
}
$input.val(data.quantity);
}
@@ -194,7 +194,7 @@ publicWidget.registry.ProductWishlist = publicWidget.Widget.extend(VariantMixin,
$('.wishlist-section').prepend('<div class="mt16 alert alert-danger alert-dismissable" role="alert" id="data_warning"></div>');
}
var cart_alert = $('.wishlist-section').parent().find('#data_warning');
cart_alert.html('<button type="button" class="close" data-dismiss="alert" aria-hidden="true">&times;</button> ' + resp.warning);
cart_alert.html('<button type="button" class="btn-close" data-bs-dismiss="alert" aria-hidden="true">&times;</button> ' + resp.warning);
}
sessionStorage.setItem('website_sale_cart_quantity', resp.cart_quantity);
$('.my_cart_quantity').html(resp.cart_quantity || '<i class="fa fa-warning" /> ');
@@ -124,7 +124,7 @@
<t t-name="slide.slide.quiz.finish">
<div>
<button type="button" class="o_wslides_quiz_modal_close_btn close position-absolute" data-dismiss="modal" aria-label="Close">&#215;</button>
<button type="button" class="o_wslides_quiz_modal_close_btn btn-close position-absolute" data-dismiss="modal" aria-label="Close"></button>
<div class="o_wslides_quiz_success_image d-none d-md-flex flex-shrink-0">
<img class="o_wslides_quiz_modal_hero" src="/website_slides/static/src/img/quiz_modal_success.svg" alt=""/>
</div>
+1 -3
View File
@@ -6,9 +6,7 @@
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Remove Configuration Tips</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-label="Close">×</span>
</button>
<button type="button" class="btn-close" data-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Do you want to remove this configuration panel?</p>
+2
View File
@@ -35,6 +35,8 @@
"moment": "readonly",
"py": "readonly",
"ClipboardJS": "readonly",
"Modal": "readonly",
"Alert": "readonly",
"globalThis": "readonly",
"module": "readonly",
"chai": "readonly",