[FIX] web,mail: only apply modal activity for Dialog modals

Before this commit, the CSS rule handling the "activity" of the modals
(their z-index compared to the one of their backdrop) was global and would
interfere with front-end or custom modals which do not use the static Dialog
methods giving them the "active" class.

Now, the rule has been inverted: the z-index of a modal is only changed when
it is given an "inactive" class, thus only impacting the modals instantiated
via Dialog or Owl Dialog classes.

closes odoo/odoo#44067

Signed-off-by: Aaron Bohy (aab) <aab@odoo.com>
This commit is contained in:
Julien Mougenot
2020-01-28 11:35:14 +00:00
parent a61332e251
commit 5f6571a6fe
4 changed files with 79 additions and 20 deletions
+1 -1
View File
@@ -87,7 +87,7 @@
@param {mail.DocumentViewer} widget
-->
<t t-name="DocumentViewer">
<div class="modal o_modal_fullscreen o_active_modal" tabindex="-1" data-keyboard="false" role="dialog">
<div class="modal o_modal_fullscreen" tabindex="-1" data-keyboard="false" role="dialog">
<t class="o_document_viewer_content_call" t-call="DocumentViewer.Content"/>
<t t-if="widget.attachment.length !== 1">
+10 -12
View File
@@ -189,20 +189,18 @@ odoo.define('web.OwlDialog', function (require) {
* @param {(LegacyDialog|OwlDialog)} dialog
*/
static display(dialog) {
// Deactivate previous dialog
const activeDialogEl = document.querySelector('.modal.o_active_modal');
if (activeDialogEl) {
activeDialogEl.classList.remove('o_active_modal');
const activeDialog = this.displayed[this.displayed.length - 1];
if (activeDialog) {
// Deactivate previous dialog
const activeDialogEl = activeDialog instanceof this ?
// Owl dialog
activeDialog.modalRef.el :
// Legacy dialog
activeDialog.$modal[0];
activeDialogEl.classList.add('o_inactive_modal');
}
// Push dialog
this.displayed.push(dialog);
// Add active class
const modalEl = dialog instanceof this ?
// Owl dialog
dialog.modalRef.el :
// Legacy dialog
dialog.$modal[0];
modalEl.classList.add('o_active_modal');
// Update body class
document.body.classList.add('modal-open');
}
@@ -224,7 +222,7 @@ odoo.define('web.OwlDialog', function (require) {
lastDialog.modalRef.el :
// Legacy dialog
lastDialog.$modal[0];
modalEl.classList.add('o_active_modal');
modalEl.classList.remove('o_inactive_modal');
} else {
document.body.classList.remove('modal-open');
}
+2 -2
View File
@@ -106,8 +106,8 @@
}
}
.modal:not(.o_active_modal) {
z-index: 1030;
.modal.o_inactive_modal {
z-index: $zindex-modal-backdrop - 1;
}
.o_dialog {
@@ -200,7 +200,7 @@ odoo.define('web.owl_dialog_tests', function (require) {
unopenedModal.close();
let modals = document.querySelectorAll('.modal');
assert.ok(modals[modals.length - 1].classList.contains('o_active_modal'),
assert.notOk(modals[modals.length - 1].classList.contains('o_inactive_modal'),
"last dialog should have the active class");
assert.notOk(modals[modals.length - 1].classList.contains('o_legacy_dialog'),
"active dialog should not have the legacy class");
@@ -211,7 +211,7 @@ odoo.define('web.owl_dialog_tests', function (require) {
await testUtils.dom.triggerEvent(modals[modals.length - 1], 'keydown', EscapeKey); // Press Escape
modals = document.querySelectorAll('.modal');
assert.ok(modals[modals.length - 1].classList.contains('o_active_modal'),
assert.notOk(modals[modals.length - 1].classList.contains('o_inactive_modal'),
"last dialog should have the active class");
assert.notOk(modals[modals.length - 1].classList.contains('o_legacy_dialog'),
"active dialog should not have the legacy class");
@@ -221,7 +221,7 @@ odoo.define('web.owl_dialog_tests', function (require) {
await testUtils.dom.click(modals[modals.length - 1].querySelector('.btn.btn-primary')); // Click on 'Ok' button
modals = document.querySelectorAll('.modal');
assert.containsOnce(document.body, '.modal.o_legacy_dialog.o_active_modal',
assert.containsOnce(document.body, '.modal.o_legacy_dialog:not(.o_inactive_modal)',
"active dialog should have the legacy class");
assert.containsOnce(document.body, '.o_dialog');
assert.containsN(document.body, '.o_legacy_dialog', 2);
@@ -230,7 +230,7 @@ odoo.define('web.owl_dialog_tests', function (require) {
await testUtils.dom.triggerEvent(modals[modals.length - 1], 'keydown', EscapeKey);
modals = document.querySelectorAll('.modal');
assert.containsOnce(document.body, '.modal.o_legacy_dialog.o_active_modal',
assert.containsOnce(document.body, '.modal.o_legacy_dialog:not(.o_inactive_modal)',
"active dialog should have the legacy class");
assert.containsOnce(document.body, '.o_dialog');
assert.containsOnce(document.body, '.o_legacy_dialog');
@@ -238,7 +238,7 @@ odoo.define('web.owl_dialog_tests', function (require) {
await testUtils.dom.click(modals[modals.length - 1].querySelector('.close'));
modals = document.querySelectorAll('.modal');
assert.ok(modals[modals.length - 1].classList.contains('o_active_modal'),
assert.notOk(modals[modals.length - 1].classList.contains('o_inactive_modal'),
"last dialog should have the active class");
assert.notOk(modals[modals.length - 1].classList.contains('o_legacy_dialog'),
"active dialog should not have the legacy class");
@@ -254,4 +254,65 @@ odoo.define('web.owl_dialog_tests', function (require) {
parent.destroy();
});
});
QUnit.test("Z-index toggling and interactions", async function (assert) {
assert.expect(3);
function createCustomModal(className) {
const $modal = $(
`<div role="dialog" class="${className}" tabindex="-1">
<div class="modal-dialog medium">
<div class="modal-content">
<main class="modal-body">The modal body</main>
</div>
</div>
</div>`
).appendTo('body').modal();
const modal = $modal[0];
modal.destroy = function () {
$modal.modal('hide');
this.remove();
};
return modal;
}
class Parent extends Component {
constructor() {
super(...arguments);
this.state = useState({ showSecondDialog: true });
}
}
Parent.components = { Dialog };
Parent.env = makeTestEnvironment();
Parent.template = xml`
<div>
<Dialog/>
<Dialog t-if="state.showSecondDialog"/>
</div>`;
const parent = new Parent();
await parent.mount(testUtils.prepareTarget());
const frontEndModal = createCustomModal('modal');
const backEndModal = createCustomModal('modal o_technical_modal');
// querySelector will target the first modal (the static one).
const owlIndexBefore = getComputedStyle(document.querySelector('.o_dialog .modal')).zIndex;
const feZIndexBefore = getComputedStyle(frontEndModal).zIndex;
const beZIndexBefore = getComputedStyle(backEndModal).zIndex;
parent.state.showSecondDialog = false;
await testUtils.nextTick();
assert.ok(owlIndexBefore < getComputedStyle(document.querySelector('.o_dialog .modal')).zIndex,
"z-index of the owl dialog should be incremented since the active modal was destroyed");
assert.strictEqual(feZIndexBefore, getComputedStyle(frontEndModal).zIndex,
"z-index of front-end modals should not be impacted by Owl Dialog activity system");
assert.strictEqual(beZIndexBefore, getComputedStyle(backEndModal).zIndex,
"z-index of custom back-end modals should not be impacted by Owl Dialog activity system");
parent.destroy();
frontEndModal.destroy();
backEndModal.destroy();
});
});