generated from adobe/aem-boilerplate
-
Notifications
You must be signed in to change notification settings - Fork 174
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fixed height auto adjustment and iframe styles in modal
- Loading branch information
Showing
6 changed files
with
230 additions
and
173 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { debounce } from '../../utils/action.js'; | ||
|
||
export const MOBILE_MAX = 599; | ||
export const TABLET_MAX = 1199; | ||
|
||
export function adjustModalHeight(contentHeight) { | ||
if (!window.location.hash) return; | ||
const dialog = document.querySelector(window.location.hash); | ||
const iframe = dialog?.querySelector('iframe'); | ||
const iframeWrapper = dialog?.querySelector('.milo-iframe'); | ||
if (!contentHeight || !iframe || !iframeWrapper) return; | ||
if (contentHeight === '100%') { | ||
iframe.style.height = '100%'; | ||
iframeWrapper.style.removeProperty('height'); | ||
dialog.style.removeProperty('height'); | ||
} else { | ||
const verticalMargins = 20; | ||
const clientHeight = document.documentElement.clientHeight - verticalMargins; | ||
if (clientHeight <= 0) return; | ||
const newHeight = contentHeight > clientHeight ? clientHeight : contentHeight; | ||
iframe.style.height = '100%'; | ||
iframeWrapper.style.height = `${newHeight}px`; | ||
dialog.style.height = `${newHeight}px`; | ||
} | ||
} | ||
|
||
export function sendViewportDimensionsToIframe(source) { | ||
const viewportWidth = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0); | ||
source.postMessage({ mobileMax: MOBILE_MAX, tabletMax: TABLET_MAX, viewportWidth }, '*'); | ||
} | ||
|
||
export function sendViewportDimensionsOnRequest(source) { | ||
sendViewportDimensionsToIframe(source); | ||
window.addEventListener('resize', debounce(() => sendViewportDimensionsToIframe(source), 10)); | ||
} | ||
|
||
function reactToMessage({ data, source }) { | ||
if (data === 'viewportWidth' && source) { | ||
/* If the page inside iframe comes from another domain, it won't be able to retrieve | ||
the viewport dimensions, so it sends a request to receive the viewport dimensions | ||
from the parent window. */ | ||
sendViewportDimensionsOnRequest(source); | ||
} | ||
|
||
if (data?.contentHeight) { | ||
/* If the page inside iframe sends the postMessage with its content height, | ||
we activate the height auto adjustment to eliminate the blank space at the bottom of the modal. | ||
For this we set the iframe height to 0% in CSS to let the page inside iframe | ||
to measure its content height properly. | ||
Then we set the modal height to be the same as the content height we received. | ||
For the modal height adjustment to work the following conditions must be met: | ||
1. The modal must have the class 'commerce-frame'; | ||
2. The page inside iframe must send a postMessage with the contentHeight (in px, or '100%); */ | ||
adjustModalHeight(data?.contentHeight); | ||
} | ||
} | ||
|
||
export function adjustStyles({ dialog, iframe }) { | ||
const isAutoHeightAdjustment = /\/mini-plans\/.*mid=ft.*web=1/.test(iframe.src); // matches e.g. https://www.adobe.com/mini-plans/photoshop.html?mid=ft&web=1 | ||
if (isAutoHeightAdjustment) { | ||
dialog.classList.add('height-fit-content'); | ||
} else { | ||
iframe.style.height = '100%'; | ||
} | ||
} | ||
|
||
export default async function enableCommerceFrameFeatures({ dialog, iframe }) { | ||
if (!dialog || !iframe) return; | ||
adjustStyles({ dialog, iframe }); | ||
window.addEventListener('message', reactToMessage); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<div class="dialog-modal commerce-frame" id="first-dialog"> | ||
<div class="fragment"> | ||
<div class="section"> | ||
<div class="milo-iframe modal"> | ||
<iframe src="https://www.adobe.com/mini-plans/photoshop.html?mid=ft&web=1&pint=Photoshop" allowfullscreen="true"></iframe> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div class="dialog-modal commerce-frame" id="second-dialog"> | ||
<div class="fragment"> | ||
<div class="section"> | ||
<div class="milo-iframe modal"> | ||
<iframe src="https://www.adobe.com/mini-plans/illustrator.html?mid=ft&web=1&pint=Illustrator" allowfullscreen="true"></iframe> | ||
</div> | ||
</div> | ||
</div> | ||
</div> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { readFile, setViewport } from '@web/test-runner-commands'; | ||
import { expect } from '@esm-bundle/chai'; | ||
import sinon from 'sinon'; | ||
import enableCommerceFrameFeatures, { MOBILE_MAX, TABLET_MAX } from '../../../libs/blocks/modal/modal.merch.js'; | ||
import { delay } from '../../helpers/waitfor.js'; | ||
|
||
document.body.innerHTML = await readFile({ path: './mocks/iframe.plain.html' }); | ||
const dialog = document.querySelector('#first-dialog'); | ||
const iframeWrapper = dialog.querySelector('.milo-iframe'); | ||
const iframe = dialog.querySelector('iframe'); | ||
const secondDialog = document.querySelector('#second-dialog'); | ||
const secondIframeWrapper = secondDialog.querySelector('.milo-iframe'); | ||
const secondIframe = secondDialog.querySelector('iframe'); | ||
|
||
describe('Modal dialog with a `commerce-frame` class', () => { | ||
it('adjustStyles: sets the iframe height to 100% if height adjustment is not applicable', () => { | ||
const originalSrc = iframe.getAttribute('src'); | ||
iframe.setAttribute('src', 'https://www.adobe.com/somepage.html'); | ||
enableCommerceFrameFeatures({ dialog, iframe }); | ||
expect(dialog.classList.contains('height-fit-content')).to.be.false; | ||
expect(iframe.style.height).to.equal('100%'); | ||
iframe.style.removeProperty('height'); | ||
iframe.setAttribute('src', originalSrc); | ||
}); | ||
|
||
it('adjustStyles: sets the `height-fit-content` class if height adjustment is applicable', () => { | ||
enableCommerceFrameFeatures({ dialog, iframe }); | ||
expect(dialog.classList.contains('height-fit-content')).to.be.true; | ||
expect(iframe.style.height).to.equal(''); | ||
}); | ||
|
||
it('sends viewport dimensions upon request, and then on every resize', async () => { | ||
sinon.spy(window, 'postMessage'); | ||
enableCommerceFrameFeatures({ dialog, iframe }); | ||
window.postMessage('viewportWidth'); | ||
await delay(10); | ||
expect(window.postMessage.calledWith({ | ||
mobileMax: MOBILE_MAX, | ||
tabletMax: TABLET_MAX, | ||
viewportWidth: 800, | ||
})).to.be.true; | ||
|
||
document.documentElement.setAttribute('style', 'width: 1200px'); | ||
window.dispatchEvent(new Event('resize')); | ||
await delay(10); | ||
expect(window.postMessage.calledWith({ | ||
mobileMax: MOBILE_MAX, | ||
tabletMax: TABLET_MAX, | ||
viewportWidth: 1200, | ||
})).to.be.true; | ||
document.documentElement.removeAttribute('style'); | ||
}); | ||
|
||
it('adjusts modal height if height auto adjustment is applicable', async () => { | ||
const contentHeight = { | ||
desktop: 714, | ||
mobile: '100%', | ||
}; | ||
await setViewport({ width: 1200, height: 1000 }); | ||
window.location.hash = '#first-dialog'; | ||
window.postMessage({ contentHeight: contentHeight.desktop }, '*'); | ||
await delay(50); | ||
expect(iframe.clientHeight).to.equal(contentHeight.desktop); | ||
expect(iframeWrapper.clientHeight).to.equal(contentHeight.desktop); | ||
expect(dialog.clientHeight).to.equal(contentHeight.desktop); | ||
|
||
await setViewport({ width: 320, height: 600 }); | ||
window.postMessage({ contentHeight: contentHeight.mobile }, '*'); | ||
await delay(50); | ||
expect(iframe.style.height).to.equal(contentHeight.mobile); | ||
expect(iframeWrapper.style.height).to.equal(''); | ||
expect(dialog.style.height).to.equal(''); | ||
window.location.hash = ''; | ||
}); | ||
|
||
it('properly adjusts the modal height when there are two modals on the page', async () => { | ||
const contentHeight = { | ||
desktop1: 714, | ||
desktop2: 600, | ||
mobile: '100%', | ||
}; | ||
await setViewport({ width: 1200, height: 1000 }); | ||
window.location.hash = '#first-dialog'; | ||
window.postMessage({ contentHeight: contentHeight.desktop1 }, '*'); | ||
await delay(50); | ||
expect(iframe.clientHeight).to.equal(contentHeight.desktop1); | ||
expect(iframeWrapper.clientHeight).to.equal(contentHeight.desktop1); | ||
expect(dialog.clientHeight).to.equal(contentHeight.desktop1); | ||
|
||
await setViewport({ width: 320, height: 600 }); | ||
window.postMessage({ contentHeight: contentHeight.mobile }, '*'); | ||
await delay(50); | ||
expect(iframe.style.height).to.equal(contentHeight.mobile); | ||
expect(iframeWrapper.style.height).to.equal(''); | ||
expect(dialog.style.height).to.equal(''); | ||
window.location.hash = ''; | ||
|
||
await setViewport({ width: 1200, height: 1000 }); | ||
window.location.hash = '#second-dialog'; | ||
window.postMessage({ contentHeight: contentHeight.desktop2 }, '*'); | ||
await delay(50); | ||
expect(secondIframe.clientHeight).to.equal(contentHeight.desktop2); | ||
expect(secondIframeWrapper.clientHeight).to.equal(contentHeight.desktop2); | ||
expect(secondDialog.clientHeight).to.equal(contentHeight.desktop2); | ||
|
||
await setViewport({ width: 320, height: 600 }); | ||
window.postMessage({ contentHeight: contentHeight.mobile }, '*'); | ||
await delay(50); | ||
expect(iframe.style.height).to.equal(contentHeight.mobile); | ||
expect(iframeWrapper.style.height).to.equal(''); | ||
expect(dialog.style.height).to.equal(''); | ||
window.location.hash = ''; | ||
}); | ||
}); |
Oops, something went wrong.