diff --git a/packages/overlays/src/GlobalOverlayController.js b/packages/overlays/src/GlobalOverlayController.js index eed118e84..f9236363d 100644 --- a/packages/overlays/src/GlobalOverlayController.js +++ b/packages/overlays/src/GlobalOverlayController.js @@ -13,6 +13,9 @@ export class GlobalOverlayController extends BaseOverlayController { preventsScroll: false, trapsKeyboardFocus: false, hidesOnEsc: false, + viewportConfig: { + placement: 'center', + }, ...params, }; @@ -26,12 +29,14 @@ export class GlobalOverlayController extends BaseOverlayController { this.trapsKeyboardFocus = finalParams.trapsKeyboardFocus; this.hidesOnEsc = finalParams.hidesOnEsc; this.invokerNode = finalParams.invokerNode; + this.overlayContainerClass = `global-overlays__overlay-container`; + this.overlayContainerPlacementClass = `${this.overlayContainerClass}--${finalParams.viewportConfig.placement}`; } /** * Syncs shown state and data. * - * @param {object} options optioons to sync + * @param {object} options options to sync * @param {boolean} [options.isShown] whether the overlay should be shown * @param {object} [options.data] data to pass to the content template function * @param {HTMLElement} [options.elementToFocusAfterHide] element to return focus when hiding @@ -69,6 +74,8 @@ export class GlobalOverlayController extends BaseOverlayController { return; } if (!this.content.isConnected) { + this.content.classList.add(this.overlayContainerClass); + this.content.classList.add(this.overlayContainerPlacementClass); this.manager.globalRootNode.appendChild(this.content); } @@ -99,6 +106,7 @@ export class GlobalOverlayController extends BaseOverlayController { this.hideDone(); if (this.contentTemplate) { + this.content.classList.remove(this.overlayContainerPlacementClass); this.manager.globalRootNode.removeChild(this.content); } } @@ -183,11 +191,11 @@ export class GlobalOverlayController extends BaseOverlayController { return; } - const blockingContoller = this.manager.shownList.find( + const blockingController = this.manager.shownList.find( ctrl => ctrl !== this && ctrl.isBlocking === true, ); - // if there are no other blocking overlays remaning, stop hiding regular overlays - if (!blockingContoller) { + // if there are no other blocking overlays remaining, stop hiding regular overlays + if (!blockingController) { this.manager.globalRootNode.classList.remove('global-overlays--blocking-opened'); } @@ -206,7 +214,7 @@ export class GlobalOverlayController extends BaseOverlayController { * it is removed. Otherwise this is the first time displaying a backdrop, so a fade-in * animation is played. * @param {OverlayController} overlay the overlay - * @param {boolean} noAnimation prevent an animatin from being displayed + * @param {boolean} noAnimation prevent an animation from being displayed */ enableBackdrop({ animation = true } = {}) { if (this.__hasActiveBackdrop === true) { @@ -243,4 +251,12 @@ export class GlobalOverlayController extends BaseOverlayController { this.__hasActiveBackdrop = false; } + + // TODO: this method has to be removed when EventTarget polyfill is available on IE11 + __fakeExtendsEventTarget() { + const delegate = document.createDocumentFragment(); + ['addEventListener', 'dispatchEvent', 'removeEventListener'].forEach(funcName => { + this[funcName] = (...args) => delegate[funcName](...args); + }); + } } diff --git a/packages/overlays/src/ModalDialogController.js b/packages/overlays/src/ModalDialogController.js index 612f0d906..6097e7f0f 100644 --- a/packages/overlays/src/ModalDialogController.js +++ b/packages/overlays/src/ModalDialogController.js @@ -7,6 +7,9 @@ export class ModalDialogController extends GlobalOverlayController { preventsScroll: true, trapsKeyboardFocus: true, hidesOnEsc: true, + viewportConfig: { + placement: 'center', + }, ...params, }); } diff --git a/packages/overlays/stories/global-overlay.stories.js b/packages/overlays/stories/global-overlay.stories.js index 41b963305..653660427 100644 --- a/packages/overlays/stories/global-overlay.stories.js +++ b/packages/overlays/stories/global-overlay.stories.js @@ -7,23 +7,29 @@ import { overlays, GlobalOverlayController } from '../index.js'; const globalOverlayDemoStyle = css` .demo-overlay { background-color: white; - position: absolute; - top: 20px; - left: 20px; width: 200px; - border: 1px solid blue; - } - - .demo-overlay--2 { - left: 240px; - } - - .demo-overlay--toast { - left: initial; - right: 20px; + border: 1px solid lightgrey; } `; +let placement = 'center'; +const togglePlacement = overlayCtrl => { + const placements = [ + 'top-left', + 'top', + 'top-right', + 'right', + 'bottom-left', + 'bottom', + 'bottom-right', + 'left', + 'center', + ]; + placement = placements[(placements.indexOf(placement) + 1) % placements.length]; + // eslint-disable-next-line no-param-reassign + overlayCtrl.overlayContainerClass = `global-overlays__overlay-container--${placement}`; +}; + storiesOf('Global Overlay System|Global Overlay', module) .add('Default', () => { const overlayCtrl = overlays.add( @@ -126,7 +132,7 @@ storiesOf('Global Overlay System|Global Overlay', module) Anchor
Tabindex
-
Contenteditable
+
Contenteditable
= `, @@ -208,6 +186,9 @@ describe('GlobalOverlayController', () => { const ctrl = overlays.add( new GlobalOverlayController({ elementToFocusAfterHide: input, + viewportConfig: { + placement: 'top-left', + }, contentTemplate: () => html`
`, @@ -230,6 +211,9 @@ describe('GlobalOverlayController', () => { const ctrl = overlays.add( new GlobalOverlayController({ + viewportConfig: { + placement: 'top-left', + }, contentTemplate: () => html`
`, @@ -252,6 +236,9 @@ describe('GlobalOverlayController', () => { const ctrl = overlays.add( new GlobalOverlayController({ + viewportConfig: { + placement: 'top-left', + }, contentTemplate: () => html`
`, @@ -351,4 +338,49 @@ describe('GlobalOverlayController', () => { expect(ctrl.backdropNode).to.have.class('global-overlays__backdrop'); }); }); + + describe('viewportConfig', () => { + it('places the overlay in center by default', async () => { + const controller = new GlobalOverlayController({ + contentTemplate: () => + html` +

Content

+ `, + }); + + controller.show(); + expect(controller.overlayContainerClass).to.equal( + 'global-overlays__overlay-container--center', + ); + }); + + it('can set the placement relative to the viewport ', async () => { + const placementMap = [ + 'top-left', + 'top', + 'top-right', + 'right', + 'bottom-right', + 'bottom', + 'bottom-left', + 'left', + 'center', + ]; + placementMap.forEach(viewportPlacement => { + const controller = new GlobalOverlayController({ + viewportConfig: { + placement: viewportPlacement, + }, + contentTemplate: () => + html` +

Content

+ `, + }); + controller.show(); + expect(controller.overlayContainerClass).to.equal( + `global-overlays__overlay-container--${viewportPlacement}`, + ); + }); + }); + }); }); diff --git a/packages/overlays/test/ModalDialogController.test.js b/packages/overlays/test/ModalDialogController.test.js index ebf109b6d..8beaeb935 100644 --- a/packages/overlays/test/ModalDialogController.test.js +++ b/packages/overlays/test/ModalDialogController.test.js @@ -25,5 +25,6 @@ describe('ModalDialogController', () => { expect(ctrl.preventsScroll).to.be.true; expect(ctrl.trapsKeyboardFocus).to.be.true; expect(ctrl.hidesOnEsc).to.be.true; + expect(ctrl.overlayContainerClass).to.equal('global-overlays__overlay-container--center'); }); });