[FIX] website, *: always allow scroll for popup taller than window

*: web_editor

Steps to reproduce:
- Activate the cookie dialog from the website settings
- Remove the backdrop from the cookie dialog
- Add some blocks to the cookie dialog in the website editor
until the dialog height is bigger than the page height.

Result:
The cookie dialog is not scrollable, so it's impossible to reach
the "I agree" button and close the dialog, leaving the user stuck
at the first page.

I check if the popup content is higher than the window. If it is, I
give priority to the popup scroll over the page one. I had to
overwrite the _showElement method to call _setScrollbar after
the content rendering, in this way I can get its correct height.
I also try to update the scrollbar each time a block in the cookie bar
has been changed and each time the window resize. This is optimize, so
if the overflow isn't changed from the last time I tried to update the
scrollbar no action will be taken. In this way you can always see the
whole popup without refreshing the page.

Also I have to set 'pointer-events' to none only if the popup is
smaller than the page itself (to interact with and scroll the
page behind), otherwise the popup won't be scrollable. I did
this by adding a class "s_popup_overflow_page" if the popup is
higher than the page and I adapted the pointer-events
property accordingly.

opw-2660786

closes odoo/odoo#83691

X-original-commit: 022a4486a99d855a3c6e2cef326bb3b0c5e66db9
Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com>
This commit is contained in:
Federico (fega)
2022-01-31 17:47:52 +00:00
parent 6f76d9c5fb
commit 97c8cf3ac2
4 changed files with 62 additions and 5 deletions
@@ -2643,6 +2643,11 @@ var SnippetsMenu = Widget.extend({
* @return {Promise}
*/
async _scrollToSnippet($el, $scrollable) {
// Don't scroll if $el is added to a visible popup that does not fill
// the page (otherwise the page would scroll to a random location).
if ($el.closest('div.modal:not(.s_popup_overflow_page)').length) {
return;
}
return dom.scrollTo($el[0], {extraOffset: 50, $scrollable: $scrollable});
},
/**
@@ -117,22 +117,63 @@ const PopupWidget = publicWidget.Widget.extend({
publicWidget.registry.popup = PopupWidget;
// Try to update the scrollbar based on the current context (modal state)
// and only if the modal overflowing has changed
function _updateScrollbar(ev) {
const context = ev.data;
const modalContent = context._element.querySelector('.modal-content');
const currentOverflow = modalContent.offsetHeight >= window.innerHeight;
if (modalContent && context._isOverflowingWindow !== currentOverflow) {
context._isOverflowingWindow = currentOverflow;
context._checkScrollbar();
context._setScrollbar();
if (context._element.classList.contains('s_popup_overflow_page')) {
$(document.body).addClass('modal-open');
} else {
$(document.body).removeClass('modal-open');
context._resetScrollbar();
}
}
}
// Prevent bootstrap to prevent scrolling and to add the strange body
// padding-right they add if the popup does not use a backdrop (especially
// important for default cookie bar).
const _baseShowElement = $.fn.modal.Constructor.prototype._showElement;
$.fn.modal.Constructor.prototype._showElement = function () {
_baseShowElement.apply(this, arguments);
// Update the scrollbar if the content changes or if the window has been
// resized
$(this._element).on('content_changed.update_scrollbar', this, _updateScrollbar);
$(window).on('resize.update_scrollbar', this, _updateScrollbar);
_updateScrollbar({ data: this });
};
const _baseHideModal = $.fn.modal.Constructor.prototype._hideModal;
$.fn.modal.Constructor.prototype._hideModal = function () {
_baseHideModal.apply(this, arguments);
$(this._element).off('content_changed.update_scrollbar');
$(window).off('resize.update_scrollbar');
};
const _baseSetScrollbar = $.fn.modal.Constructor.prototype._setScrollbar;
$.fn.modal.Constructor.prototype._setScrollbar = function () {
if (this._element.classList.contains('s_popup_no_backdrop')) {
if (this._element.classList.contains('s_popup_no_backdrop') && !this._isOverflowingWindow) {
this._element.classList.remove('s_popup_overflow_page');
return;
}
return _baseSetScrollbar.apply(this, ...arguments);
this._element.classList.add('s_popup_overflow_page');
return _baseSetScrollbar.apply(this, arguments);
};
const _baseGetScrollbarWidth = $.fn.modal.Constructor.prototype._getScrollbarWidth;
$.fn.modal.Constructor.prototype._getScrollbarWidth = function () {
if (this._element.classList.contains('s_popup_no_backdrop')) {
if (this._element.classList.contains('s_popup_no_backdrop') && !this._isOverflowingWindow) {
return 0;
}
return _baseGetScrollbarWidth.apply(this, ...arguments);
return _baseGetScrollbarWidth.apply(this, arguments);
};
return PopupWidget;
@@ -63,7 +63,12 @@
// No backdrop
.s_popup_no_backdrop {
pointer-events: none;
// If the popup is taller than the page you should not be able to
// interact with the elements behind, otherwise you should (when there
// is no backdrop).
&:not(.s_popup_overflow_page) {
pointer-events: none;
}
.modal-content {
pointer-events: auto;
@@ -79,6 +79,12 @@ options.registry.SnippetPopup = options.Class.extend({
this.$target.modal('hide');
});
},
/**
* @override
*/
cleanForSave: function () {
this.$target.removeClass("s_popup_overflow_page");
},
//--------------------------------------------------------------------------
// Options