[FIX] mail: show translations for emails

Due to shadowRoot, emails were not properly translated.

closes odoo/odoo#140847

Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
This commit is contained in:
Sébastien Theys
2023-11-03 00:49:06 +00:00
parent 3bf990d0b0
commit 06603e9dc7
3 changed files with 69 additions and 8 deletions
+26 -7
View File
@@ -100,6 +100,8 @@ export class Message extends Component {
emailHeaderOpen: false,
showTranslation: false,
});
/** @type {ShadowRoot} */
this.shadowRoot;
this.root = useRef("root");
this.hasTouch = hasTouch;
this.messageBody = useRef("body");
@@ -136,11 +138,7 @@ export class Message extends Component {
this.prepareMessageBody(this.messageBody.el);
}
if (this.shadowBody.el) {
const shadowRoot = this.shadowBody.el.attachShadow({ mode: "open" });
const body = document.createElement("span");
body.innerHTML =
this.props.messageSearch?.highlight(this.message.body) ?? this.message.body;
this.prepareMessageBody(body);
this.shadowRoot = this.shadowBody.el.attachShadow({ mode: "open" });
const color = cookie.get("color_scheme") === "dark" ? "white" : "black";
const shadowStyle = document.createElement("style");
shadowStyle.innerHTML = `
@@ -159,11 +157,32 @@ export class Message extends Component {
}
`;
if (cookie.get("color_scheme") === "dark") {
shadowRoot.appendChild(shadowStyle);
this.shadowRoot.appendChild(shadowStyle);
}
shadowRoot.appendChild(body);
}
});
useEffect(
() => {
if (this.shadowBody.el) {
const body = document.createElement("span");
body.innerHTML = this.state.showTranslation
? this.message.translationValue
: this.props.messageSearch?.highlight(this.message.body) ??
this.message.body;
this.prepareMessageBody(body);
this.shadowRoot.appendChild(body);
return () => {
this.shadowRoot.removeChild(body);
};
}
},
() => [
this.state.showTranslation,
this.message.translationValue,
this.props.messageSearch?.searchTerm,
this.message.body,
]
);
}
get attClass() {
@@ -46,3 +46,42 @@ QUnit.test("Toggle display of original/translated version of chatter message", a
// The translation button should not trigger more than one external request for a single message.
assert.verifySteps(["Request"]);
});
QUnit.test("translation of email message", async () => {
const pyEnv = await startServer();
const partnerId = pyEnv["res.partner"].create({});
pyEnv["mail.message"].create({
model: "res.partner",
body: "Al mal tiempo, buena cara.",
message_type: "email",
author_id: partnerId,
res_id: partnerId,
});
const { openFormView } = await start({
mockRPC(route) {
if (route === "/mail/message/translate") {
return { body: "To bad weather, good face.", lang_name: "Spanish", error: null };
}
},
});
await openFormView("res.partner", partnerId);
await contains("span", {
text: "Al mal tiempo, buena cara.",
parent: [".o-mail-Message-body > div", { shadowRoot: true }],
});
await click("button[title='Expand']");
await click("span[title='Translate']");
await contains("span", {
text: "To bad weather, good face.",
parent: [".o-mail-Message-body > div", { shadowRoot: true }],
});
await contains(".o-mail-Message-body", {
text: "(Translated from: Spanish)",
});
await click("button[title='Expand']");
await click("span[title='Revert']");
await contains("span", {
text: "Al mal tiempo, buena cara.",
parent: [".o-mail-Message-body > div", { shadowRoot: true }],
});
});
+4 -1
View File
@@ -226,6 +226,7 @@ if (window.QUnit) {
* Note: when using one of the scrollTop options, it is advised to ensure the height is not going
* to change soon, by checking with a preceding contains that all the expected elements are in DOM.
* @property {boolean} [setFocus] if provided, focuses the first found element.
* @property {boolean} [shadowRoot] if provided, targets the shadowRoot of the found elements.
* @property {number|"bottom"} [setScroll] if provided, sets the scrollTop on the first found
* element.
* @property {HTMLElement} [target=getFixture()]
@@ -521,7 +522,9 @@ class Contains {
if (!target) {
return;
}
const baseRes = [...target.querySelectorAll(this.selector)];
const baseRes = [...target.querySelectorAll(this.selector)]
.map((el) => (this.options.shadowRoot ? el.shadowRoot : el))
.filter((el) => el);
/** @type {Contains[]} */
this.childrenContains = [];
const res = baseRes.filter((el, currentIndex) => {