From 97c8cf3ac263884b3eeb9862b84a426162d9794e Mon Sep 17 00:00:00 2001 From: "Federico (fega)" Date: Tue, 30 Nov 2021 14:52:35 +0000 Subject: [PATCH] [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) --- .../static/src/js/editor/snippets.editor.js | 5 ++ .../static/src/snippets/s_popup/000.js | 49 +++++++++++++++++-- .../static/src/snippets/s_popup/001.scss | 7 ++- .../static/src/snippets/s_popup/options.js | 6 +++ 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js index 2ca099a70ef..93f4aadd5a2 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -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}); }, /** diff --git a/addons/website/static/src/snippets/s_popup/000.js b/addons/website/static/src/snippets/s_popup/000.js index cb8b2fa3e9c..ba6194ed02d 100644 --- a/addons/website/static/src/snippets/s_popup/000.js +++ b/addons/website/static/src/snippets/s_popup/000.js @@ -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; diff --git a/addons/website/static/src/snippets/s_popup/001.scss b/addons/website/static/src/snippets/s_popup/001.scss index f8ae94ca632..a8200adf4e9 100644 --- a/addons/website/static/src/snippets/s_popup/001.scss +++ b/addons/website/static/src/snippets/s_popup/001.scss @@ -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; diff --git a/addons/website/static/src/snippets/s_popup/options.js b/addons/website/static/src/snippets/s_popup/options.js index cf0dfca6041..722c3ada212 100644 --- a/addons/website/static/src/snippets/s_popup/options.js +++ b/addons/website/static/src/snippets/s_popup/options.js @@ -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