diff --git a/packages/ui/components/overlays/src/OverlaysManager.js b/packages/ui/components/overlays/src/OverlaysManager.js index 42d9fc6f3..b3c4a0720 100644 --- a/packages/ui/components/overlays/src/OverlaysManager.js +++ b/packages/ui/components/overlays/src/OverlaysManager.js @@ -6,14 +6,18 @@ import { globalOverlaysStyle } from './globalOverlaysStyle.js'; import { setSiblingsInert, unsetSiblingsInert } from './utils/inert-siblings.js'; -const isIOS = navigator.userAgent.match(/iPhone|iPad|iPod/i); -const isMacSafari = - navigator.vendor && - navigator.vendor.indexOf('Apple') > -1 && - navigator.userAgent && - navigator.userAgent.indexOf('CriOS') === -1 && - navigator.userAgent.indexOf('FxiOS') === -1 && - navigator.appVersion.indexOf('Mac') !== -1; +// Export this as protected var, so that we can easily mock it in tests +// TODO: combine with browserDetection of core? +export const _browserDetection = { + isIOS: /iPhone|iPad|iPod/i.test(navigator.userAgent), + isMacSafari: + navigator.vendor && + navigator.vendor.indexOf('Apple') > -1 && + navigator.userAgent && + navigator.userAgent.indexOf('CriOS') === -1 && + navigator.userAgent.indexOf('FxiOS') === -1 && + navigator.appVersion.indexOf('Mac') !== -1, +}; /** * `OverlaysManager` which manages overlays which are rendered into the body @@ -206,6 +210,7 @@ export class OverlaysManager { // eslint-disable-next-line class-methods-use-this requestToPreventScroll() { + const { isIOS, isMacSafari } = _browserDetection; // no check as classList will dedupe it anyways document.body.classList.add('global-overlays-scroll-lock'); if (isIOS || isMacSafari) { @@ -219,6 +224,7 @@ export class OverlaysManager { } requestToEnableScroll() { + const { isIOS, isMacSafari } = _browserDetection; if (!this.shownList.some(ctrl => ctrl.preventsScroll === true)) { document.body.classList.remove('global-overlays-scroll-lock'); if (isIOS || isMacSafari) { diff --git a/packages/ui/components/overlays/test/OverlaysManager.test.js b/packages/ui/components/overlays/test/OverlaysManager.test.js index ac0434927..cd1f7cf21 100644 --- a/packages/ui/components/overlays/test/OverlaysManager.test.js +++ b/packages/ui/components/overlays/test/OverlaysManager.test.js @@ -1,6 +1,7 @@ import { expect, fixture } from '@open-wc/testing'; import { html } from 'lit/static-html.js'; import { OverlayController, OverlaysManager } from '@lion/ui/overlays.js'; +import { _browserDetection } from '../src/OverlaysManager.js'; /** * @typedef {import('../types/OverlayConfig.js').OverlayConfig} OverlayConfig @@ -97,4 +98,79 @@ describe('OverlaysManager', () => { await dialog2.hide(); expect(mngr.shownList).to.deep.equal([]); }); + + describe('Browser/device edge cases', () => { + const isIOSOriginal = _browserDetection.isIOS; + const isMacSafariOriginal = _browserDetection.isMacSafari; + + function mockIOS() { + _browserDetection.isIOS = true; + _browserDetection.isMacSafari = false; + } + + function mockMacSafari() { + // When we are iOS + _browserDetection.isIOS = false; + _browserDetection.isMacSafari = true; + } + + afterEach(() => { + // Restore original values + _browserDetection.isIOS = isIOSOriginal; + _browserDetection.isMacSafari = isMacSafariOriginal; + }); + + describe('When initialized with "preventsScroll: true"', () => { + it('adds class "global-overlays-scroll-lock-ios-fix" to body and html on iOS', async () => { + mockIOS(); + const dialog = new OverlayController({ ...defaultOptions, preventsScroll: true }, mngr); + await dialog.show(); + expect(Array.from(document.body.classList)).to.contain( + 'global-overlays-scroll-lock-ios-fix', + ); + expect(Array.from(document.documentElement.classList)).to.contain( + 'global-overlays-scroll-lock-ios-fix', + ); + await dialog.hide(); + expect(Array.from(document.body.classList)).to.not.contain( + 'global-overlays-scroll-lock-ios-fix', + ); + expect(Array.from(document.documentElement.classList)).to.not.contain( + 'global-overlays-scroll-lock-ios-fix', + ); + + // When we are not iOS nor MacSafari + _browserDetection.isIOS = false; + _browserDetection.isMacSafari = false; + + const dialog2 = new OverlayController({ ...defaultOptions, preventsScroll: true }, mngr); + await dialog2.show(); + expect(Array.from(document.body.classList)).to.not.contain( + 'global-overlays-scroll-lock-ios-fix', + ); + expect(Array.from(document.documentElement.classList)).to.not.contain( + 'global-overlays-scroll-lock-ios-fix', + ); + }); + + it('adds class "global-overlays-scroll-lock-ios-fix" to body on MacSafari', async () => { + mockMacSafari(); + const dialog = new OverlayController({ ...defaultOptions, preventsScroll: true }, mngr); + await dialog.show(); + expect(Array.from(document.body.classList)).to.contain( + 'global-overlays-scroll-lock-ios-fix', + ); + expect(Array.from(document.documentElement.classList)).to.not.contain( + 'global-overlays-scroll-lock-ios-fix', + ); + await dialog.hide(); + expect(Array.from(document.body.classList)).to.not.contain( + 'global-overlays-scroll-lock-ios-fix', + ); + expect(Array.from(document.documentElement.classList)).to.not.contain( + 'global-overlays-scroll-lock-ios-fix', + ); + }); + }); + }); });