diff --git a/addons/web/static/src/webclient/settings_form_view/settings_form_compiler.js b/addons/web/static/src/webclient/settings_form_view/settings_form_compiler.js index ea977d2fbeb..3af48890c34 100644 --- a/addons/web/static/src/webclient/settings_form_view/settings_form_compiler.js +++ b/addons/web/static/src/webclient/settings_form_view/settings_form_compiler.js @@ -175,8 +175,12 @@ function compileField(el, params) { return res; } +const labelsWeak = new WeakMap(); function compileLabel(el, params) { const res = this.compileLabel(el, params); + // It the node is a FormLabel component node, the label is + // localized *after* the field. + // We don't know yet if the label refers to a field or not. if (res.textContent && res.tagName !== "FormLabel" && params.config) { params.labels.push({ label: res.textContent.trim(), @@ -186,6 +190,7 @@ function compileLabel(el, params) { const highlight = createElement("HighlightText"); highlight.setAttribute("originalText", `\`${res.textContent}\``); append(res, highlight); + labelsWeak.set(res, { textContent: res.textContent }); res.firstChild.remove(); } return res; @@ -235,7 +240,17 @@ export class SettingsFormCompiler extends FormCompiler { ); } createLabelFromField(fieldId, fieldName, fieldString, label, params) { + const labelweak = labelsWeak.get(label); + if (labelweak) { + // Undo what we've done when we where not sure whether this label was attached to a field + // Now, we now it is. + label.textContent = labelweak.textContent; + } const res = super.createLabelFromField(fieldId, fieldName, fieldString, label, params); + if (labelweak) { + // the work of pushing the label in the search structure is already done + return res; + } let labelText = label.textContent || fieldString; labelText = labelText ? labelText : params.record.fields[fieldName].string; diff --git a/addons/web/static/tests/webclient/settings_form_view/settings_form_view_tests.js b/addons/web/static/tests/webclient/settings_form_view/settings_form_view_tests.js index 1e94ef00605..febe8d2cead 100644 --- a/addons/web/static/tests/webclient/settings_form_view/settings_form_view_tests.js +++ b/addons/web/static/tests/webclient/settings_form_view/settings_form_view_tests.js @@ -7,16 +7,26 @@ import { makeDeferred, mockTimeout, nextTick, + patchWithCleanup, } from "@web/../tests/helpers/utils"; import { editSearch } from "@web/../tests/search/helpers"; import { makeView, setupViewRegistries } from "@web/../tests/views/helpers"; import { createWebClient, doAction } from "@web/../tests/webclient/helpers"; import { registry } from "@web/core/registry"; +import { SettingsFormCompiler } from "@web/webclient/settings_form_view/settings_form_compiler"; let target; let serverData; let execTimeouts; +QUnit.assert.areEquivalent = function (template1, template2) { + if (template1.replace(/\s/g, "") === template2.replace(/\s/g, "")) { + QUnit.assert.ok(true); + } else { + QUnit.assert.strictEqual(template1, template2); + } +}; + QUnit.module("SettingsFormView", (hooks) => { hooks.beforeEach(() => { serverData = { @@ -1251,4 +1261,48 @@ QUnit.module("SettingsFormView", (hooks) => { ["xphone"] ); }); + + QUnit.test("standalone field labels with string inside a settings page", async (assert) => { + let compiled = undefined; + patchWithCleanup(SettingsFormCompiler.prototype, { + compile() { + const _compiled = this._super(...arguments); + compiled = _compiled; + return _compiled; + }, + }); + + await makeView({ + type: "form", + resModel: "res.config.settings", + serverData, + arch: ` +
`, + }); + + assert.strictEqual( + target.querySelector("label.highhopes").textContent, + "My\" little ' Label" + ); + + const expectedCompiled = ` +