diff --git a/addons/web_editor/static/src/js/backend/convert_inline.js b/addons/web_editor/static/src/js/backend/convert_inline.js index fdaf3d9d7f0..2172d7731c9 100644 --- a/addons/web_editor/static/src/js/backend/convert_inline.js +++ b/addons/web_editor/static/src/js/backend/convert_inline.js @@ -23,9 +23,10 @@ const FONT_PROPERTIES_TO_INHERIT = [ 'font-style', 'text-decoration', 'text-transform', + 'text-align', ]; // Attributes all tables should have in a mailing. -const TABLE_ATTRIBUTES = { +export const TABLE_ATTRIBUTES = { cellspacing: 0, cellpadding: 0, border: 0, @@ -34,11 +35,11 @@ const TABLE_ATTRIBUTES = { role: 'presentation', }; // Cancel tables default styles. -const TABLE_STYLES = { +export const TABLE_STYLES = { 'border-collapse': 'collapse', 'text-align': 'inherit', 'font-size': 'unset', - 'line-height': 'unset', + 'line-height': 'inherit', }; //-------------------------------------------------------------------------- @@ -450,11 +451,24 @@ function classToStyle($editable, cssRules) { // Compute dynamic styles (var, calc). writes.push(() => { let computedStyle; - for (let styleName of node.style) { + for (const styleName of node.style) { const styleValue = node.style.getPropertyValue(styleName); if (styleValue.includes('var(') || styleValue.includes('calc(')) { computedStyle = computedStyle || getComputedStyle(node); - node.style.setProperty(styleName, computedStyle[styleName]); + const prop = styleValue.includes('var(') ? styleValue.replace(/var\((.*)\)/, '$1') : styleName; + const value = computedStyle.getPropertyValue(prop) || computedStyle.getPropertyValue(styleName); + node.style.setProperty(styleName, value); + } + } + }); + + // Fix inheritance of font properties on Outlook. + writes.push(() => { + const propsToConvert = FONT_PROPERTIES_TO_INHERIT.filter(prop => node.style[prop] === 'inherit'); + if (propsToConvert.length) { + const computedStyle = getComputedStyle(node); + for (const prop of propsToConvert) { + node.style.setProperty(prop, computedStyle[prop]); } } }); @@ -668,7 +682,6 @@ async function toInline($editable, cssRules, $iframe) { listGroupToTable(editable); addTables($editable); handleMasonry(editable); - normalizeColors($editable); const rootFontSizeProperty = getComputedStyle(editable.ownerDocument.documentElement).fontSize; const rootFontSize = parseFloat(rootFontSizeProperty.replace(/[^\d\.]/g, '')); normalizeRem($editable, rootFontSize); @@ -676,6 +689,7 @@ async function toInline($editable, cssRules, $iframe) { enforceTablesResponsivity(editable); flattenBackgroundImages(editable); formatTables($editable); + normalizeColors($editable); responsiveToStaticForOutlook(editable); // Fix Outlook image rendering bug. for (const attributeName of ['width', 'height']) { @@ -936,15 +950,18 @@ function formatTables($editable) { } } } - // Tables don't properly inherit alignments from their ancestors in Outlook. + // Tables don't properly inherit certain styles from their ancestors in Outlook. for (const table of editable.querySelectorAll('table')) { - if (table.style.textAlign === 'inherit') { - let ancestor = table; - while (ancestor && (!ancestor.style.textAlign || ancestor.style.textAlign === 'inherit')) { - ancestor = ancestor.parentElement; - } - if (ancestor) { - table.style.setProperty('text-align', ancestor.style.textAlign); + const propsToConvert = FONT_PROPERTIES_TO_INHERIT.filter(prop => table.style[prop] === 'inherit' || !table.style[prop]); + if (propsToConvert.length) { + for (const prop of propsToConvert) { + let ancestor = table; + while (ancestor && (!ancestor.style[prop] || ancestor.style[prop] === 'inherit')) { + ancestor = ancestor.parentElement; + } + if (ancestor) { + table.style.setProperty(prop, ancestor.style[prop]); + } } } } diff --git a/addons/web_editor/static/tests/convert_inline_tests.js b/addons/web_editor/static/tests/convert_inline_tests.js index ca50c37d748..8455ef76bdd 100644 --- a/addons/web_editor/static/tests/convert_inline_tests.js +++ b/addons/web_editor/static/tests/convert_inline_tests.js @@ -308,21 +308,21 @@ QUnit.module('convert_inline', {}, function () { `` + `` + + `font-size: unset; line-height: inherit;\">` + `` + `
HEADER
`) .replace(/]*>\(1, 0\)<\/td>/, `` + `` + + `font-size: unset; line-height: inherit;\">` + `` + `

TITLE

BODY
`) .replace(/]*>\(2, 0\)<\/td>/, `` + `` + + `font-size: unset; line-height: inherit;\">` + `` + `
`), }); @@ -603,7 +603,7 @@ QUnit.module('convert_inline', {}, function () { assert.strictEqual($editable.html(), getRegularTableHtml(1, 1, 12, 100) .split('style=').join('class="o_layout" style=') - .replace(' font-size: unset; line-height: unset;', '') // o_layout keeps those default values + .replace(' font-size: unset; line-height: inherit;', '') // o_layout keeps those default values .replace(/]*>\(0, 0\)/, '' + getRegularTableHtml(1, 1, 12, 100).replace(/]*>\(0, 0\)/, '
Mailing
')), "should have converted .o_layout to a special table structure with a table in it" ); @@ -617,7 +617,7 @@ QUnit.module('convert_inline', {}, function () { assert.strictEqual($editable.html(), getRegularTableHtml(1, 1, 12, 100) .split('style=').join('class="o_layout" style=') - .replace(' font-size: unset; line-height: unset;', '') // o_layout keeps those default values + .replace(' font-size: unset; line-height: inherit;', '') // o_layout keeps those default values .replace(/]*>\(0, 0\)/, '
Mailing
'), "should have converted .o_layout to a special table structure, keeping the table in it" ); diff --git a/addons/web_editor/static/tests/test_utils.js b/addons/web_editor/static/tests/test_utils.js index 66dc4683560..8e7df76622b 100644 --- a/addons/web_editor/static/tests/test_utils.js +++ b/addons/web_editor/static/tests/test_utils.js @@ -8,6 +8,7 @@ import Widget from "web.Widget"; import Wysiwyg from "web_editor.wysiwyg"; import options from "web_editor.snippets.options"; import { range } from "@web/core/utils/numbers"; +import { TABLE_ATTRIBUTES, TABLE_STYLES } from '@web_editor/js/backend/convert_inline'; const COLOR_PICKER_TEMPLATE = ` @@ -727,22 +728,8 @@ var textInput = function (target, char) { // Convert Inline //-------------------------------------------------------------------------- -const tableAttributes = { - cellspacing: 0, - cellpadding: 0, - border: 0, - width: '100%', - align: 'center', - role: 'presentation', -}; -const tableAttributesString = Object.keys(tableAttributes).map(key => `${key}="${tableAttributes[key]}"`).join(' '); -const tableStyles = { - 'border-collapse': 'collapse', - 'text-align': 'inherit', - 'font-size': 'unset', - 'line-height': 'unset', -}; -const tableStylesString = Object.keys(tableStyles).map(key => `${key}: ${tableStyles[key]};`).join(' '); +const tableAttributesString = Object.keys(TABLE_ATTRIBUTES).map(key => `${key}="${TABLE_ATTRIBUTES[key]}"`).join(' '); +const tableStylesString = Object.keys(TABLE_STYLES).map(key => `${key}: ${TABLE_STYLES[key]};`).join(' '); /** * Take a matrix representing a grid and return an HTML string of the Bootstrap * grid. The matrix is an array of rows, with each row being an array of cells.