Remove the widget body padding and the surplus reported height

Previously the widget document had `padding: 6px` on the body, so every
embedded widget sat 6px inside its container and could not align flush with
the host layout. `updateIframeHeight` then reported
`document.body.offsetHeight + 12`, but the body is `box-sizing: border-box`
and `offsetHeight` already includes padding, so the addition double-counted
it.

Measured against the deployed widget: the content needs 20610px, the body
reported 20622px with the padding, and the parent was told 20634px, leaving
24px of empty space below every embed on top of the horizontal inset.

Removing the padding does not clip anything. With it at zero, offsetHeight,
body scrollHeight and documentElement scrollHeight all agree, and the last
child carries no bottom margin, so no margin collapses through the body edge.

Resolves #1487.
This commit is contained in:
Dmitry Verkhoturov
2026-08-18 18:46:05 -05:00
committed by Umputun
parent 8801903d01
commit fdfce6495c
3 changed files with 57 additions and 3 deletions
+1 -1
View File
@@ -2,7 +2,7 @@ body {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
margin: 0;
padding: 6px;
padding: 0;
font-family: system-ui;
font-size: 14px;
color: rgb(var(--primary-text-color));
@@ -0,0 +1,54 @@
import { updateIframeHeight } from './post-message';
describe('updateIframeHeight', () => {
const postMessage = jest.fn();
let originalParent: Window;
beforeAll(() => {
originalParent = window.parent;
// postMessageToParent bails out when window.parent is window, which is the case in jsdom
Object.defineProperty(window, 'parent', { value: { postMessage }, writable: true, configurable: true });
});
afterAll(() => {
Object.defineProperty(window, 'parent', { value: originalParent, writable: true, configurable: true });
});
beforeEach(() => {
postMessage.mockClear();
jest.spyOn(document.body, 'offsetHeight', 'get').mockReturnValue(500);
});
afterEach(() => {
jest.restoreAllMocks();
});
it('should report the document height without adding to it', () => {
updateIframeHeight();
expect(postMessage).toHaveBeenCalledWith({ height: 500 }, '*');
});
it('should report the dropdown height when it exceeds the document height', () => {
const dropdown = document.createElement('div');
jest.spyOn(dropdown, 'getBoundingClientRect').mockReturnValue({ top: 100 } as DOMRect);
jest.spyOn(dropdown, 'scrollHeight', 'get').mockReturnValue(600);
updateIframeHeight(dropdown);
// 20px allowance for the shadow under the dropdown
expect(postMessage).toHaveBeenCalledWith({ height: 720 }, '*');
});
it('should report the document height when it exceeds the dropdown height', () => {
const dropdown = document.createElement('div');
jest.spyOn(dropdown, 'getBoundingClientRect').mockReturnValue({ top: 10 } as DOMRect);
jest.spyOn(dropdown, 'scrollHeight', 'get').mockReturnValue(50);
updateIframeHeight(dropdown);
expect(postMessage).toHaveBeenCalledWith({ height: 500 }, '*');
});
});
@@ -78,8 +78,8 @@ export function updateIframeHeight(dropdown?: HTMLElement) {
scrollHeight = window.scrollY + Math.abs(top) + dropdown.scrollHeight + 20;
}
// The size of vertical padding on body is 12px
const bodyHeight = document.body.offsetHeight + 12;
// offsetHeight already covers padding and border, so it is the full document height
const bodyHeight = document.body.offsetHeight;
postMessageToParent({ height: Math.max(scrollHeight, bodyHeight) });
}