[FIX] mass_mailing: prevent overriding font awesome font-family

When changing a font-family style in the design tab, we need to make
sure we don't override the font-family of font awesome icons or it will
break them. This takes font-family out of its selector and applies
it to special selectors that exclude `.fa`.

task-2821155

X-original-commit: d25b38969d1d0f2f791ad33f2591ea4f3b7b5ed8
Part-of: odoo/odoo#92138
This commit is contained in:
Antoine Guenet
2022-05-24 12:48:18 +02:00
parent 3eaba29e2b
commit 417fc95b72
2 changed files with 43 additions and 17 deletions
@@ -33,6 +33,16 @@ export const PRIORITY_STYLES = {
export const RE_CSS_TEXT_MATCH = /([^{]+)([^}]+)/;
export const RE_SELECTOR_ENDS_WITH_GT_STAR = />\s*\*\s*$/;
export const transformFontFamilySelector = selector => {
if (selector.trim().endsWith(':not(.fa)')) {
return [selector];
}
if (!selector.endsWith('*')) {
return [`${selector.trim()}:not(.fa)`, `${selector.trim()} :not(.fa)`];
} else if (RE_SELECTOR_ENDS_WITH_GT_STAR.test(selector)) {
return [`${selector.replace(RE_SELECTOR_ENDS_WITH_GT_STAR, '').trim()} :not(.fa)`];
}
}
/**
* Take a css text and splits each comma-separated selector into separate
* styles, applying the css prefix to each. Return the modified css text.
@@ -47,16 +57,32 @@ export const splitCss = css => {
document.head.appendChild(styleElement);
const rules = [...styleElement.sheet.cssRules];
styleElement.remove();
const result = rules.map(rule => {
const [, fullSelector, styles] = rule.cssText.match(RE_CSS_TEXT_MATCH);
return fullSelector.split(',').map(selector => {
const stylesToWrite = {};
for (const rule of rules) {
const styles = rule.style;
for (let selector of rule.selectorText.split(',')) {
if (!selector.trim().startsWith(CSS_PREFIX)) {
selector = `${CSS_PREFIX} ${selector.trim()}`;
}
return `${selector.trim()} {${styles.replace('{', '').trim()}}`;
}).join('');
}).join('');
return result;
for (const style of rule.style) {
let selectors = [selector];
if (style === 'font-family') {
// Ensure font-family gets passed to all descendants and never
// overwrite font awesome.
selectors = transformFontFamilySelector(selector);
}
for (const selectorToWriteTo of selectors) {
if (!stylesToWrite[selectorToWriteTo]) {
stylesToWrite[selectorToWriteTo] = [];
}
stylesToWrite[selectorToWriteTo].push([style, styles[style]]);
}
}
}
}
return Object.entries(stylesToWrite).map(([selector, styles]) => (
`${selector.trim()} {\n${styles.map(([styleName, style]) => ` ${styleName}: ${style};`).join('\n')}\n}`
)).join('\n');
};
export const getFontName = fontFamily => fontFamily.split(',')[0].replace(/"/g, '').replace(/([a-z])([A-Z])/g, (v, a, b) => `${a} ${b}`).trim();
export const normalizeFontFamily = fontFamily => fontFamily.replace(/"/g, '').replace(/, /g, ',');
@@ -90,4 +116,5 @@ export default {
CSS_PREFIX, BTN_SIZE_STYLES, DEFAULT_BUTTON_SIZE, PRIORITY_STYLES,
RE_CSS_TEXT_MATCH, FONT_FAMILIES, RE_SELECTOR_ENDS_WITH_GT_STAR,
splitCss, getFontName, normalizeFontFamily, initializeDesignTabCss,
transformFontFamilySelector,
}
@@ -6,9 +6,10 @@ const {ColorpickerWidget} = require('web.Colorpicker');
const SelectUserValueWidget = options.userValueWidgetsRegistry['we-select'];
const weUtils = require('web_editor.utils');
const {
CSS_PREFIX, BTN_SIZE_STYLES, RE_SELECTOR_ENDS_WITH_GT_STAR,
CSS_PREFIX, BTN_SIZE_STYLES,
DEFAULT_BUTTON_SIZE, PRIORITY_STYLES, FONT_FAMILIES,
getFontName, normalizeFontFamily, initializeDesignTabCss
getFontName, normalizeFontFamily, initializeDesignTabCss,
transformFontFamilySelector,
} = require('mass_mailing.design_constants');
@@ -237,18 +238,16 @@ options.registry.DesignTab = options.Class.extend({
if (params.cssProperty.includes('color')) {
value = weUtils.normalizeColor(value);
}
const selectors = this._getSelectors(params.selectorText);
let selectors = this._getSelectors(params.selectorText);
const firstSelector = selectors[0].replace(CSS_PREFIX, '').trim();
if (params.cssProperty === 'font-family') {
// Ensure font-family gets passed to all descendants.
selectors.push(...selectors.map(selector => selector + ' *'));
// Ensure font-family gets passed to all descendants and never
// overwrite font awesome.
const newSelectors = [];
for (const selector of selectors) {
if (!selector.endsWith('*')) {
selectors.push(`${selector} *`);
} else if (RE_SELECTOR_ENDS_WITH_GT_STAR.test(selector)) {
selectors.push(selector.replace(RE_SELECTOR_ENDS_WITH_GT_STAR, ' *'));
}
newSelectors.push(...transformFontFamilySelector(selector));
}
selectors = [...new Set(newSelectors)];
}
for (const selector of selectors) {
const priority = PRIORITY_STYLES[firstSelector].includes(params.cssProperty) ? ' !important' : '';