diff --git a/addons/web/static/src/core/utils/render.js b/addons/web/static/src/core/utils/render.js index 34cfd9abcab..3a64c4cb377 100644 --- a/addons/web/static/src/core/utils/render.js +++ b/addons/web/static/src/core/utils/render.js @@ -4,6 +4,13 @@ import { blockDom, markup } from "@odoo/owl"; export function renderToElement(template, context = {}) { const el = render(template, context).firstElementChild; + if (el?.nextElementSibling) { + throw new Error( + `The rendered template '${template}' contains multiple root ` + + `nodes that will be ignored using renderToElement, you should ` + + `consider using renderToFragment or refactoring the template.` + ); + } el?.remove(); return el; } diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js index 8f21fc9b7f9..de02e6cad68 100644 --- a/addons/website/static/src/js/editor/snippets.options.js +++ b/addons/website/static/src/js/editor/snippets.options.js @@ -19,7 +19,7 @@ import { convertRgbToHsl, convertHslToRgb, } from '@web/core/utils/colors'; -import { renderToElement } from "@web/core/utils/render"; +import { renderToElement, renderToFragment } from "@web/core/utils/render"; const InputUserValueWidget = options.userValueWidgetsRegistry['we-input']; const SelectUserValueWidget = options.userValueWidgetsRegistry['we-select']; @@ -1676,10 +1676,7 @@ options.registry.ThemeColors = options.registry.OptionsTab.extend({ const ccPreviewEl = $(renderToElement('web_editor.color.combination.preview.legacy'))[0]; ccPreviewEl.classList.add('text-center', `o_cc${i}`, 'o_colored_level', 'o_we_collapse_toggler'); collapseEl.appendChild(ccPreviewEl); - const editionEls = $(renderToElement('website.color_combination_edition', {number: i})); - for (const el of editionEls) { - collapseEl.appendChild(el); - } + collapseEl.appendChild(renderToFragment('website.color_combination_edition', {number: i})); ccPreviewEls.push(ccPreviewEl); presetCollapseEl.appendChild(collapseEl); } diff --git a/addons/website/static/src/snippets/s_dynamic_snippet/000.js b/addons/website/static/src/snippets/s_dynamic_snippet/000.js index 27c6fb3f33c..577b7d53f11 100644 --- a/addons/website/static/src/snippets/s_dynamic_snippet/000.js +++ b/addons/website/static/src/snippets/s_dynamic_snippet/000.js @@ -3,7 +3,7 @@ import publicWidget from "@web/legacy/js/public/public_widget"; import { Markup } from "@web/legacy/js/core/utils"; import { uniqueId } from "@web/core/utils/functions"; -import { renderToElement } from "@web/core/utils/render"; +import { renderToString } from "@web/core/utils/render"; import { listenSizeChange, utils as uiUtils } from "@web/core/ui/ui_service"; const DEFAULT_NUMBER_OF_ELEMENTS = 4; const DEFAULT_NUMBER_OF_ELEMENTS_SM = 1; @@ -136,7 +136,7 @@ const DynamicSnippet = publicWidget.Widget.extend({ * @private */ _prepareContent: function () { - this.renderedContent = renderToElement( + this.renderedContent = renderToString( this.template_key, this._getQWebRenderOptions() ); diff --git a/addons/website/static/tests/tours/website_style_edition.js b/addons/website/static/tests/tours/website_style_edition.js index a9247f65980..729a929960f 100644 --- a/addons/website/static/tests/tours/website_style_edition.js +++ b/addons/website/static/tests/tours/website_style_edition.js @@ -1,22 +1,39 @@ /** @odoo-module **/ +import weUtils from "@web_editor/js/common/utils"; import wTourUtils from "@website/js/tours/tour_utils"; const TARGET_FONT_SIZE = 30; +const TARGET_BODY_BG_COLOR = '#00FF00'; +const TARGET_BODY_BG_COLOR_V2 = 'rgb(0, 255, 0)'; +const TARGET_BODY_COLOR = '#FF00FF'; +const TARGET_BODY_COLOR_V2 = 'rgb(255, 0, 255)'; const checkFontSize = function () { const style = document.defaultView.getComputedStyle(this.$anchor[0]); - if (style.fontSize !== `${TARGET_FONT_SIZE}px`) { + if (!weUtils.areCssValuesEqual(style.fontSize, `${TARGET_FONT_SIZE}px`, 'font-size', this.$anchor)) { console.error(`Expected the font-size to be equal to ${TARGET_FONT_SIZE}px but found ${style.fontSize} instead`); } }; +const checkBodyBgColor = function () { + const style = document.defaultView.getComputedStyle(this.$anchor[0]); + if (!weUtils.areCssValuesEqual(style.backgroundColor, `${TARGET_BODY_BG_COLOR}`, 'background-color', this.$anchor)) { + console.error(`Expected the background color to be equal to ${TARGET_BODY_BG_COLOR} but found ${style.backgroundColor} instead`); + } +}; +const checkBodyColor = function () { + const style = document.defaultView.getComputedStyle(this.$anchor[0]); + if (!weUtils.areCssValuesEqual(style.color, `${TARGET_BODY_COLOR}`, 'color', this.$anchor)) { + console.error(`Expected the color to be equal to ${TARGET_BODY_COLOR} but found ${style.color} instead`); + } +}; wTourUtils.registerWebsitePreviewTour("website_style_edition", { test: true, url: '/', edition: true, }, () => [ - wTourUtils.goToTheme(), +wTourUtils.goToTheme(), { content: "Change font size", trigger: '[data-variable="font-size-base"] input', @@ -28,12 +45,54 @@ wTourUtils.registerWebsitePreviewTour("website_style_edition", { // a #t=... at the end then removes the old one. extra_trigger: 'iframe html:not(:has(link[href$="web.assets_frontend.min.css"]))', run: checkFontSize, +}, { + content: "Open the color combinations area", + trigger: '.o_we_theme_presets_collapse we-toggler', +}, { + content: "Open a color combination", + trigger: '.o_we_cc_preview_wrapper', +}, { + content: "Edit the background color of that color combination", + trigger: '.o_we_theme_presets_collapse we-collapse .o_we_so_color_palette:eq(0)', +}, { + content: "Choose a color", + trigger: `.o_we_color_btn[style*="background-color:${TARGET_BODY_BG_COLOR}"]`, +}, { + content: "Check the body background color was properly adapted", + trigger: 'iframe body', + extra_trigger: ` + .o_we_theme_presets_collapse we-collapse .o_we_so_color_palette:eq(0) .o_we_color_preview[style*="${TARGET_BODY_BG_COLOR}"], + .o_we_theme_presets_collapse we-collapse .o_we_so_color_palette:eq(0) .o_we_color_preview[style*="${TARGET_BODY_BG_COLOR_V2}"] + `, + run: checkBodyBgColor, +}, { + content: "Edit the text color of that color combination", + trigger: '.o_we_theme_presets_collapse we-collapse .o_we_so_color_palette:eq(1)', +}, { + content: "Choose a color", + trigger: `.o_we_color_btn[style*="background-color:${TARGET_BODY_COLOR}"]`, +}, { + content: "Check the body color was properly adapted", + trigger: 'iframe body', + extra_trigger: ` + .o_we_theme_presets_collapse we-collapse .o_we_so_color_palette:eq(1) .o_we_color_preview[style*="${TARGET_BODY_COLOR}"], + .o_we_theme_presets_collapse we-collapse .o_we_so_color_palette:eq(1) .o_we_color_preview[style*="${TARGET_BODY_COLOR_V2}"] + `, + run: checkBodyColor, }, ...wTourUtils.clickOnSave(), { content: "Check the font size is still ok outside of edit mode", - trigger: 'iframe body:not(.editor_enable) #wrapwrap', + trigger: 'iframe body #wrapwrap', run: checkFontSize, +}, { + content: "Check the body background color is still ok outside of edit mode", + trigger: 'iframe body', + run: checkBodyBgColor, +}, { + content: "Check the body color is still ok outside of edit mode", + trigger: 'iframe body', + run: checkBodyColor, }, ...wTourUtils.clickOnEditAndWaitEditMode(), wTourUtils.goToTheme(), diff --git a/addons/website_sale_stock/static/src/js/variant_mixin.js b/addons/website_sale_stock/static/src/js/variant_mixin.js index 973ed1fece4..9225448f5a0 100644 --- a/addons/website_sale_stock/static/src/js/variant_mixin.js +++ b/addons/website_sale_stock/static/src/js/variant_mixin.js @@ -3,7 +3,7 @@ import { Markup } from "@web/legacy/js/core/utils"; import VariantMixin from "@website_sale/js/sale_variant_mixin"; import publicWidget from "@web/legacy/js/public/public_widget"; -import { renderToElement } from "@web/core/utils/render"; +import { renderToFragment } from "@web/core/utils/render"; import "@website_sale/js/website_sale"; @@ -65,11 +65,10 @@ VariantMixin._onChangeCombinationStock = function (ev, $parent, combination) { .remove(); combination.has_out_of_stock_message = $(combination.out_of_stock_message).text() !== ''; combination.out_of_stock_message = Markup(combination.out_of_stock_message); - const $message = $(renderToElement( + $('div.availability_messages').append(renderToFragment( 'website_sale_stock.product_availability', combination )); - $('div.availability_messages').html($message); }; publicWidget.registry.WebsiteSale.include({