fix(overlays): prevent resize due to scrollbars on global overlay open (#746)

This commit is contained in:
Mathieu Puech 2020-06-03 05:11:56 -04:00 committed by Thomas Allmer
parent 78dbfbed49
commit c8da23ccb2
2 changed files with 71 additions and 0 deletions

View file

@ -9,6 +9,7 @@ async function preloadPopper() {
const GLOBAL_OVERLAYS_CONTAINER_CLASS = 'global-overlays__overlay-container';
const GLOBAL_OVERLAYS_CLASS = 'global-overlays__overlay';
const supportsCSSTypedObject = window.CSS && CSS.number;
/**
* @desc OverlayController is the fundament for every single type of overlay. With the right
@ -383,7 +384,9 @@ export class OverlayController {
this.dispatchEvent(event);
if (!event.defaultPrevented) {
this._contentWrapperNode.style.display = '';
this._keepBodySize({ phase: 'before-show' });
await this._handleFeatures({ phase: 'show' });
this._keepBodySize({ phase: 'show' });
await this._handlePosition({ phase: 'show' });
this.elementToFocusAfterHide = elementToFocusAfterHide;
this.dispatchEvent(new Event('show'));
@ -410,6 +413,51 @@ export class OverlayController {
}
}
_keepBodySize({ phase }) {
switch (phase) {
case 'before-show':
this.__bodyClientWidth = document.body.clientWidth;
this.__bodyClientHeight = document.body.clientHeight;
this.__bodyMarginRight = 0;
this.__bodyMarginBottom = 0;
break;
case 'show': {
if (supportsCSSTypedObject) {
this.__bodyMarginRight = document.body.computedStyleMap().get('margin-right').value;
this.__bodyMarginBottom = document.body.computedStyleMap().get('margin-bottom').value;
} else if (window.getComputedStyle) {
const bodyStyle = window.getComputedStyle(document.body);
if (bodyStyle) {
this.__bodyMarginRight = parseInt(bodyStyle.getPropertyValue('margin-right'), 10);
this.__bodyMarginBottom = parseInt(bodyStyle.getPropertyValue('margin-bottom'), 10);
}
}
const scrollbarWidth = document.body.clientWidth - this.__bodyClientWidth;
const scrollbarHeight = document.body.clientHeight - this.__bodyClientHeight;
const newMarginRight = this.__bodyMarginRight + scrollbarWidth;
const newMarginBottom = this.__bodyMarginBottom + scrollbarHeight;
if (supportsCSSTypedObject) {
document.body.attributeStyleMap.set('margin-right', CSS.px(newMarginRight));
document.body.attributeStyleMap.set('margin-bottom', CSS.px(newMarginBottom));
} else {
document.body.style.marginRight = `${newMarginRight}px`;
document.body.style.marginBottom = `${newMarginBottom}px`;
}
break;
}
case 'hide':
if (supportsCSSTypedObject) {
document.body.attributeStyleMap.set('margin-right', CSS.px(this.__bodyMarginRight));
document.body.attributeStyleMap.set('margin-bottom', CSS.px(this.__bodyMarginBottom));
} else {
document.body.style.marginRight = `${this.__bodyMarginRight}px`;
document.body.style.marginBottom = `${this.__bodyMarginBottom}px`;
}
break;
/* no default */
}
}
/**
* @event before-hide right before the overlay hides. Used for animations and switching overlays
* @event hide right after the overlay is hidden
@ -429,6 +477,7 @@ export class OverlayController {
// await this.transitionHide({ backdropNode: this.backdropNode, conentNode: this.contentNode });
this._contentWrapperNode.style.display = 'none';
this._handleFeatures({ phase: 'hide' });
this._keepBodySize({ phase: 'hide' });
this.dispatchEvent(new Event('hide'));
this._restoreFocus();
}

View file

@ -47,6 +47,28 @@ export function runOverlayMixinSuite({ tagString, tag, suffix = '' }) {
expect(el.opened).to.be.false;
});
it('does not change the body size when opened', async () => {
const parentNode = document.createElement('div');
parentNode.setAttribute('style', 'height: 10000px; width: 10000px;');
const elWithBigParent = await fixture(
html`
<${tag}>
<div slot="content">content of the overlay</div>
<button slot="invoker">invoker button</button>
</${tag}>
`,
{ parentNode },
);
const { offsetWidth, offsetHeight } = elWithBigParent.offsetParent;
await elWithBigParent._overlayCtrl.show();
expect(elWithBigParent.opened).to.be.true;
expect(elWithBigParent.offsetParent.offsetWidth).to.equal(offsetWidth);
expect(elWithBigParent.offsetParent.offsetHeight).to.equal(offsetHeight);
await elWithBigParent._overlayCtrl.hide();
expect(elWithBigParent.offsetParent.offsetWidth).to.equal(offsetWidth);
expect(elWithBigParent.offsetParent.offsetHeight).to.equal(offsetHeight);
});
it('should respond to initially and dynamically setting the config', async () => {
const itEl = await fixture(html`
<${tag} .config=${{ trapsKeyboardFocus: false, viewportConfig: { placement: 'top' } }}>