Skip to content

Commit 7233382

Browse files
authored
Merge pull request #26 from pendo-io/ajax-fix-chrome-grid-template
Fix chrome grid template
2 parents ec7d7e6 + 2f4c2e7 commit 7233382

File tree

3 files changed

+141
-4
lines changed

3 files changed

+141
-4
lines changed

.changeset/clean-plants-look.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"rrweb-snapshot": patch
3+
---
4+
5+
Fix issue with chrome improperly parsing grid-template-areas to grid-template shorthand.

packages/rrweb-snapshot/src/utils.ts

+42-4
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,14 @@ export function stringifyRule(rule: CSSRule, sheetHref: string | null): string {
177177
return importStringified;
178178
} else {
179179
let ruleStringified = rule.cssText;
180-
if (isCSSStyleRule(rule) && rule.selectorText.includes(':')) {
181-
// Safari does not escape selectors with : properly
182-
// see https://bugs.webkit.org/show_bug.cgi?id=184604
183-
ruleStringified = fixSafariColons(ruleStringified);
180+
if (isCSSStyleRule(rule)) {
181+
ruleStringified = replaceChromeGridTemplateAreas(rule);
182+
183+
if (rule.selectorText.includes(':')) {
184+
// Safari does not escape selectors with : properly
185+
// see https://bugs.webkit.org/show_bug.cgi?id=184604
186+
ruleStringified = fixSafariColons(ruleStringified);
187+
}
184188
}
185189
if (sheetHref) {
186190
return absolutifyURLs(ruleStringified, sheetHref);
@@ -189,6 +193,40 @@ export function stringifyRule(rule: CSSRule, sheetHref: string | null): string {
189193
}
190194
}
191195

196+
export function replaceChromeGridTemplateAreas(rule: CSSStyleRule): string {
197+
// chrome does not correctly provide the grid-template-areas in the rule.cssText
198+
// when it parses them to grid-template short-hand syntax
199+
// e.g. https://bugs.chromium.org/p/chromium/issues/detail?id=1303968
200+
// so, we manually rebuild the cssText using rule.style when
201+
// we find the cssText contains grid-template:, rule.style contains grid-template-areas, but
202+
// cssText does not include grid-template-areas
203+
const hasGridTemplateInCSSText = rule.cssText.includes('grid-template:');
204+
const hasGridTemplateAreaInStyleRules =
205+
rule.style.getPropertyValue('grid-template-areas') !== '';
206+
const hasGridTemplateAreaInCSSText = rule.cssText.includes(
207+
'grid-template-areas:',
208+
);
209+
210+
if (
211+
hasGridTemplateInCSSText &&
212+
hasGridTemplateAreaInStyleRules &&
213+
!hasGridTemplateAreaInCSSText
214+
) {
215+
const styleDeclarations = [];
216+
217+
for (let i = 0; i < rule.style.length; i++) {
218+
const styleName = rule.style[i];
219+
const styleValue = rule.style.getPropertyValue(styleName);
220+
221+
styleDeclarations.push(`${styleName}: ${styleValue}`);
222+
}
223+
224+
return `${rule.selectorText} { ${styleDeclarations.join('; ')}; }`;
225+
}
226+
227+
return rule.cssText;
228+
}
229+
192230
export function fixSafariColons(cssStringified: string): string {
193231
// Replace e.g. [aa:bb] with [aa\\:bb]
194232
const regex = /(\[(?:[\w-]+)[^\\])(:(?:[\w-]+)\])/gm;

packages/rrweb-snapshot/test/utils.test.ts

+94
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { NodeType, serializedNode } from '../src/types';
66
import {
77
escapeImportStatement,
88
extractFileExtension,
9+
replaceChromeGridTemplateAreas,
910
fixSafariColons,
1011
isNodeMetaEqual,
1112
} from '../src/utils';
@@ -268,6 +269,99 @@ describe('utils', () => {
268269
expect(out5).toEqual(`@import url("/foo.css;900;800\\"") layer;`);
269270
});
270271
});
272+
273+
describe('replaceChromeGridTemplateAreas', () => {
274+
it('does not alter corectly parsed grid template rules', () => {
275+
const cssText =
276+
'#wrapper { display: grid; width: 100%; height: 100%; grid-template: minmax(2, 1fr); margin: 0px auto; }';
277+
const mockCssRule = {
278+
cssText,
279+
selectorText: '#wrapper',
280+
style: {
281+
getPropertyValue(prop) {
282+
return {
283+
'grid-template-areas': '',
284+
}[prop];
285+
},
286+
},
287+
} as Partial<CSSStyleRule> as CSSStyleRule;
288+
289+
expect(replaceChromeGridTemplateAreas(mockCssRule)).toEqual(cssText);
290+
});
291+
292+
it('fixes incorrectly parsed grid template rules', () => {
293+
const cssText1 =
294+
'#wrapper { grid-template-areas: "header header" "main main" "footer footer"; grid-template-rows: minmax(2, 1fr); grid-template-columns: minmax(2, 1fr); display: grid; margin: 0px auto; }';
295+
const cssText2 =
296+
'.some-class { color: purple; grid-template: "TopNav TopNav" 65px "SideNav Content" 52px "SideNav Content" / 255px auto; column-gap: 32px; }';
297+
298+
const mockCssRule1 = {
299+
cssText: cssText1,
300+
selectorText: '#wrapper',
301+
style: {
302+
length: 5,
303+
0: 'grid-template-areas',
304+
1: 'grid-template-rows',
305+
2: 'grid-template-columns',
306+
3: 'display',
307+
4: 'margin',
308+
getPropertyValue: (key: string): string => {
309+
switch (key) {
310+
case 'grid-template-areas':
311+
return '"header header" "main main" "footer footer"';
312+
case 'grid-template-rows':
313+
return 'minmax(2, 1fr)';
314+
case 'grid-template-columns':
315+
return 'minmax(2, 1fr)';
316+
case 'display':
317+
return 'grid';
318+
case 'margin':
319+
return '0px auto';
320+
default:
321+
return '';
322+
}
323+
},
324+
} as Record<string | number, any>,
325+
} as Partial<CSSStyleRule> as CSSStyleRule;
326+
327+
const mockCssRule2 = {
328+
cssText: cssText2,
329+
selectorText: '.some-class',
330+
style: {
331+
length: 5,
332+
0: 'color',
333+
1: 'grid-template-areas',
334+
2: 'grid-template-rows',
335+
3: 'grid-template-columns',
336+
4: 'column-gap',
337+
getPropertyValue: (key: string): string => {
338+
switch (key) {
339+
case 'color':
340+
return 'purple';
341+
case 'grid-template-areas':
342+
return '"TopNav TopNav" "SideNav Content" "SideNav Content"';
343+
case 'grid-template-rows':
344+
return '65px 52px auto';
345+
case 'grid-template-columns':
346+
return '255px auto';
347+
case 'column-gap':
348+
return '32px';
349+
default:
350+
return '';
351+
}
352+
},
353+
} as Record<string | number, any>,
354+
} as Partial<CSSStyleRule> as CSSStyleRule;
355+
356+
expect(replaceChromeGridTemplateAreas(mockCssRule1)).toEqual(
357+
'#wrapper { grid-template-areas: "header header" "main main" "footer footer"; grid-template-rows: minmax(2, 1fr); grid-template-columns: minmax(2, 1fr); display: grid; margin: 0px auto; }',
358+
);
359+
expect(replaceChromeGridTemplateAreas(mockCssRule2)).toEqual(
360+
'.some-class { color: purple; grid-template-areas: "TopNav TopNav" "SideNav Content" "SideNav Content"; grid-template-rows: 65px 52px auto; grid-template-columns: 255px auto; column-gap: 32px; }',
361+
);
362+
});
363+
});
364+
271365
describe('fixSafariColons', () => {
272366
it('parses : in attribute selectors correctly', () => {
273367
const out1 = fixSafariColons('[data-foo] { color: red; }');

0 commit comments

Comments
 (0)