Skip to content

Commit aa2bc86

Browse files
authored
fix(body): user's margin summing with what's client-defined in the body (#2637)
1 parent ab4c6e3 commit aa2bc86

File tree

8 files changed

+76
-10
lines changed

8 files changed

+76
-10
lines changed

.changeset/petite-eels-appear.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-email/body": minor
3+
---
4+
5+
reset the `margin` property in the `<body>` when it has a user definition

.changeset/vast-books-wink.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@react-email/components": minor
3+
---
4+
5+
body: reset the `margin` property in the `<body>` when it has a user definition
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,25 @@
11
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
22

3+
exports[`<Body> component > margin resetting behavior > should reset the margin property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
4+
5+
exports[`<Body> component > margin resetting behavior > should reset the marginBlock property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin-block:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin-block:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
6+
7+
exports[`<Body> component > margin resetting behavior > should reset the marginBlockEnd property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin-block-end:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin-block-end:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
8+
9+
exports[`<Body> component > margin resetting behavior > should reset the marginBlockStart property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin-block-start:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin-block-start:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
10+
11+
exports[`<Body> component > margin resetting behavior > should reset the marginBottom property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin-bottom:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin-bottom:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
12+
13+
exports[`<Body> component > margin resetting behavior > should reset the marginInline property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin-inline:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin-inline:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
14+
15+
exports[`<Body> component > margin resetting behavior > should reset the marginInlineEnd property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin-inline-end:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin-inline-end:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
16+
17+
exports[`<Body> component > margin resetting behavior > should reset the marginInlineStart property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin-inline-start:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin-inline-start:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
18+
19+
exports[`<Body> component > margin resetting behavior > should reset the marginLeft property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin-left:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin-left:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
20+
21+
exports[`<Body> component > margin resetting behavior > should reset the marginRight property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin-right:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin-right:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
22+
23+
exports[`<Body> component > margin resetting behavior > should reset the marginTop property when it comes from props 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body style="margin-top:0"><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td style="margin-top:10px">Random text</td></tr></tbody></table><!--/$--></body>"`;
24+
325
exports[`<Body> component > renders correctly 1`] = `"<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><!--$--><body><table border="0" width="100%" cellPadding="0" cellSpacing="0" role="presentation" align="center"><tbody><tr><td>Lorem ipsum</td></tr></tbody></table><!--/$--></body>"`;

packages/body/src/body.spec.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { render } from '@react-email/render';
22
import { Body } from './index';
3+
import { marginProperties } from './margin-properties';
34

45
describe('<Body> component', () => {
56
it('renders children correctly', async () => {
@@ -23,4 +24,15 @@ describe('<Body> component', () => {
2324
const actualOutput = await render(<Body>Lorem ipsum</Body>);
2425
expect(actualOutput).toMatchSnapshot();
2526
});
27+
28+
describe('margin resetting behavior', () => {
29+
for (const property of marginProperties) {
30+
it(`should reset the ${property} property when it comes from props`, async () => {
31+
const actualOutput = await render(
32+
<Body style={{ [property]: 10 }}>Random text</Body>,
33+
);
34+
expect(actualOutput).toMatchSnapshot();
35+
});
36+
}
37+
});
2638
});

packages/body/src/body.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,25 @@
11
import * as React from 'react';
2+
import { marginProperties } from './margin-properties';
23

34
export type BodyProps = Readonly<React.HtmlHTMLAttributes<HTMLBodyElement>>;
45

56
export const Body = React.forwardRef<HTMLBodyElement, BodyProps>(
67
({ children, style, ...props }, ref) => {
8+
const bodyStyle: Record<string, string | number | undefined> = {
9+
background: style?.background,
10+
backgroundColor: style?.backgroundColor,
11+
};
12+
if (style) {
13+
for (const property of marginProperties) {
14+
// We reset the margin if the user sets it, this mimics the
15+
// same behavior that would happen if this was only using the body.
16+
// This avoids the incoming margin summing up with the margin
17+
// defined by the email client on the body, or by the browser itself
18+
bodyStyle[property] = style[property] !== undefined ? 0 : undefined;
19+
}
20+
}
721
return (
8-
<body
9-
{...props}
10-
style={{
11-
background: style?.background,
12-
backgroundColor: style?.backgroundColor,
13-
}}
14-
ref={ref}
15-
>
22+
<body {...props} style={bodyStyle} ref={ref}>
1623
<table
1724
border={0}
1825
width="100%"
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export const marginProperties: (keyof React.CSSProperties)[] = [
2+
'margin',
3+
'marginTop',
4+
'marginBottom',
5+
'marginRight',
6+
'marginLeft',
7+
'marginInline',
8+
'marginBlock',
9+
'marginBlockStart',
10+
'marginBlockEnd',
11+
'marginInlineStart',
12+
'marginInlineEnd',
13+
];

packages/preview-server/src/utils/__snapshots__/get-email-component.spec.ts.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ exports[`getEmailComponent() > with a demo email template 1`] = `
1212
<meta name="x-apple-disable-message-reformatting" />
1313
<!--$-->
1414
</head>
15-
<body style="background-color:rgb(255,255,255)">
15+
<body
16+
style="background-color:rgb(255,255,255);margin-top:0;margin-bottom:0;margin-right:0;margin-left:0">
1617
<table
1718
border="0"
1819
width="100%"

packages/react-email/src/commands/testing/__snapshots__/export.spec.ts.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ exports[`email export 1`] = `
1010
<meta name="x-apple-disable-message-reformatting" />
1111
<!--$-->
1212
</head>
13-
<body style="background-color:rgb(255,255,255)">
13+
<body
14+
style="background-color:rgb(255,255,255);margin-top:0;margin-bottom:0;margin-right:0;margin-left:0">
1415
<table
1516
border="0"
1617
width="100%"

0 commit comments

Comments
 (0)