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 de5886ea9c4..d4a5808f645 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -1785,6 +1785,7 @@ var SnippetsMenu = Widget.extend({ 'request_editable': '_onRequestEditable', 'disable_loading_effect': '_onDisableLoadingEffect', 'enable_loading_effect': '_onEnableLoadingEffect', + "update_invisible_dom": "_onUpdateInvisibleDom", }, // enum of the SnippetsMenu's tabs. tabs: { @@ -3769,6 +3770,14 @@ var SnippetsMenu = Widget.extend({ */ _onSnippetDragAndDropStop: async function (ev) { this.snippetEditorDragging = false; + const visibleConditionalEls = []; + for (const snippetEditor of this.snippetEditors) { + const targetEl = snippetEditor.$target[0]; + if (targetEl.dataset["visibility"] === "conditional" && + !targetEl.classList.contains("o_conditional_hidden")) { + visibleConditionalEls.push(targetEl); + } + } const modalEl = ev.data.$snippet[0].closest('.modal'); const carouselItemEl = ev.data.$snippet[0].closest('.carousel-item'); // If the snippet is in a modal, destroy editors only in that modal. @@ -3778,6 +3787,16 @@ var SnippetsMenu = Widget.extend({ // first slide. await this._destroyEditors(carouselItemEl ? $(carouselItemEl) : modalEl ? $(modalEl) : null); await this._activateSnippet(ev.data.$snippet); + // Because of _destroyEditors(), all the snippets with a conditional + // visibility are hidden. Show the ones that were visible before the + // drag and drop. + for (const visibleConditionalEl of visibleConditionalEls) { + visibleConditionalEl.classList.remove("o_conditional_hidden"); + delete visibleConditionalEl.dataset["invisible"]; + } + // Update the "Invisible Elements" panel as the order of invisible + // snippets could have changed on the page. + await this._updateInvisibleDOM(); }, /** * Returns the droppable snippet from which a dropped snippet originates. @@ -4234,6 +4253,14 @@ var SnippetsMenu = Widget.extend({ _onSnippetSearchResetClick: function () { this._filterSnippets(''); }, + /** + * Called when a child editor asks to update the "Invisible Elements" panel. + * + * @private + */ + async _onUpdateInvisibleDom() { + await this._updateInvisibleDOM(); + }, _addToolbar(toolbarMode = "text") { if (this.folded) { return; diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index cbf58815c7e..a25fdce175a 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -5426,6 +5426,9 @@ registry.SnippetMove = SnippetOptionWidget.extend({ optionName: 'StepsConnector', name: 'move_snippet', }); + // Update the "Invisible Elements" panel as the order of invisible + // snippets could have changed on the page. + this.trigger_up("update_invisible_dom"); }, }); diff --git a/addons/website/static/tests/tours/conditional_visibility.js b/addons/website/static/tests/tours/conditional_visibility.js index de27c61aba7..2a70251bdb4 100644 --- a/addons/website/static/tests/tours/conditional_visibility.js +++ b/addons/website/static/tests/tours/conditional_visibility.js @@ -28,6 +28,18 @@ function checkEyeIcon(snippetName, visible) { run: () => {}, // it is a check }; } +function checkEyesIconAfterSave(footerIsHidden = true) { + const eyeIconChecks = [ + checkEyeIcon("Header", false), + checkEyeIcon("Text - Image", true), + checkEyeIcon("Popup", false), + checkEyeIcon("Banner", true), + ]; + if (footerIsHidden) { + eyeIconChecks.push(checkEyeIcon("Footer", false)); + } + return eyeIconChecks; +} wTourUtils.registerWebsitePreviewTour('conditional_visibility_1', { edition: true, url: '/', @@ -123,9 +135,47 @@ checkEyeIcon("Header", false), checkEyeIcon("Banner", false), ...wTourUtils.clickOnSave(), ...wTourUtils.clickOnEditAndWaitEditMode(), -checkEyeIcon("Header", false), -checkEyeIcon("Text - Image", true), -checkEyeIcon("Popup", false), -checkEyeIcon("Banner", true), -checkEyeIcon("Footer", false), +...checkEyesIconAfterSave(), +]); + +wTourUtils.registerWebsitePreviewTour("conditional_visibility_4", { + edition: true, + test: true, + url: "/", +}, +[ +// Click on the "Text-Image" snippet. +wTourUtils.clickOnSnippet(snippets[0]), +{ + content: "Click on the 'move down' option", + trigger: "iframe we-button.o_we_user_value_widget.fa-angle-down", +}, +...checkEyesIconAfterSave(), +{ + content: "Check the order on the 'Invisible Elements' panel", + trigger: ".o_we_invisible_el_panel div:nth-child(3):contains('Banner')", + run: () => {}, // it is a check +}, +{ + content: "Toggle the visibility of the Footer", + trigger: ".o_we_invisible_el_panel .o_we_invisible_entry:contains('Footer')", +}, +{ + content: "Check that the footer is visible", + trigger: "iframe #wrapwrap footer", + run: () => {}, // it is a check +}, +// Click on the "Banner" snippet. +wTourUtils.clickOnSnippet(snippets[1]), +{ + content: "Drag the 'Banner' snippet to the end of the page", + trigger: "iframe .o_overlay_move_options .ui-draggable-handle", + run: "drag_and_drop iframe #wrapwrap footer", +}, +...checkEyesIconAfterSave(false), +{ + content: "Check the order on the 'Invisible Elements' panel", + trigger: ".o_we_invisible_el_panel div:nth-child(3):contains('Text - Image')", + run: () => {}, // it is a check +}, ]); diff --git a/addons/website/tests/test_ui.py b/addons/website/tests/test_ui.py index b4bfa4e6eb7..fd54f1c0171 100644 --- a/addons/website/tests/test_ui.py +++ b/addons/website/tests/test_ui.py @@ -289,6 +289,7 @@ class TestUi(odoo.tests.HttpCase): self.start_tour(self.env['website'].get_client_action_url('/'), 'conditional_visibility_1', login='admin') self.start_tour('/web', 'conditional_visibility_2', login='admin') self.start_tour(self.env['website'].get_client_action_url('/'), 'conditional_visibility_3', login='admin') + self.start_tour(self.env['website'].get_client_action_url('/'), 'conditional_visibility_4', login='admin') def test_11_website_snippet_background_edition(self): self.env['ir.attachment'].create({