[FIX] web_editor: properly inline and inherit body line-height from var

Before this commit, the line-height of the body was not properly
inherited where it should. It is defined as a CSS variable and its value
was incorrectly retrieved (if the value is 1.5 and the font-size is
10px, `getComputedStyle` returns 15px instead of 1.5). Other properties
related to fonts were likewise improperly inherited.

X-original-commit: 12ae698bd9aff8466d69819e0b849752f967c126
Part-of: odoo/odoo#124465
This commit is contained in:
Antoine Guenet
2023-06-09 17:01:41 +02:00
parent a61d9f3c26
commit 18b172e821
3 changed files with 39 additions and 35 deletions
@@ -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]);
}
}
}
}
@@ -308,21 +308,21 @@ QUnit.module('convert_inline', {}, function () {
`<td>` +
`<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" align=\"center\" ` +
`role=\"presentation\" style=\"width: 100% !important; border-collapse: collapse; text-align: inherit; ` +
`font-size: unset; line-height: unset;\"><tr>` +
`font-size: unset; line-height: inherit;\"><tr>` +
`<td class="card-header"><span>HEADER</span></td>` +
`</tr></table></td>`)
.replace(/<td[^>]*>\(1, 0\)<\/td>/,
`<td>` +
`<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" align=\"center\" ` +
`role=\"presentation\" style=\"width: 100% !important; border-collapse: collapse; text-align: inherit; ` +
`font-size: unset; line-height: unset;\"><tr>` +
`font-size: unset; line-height: inherit;\"><tr>` +
`<td class="card-body"><h2 class="card-title">TITLE</h2><small>BODY <img></small></td>` +
`</tr></table></td>`)
.replace(/<td[^>]*>\(2, 0\)<\/td>/,
`<td>` +
`<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" width=\"100%\" align=\"center\" ` +
`role=\"presentation\" style=\"width: 100% !important; border-collapse: collapse; text-align: inherit; ` +
`font-size: unset; line-height: unset;\"><tr>` +
`font-size: unset; line-height: inherit;\"><tr>` +
`<td class="card-footer"><a href="#" class="btn">FOOTER</a></td>` +
`</tr></table></td>`),
});
@@ -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(/<td[^>]*>\(0, 0\)/, '<td>' + getRegularTableHtml(1, 1, 12, 100).replace(/<td[^>]*>\(0, 0\)/, '<td><div>Mailing</div>')),
"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(/<td[^>]*>\(0, 0\)/, '<td><table><tbody><tr><td>Mailing</td></tr></tbody></table>'),
"should have converted .o_layout to a special table structure, keeping the table in it"
);
+3 -16
View File
@@ -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 = `
<colorpicker>
@@ -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.