Skip to content

Commit 31cd703

Browse files
authored
Merge pull request #8652 from IgniteUI/mvenkov/close-overlay-in-shadow-dom-9.2
Close on outside click when in shadow DOM - 9.1
2 parents 957d034 + eac9a0f commit 31cd703

File tree

2 files changed

+94
-39
lines changed

2 files changed

+94
-39
lines changed

projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts

Lines changed: 90 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@ import {
66
ViewChild,
77
HostBinding,
88
ApplicationRef,
9-
ComponentRef
9+
ComponentRef,
10+
ViewEncapsulation
1011
} from '@angular/core';
1112
import { TestBed, fakeAsync, tick, async, inject } from '@angular/core/testing';
1213
import { BrowserModule } from '@angular/platform-browser';
@@ -177,9 +178,9 @@ function formatString(inputString: string, formatters: any[]) {
177178

178179
describe('igxOverlay', () => {
179180
const formatters = [
180-
{pattern: /:\s/g, replacement: ':'},
181-
{pattern: /red;/, replacement: 'red'}
182-
];
181+
{ pattern: /:\s/g, replacement: ':' },
182+
{ pattern: /red;/, replacement: 'red' }
183+
];
183184
beforeEach(async(() => {
184185
UIInteractions.clearOverlay();
185186
}));
@@ -996,6 +997,46 @@ describe('igxOverlay', () => {
996997
overlayDiv = document.getElementsByClassName(CLASS_OVERLAY_MAIN)[0];
997998
expect(overlayDiv).toBeUndefined();
998999
}));
1000+
1001+
it('should correctly handle close on outside click in shadow DOM', fakeAsync(() => {
1002+
const fix = TestBed.createComponent(EmptyPageInShadowDomComponent);
1003+
const button = fix.componentInstance.buttonElement;
1004+
const outlet = fix.componentInstance.outletElement;
1005+
const overlay = fix.componentInstance.overlay;
1006+
fix.detectChanges();
1007+
1008+
const overlaySettings: OverlaySettings = {
1009+
modal: false,
1010+
closeOnOutsideClick: true,
1011+
positionStrategy: new ConnectedPositioningStrategy(),
1012+
outlet: outlet
1013+
};
1014+
1015+
overlaySettings.positionStrategy.settings.target = button.nativeElement;
1016+
overlay.show(overlay.attach(SimpleDynamicComponent), overlaySettings);
1017+
tick();
1018+
fix.detectChanges();
1019+
1020+
let overlayDiv: Element = outlet.nativeElement.getElementsByTagName('component')[0];
1021+
expect(overlayDiv).toBeDefined();
1022+
1023+
const toggledDiv = overlayDiv.children[0];
1024+
(toggledDiv as any).click();
1025+
1026+
tick();
1027+
fix.detectChanges();
1028+
1029+
overlayDiv = outlet.nativeElement.getElementsByTagName('component')[0];
1030+
expect(overlayDiv).toBeDefined();
1031+
1032+
document.body.click();
1033+
1034+
tick();
1035+
fix.detectChanges();
1036+
1037+
overlayDiv = outlet.nativeElement.getElementsByTagName('component')[0];
1038+
expect(overlayDiv).toBeUndefined();
1039+
}));
9991040
});
10001041

10011042
describe('Unit Tests - Scroll Strategies: ', () => {
@@ -1431,44 +1472,44 @@ describe('igxOverlay', () => {
14311472

14321473
it(`Should use StartPoint:Left/Bottom, Direction Right/Bottom and openAnimation: scaleInVerTop, closeAnimation: scaleOutVerTop
14331474
as default options when using a ConnectedPositioningStrategy without passing other but target element options.`, () => {
1434-
const targetEl: HTMLElement = <HTMLElement>document.getElementsByClassName('300_button')[0];
1435-
const positionSettings2 = {
1436-
target: targetEl
1437-
};
1475+
const targetEl: HTMLElement = <HTMLElement>document.getElementsByClassName('300_button')[0];
1476+
const positionSettings2 = {
1477+
target: targetEl
1478+
};
14381479

1439-
const strategy = new ConnectedPositioningStrategy(positionSettings2);
1480+
const strategy = new ConnectedPositioningStrategy(positionSettings2);
14401481

1441-
const expectedDefaults = {
1442-
target: targetEl,
1443-
horizontalDirection: HorizontalAlignment.Right,
1444-
verticalDirection: VerticalAlignment.Bottom,
1445-
horizontalStartPoint: HorizontalAlignment.Left,
1446-
verticalStartPoint: VerticalAlignment.Bottom,
1447-
openAnimation: scaleInVerTop,
1448-
closeAnimation: scaleOutVerTop,
1449-
minSize: { width: 0, height: 0 }
1450-
};
1482+
const expectedDefaults = {
1483+
target: targetEl,
1484+
horizontalDirection: HorizontalAlignment.Right,
1485+
verticalDirection: VerticalAlignment.Bottom,
1486+
horizontalStartPoint: HorizontalAlignment.Left,
1487+
verticalStartPoint: VerticalAlignment.Bottom,
1488+
openAnimation: scaleInVerTop,
1489+
closeAnimation: scaleOutVerTop,
1490+
minSize: { width: 0, height: 0 }
1491+
};
14511492

1452-
expect(strategy.settings).toEqual(expectedDefaults);
1453-
});
1493+
expect(strategy.settings).toEqual(expectedDefaults);
1494+
});
14541495

14551496
it(`Should use target: null StartPoint:Left/Bottom, Direction Right/Bottom and openAnimation: scaleInVerTop,
14561497
closeAnimation: scaleOutVerTop as default options when using a ConnectedPositioningStrategy without passing options.`, () => {
1457-
const strategy = new ConnectedPositioningStrategy();
1498+
const strategy = new ConnectedPositioningStrategy();
14581499

1459-
const expectedDefaults = {
1460-
target: null,
1461-
horizontalDirection: HorizontalAlignment.Right,
1462-
verticalDirection: VerticalAlignment.Bottom,
1463-
horizontalStartPoint: HorizontalAlignment.Left,
1464-
verticalStartPoint: VerticalAlignment.Bottom,
1465-
openAnimation: scaleInVerTop,
1466-
closeAnimation: scaleOutVerTop,
1467-
minSize: { width: 0, height: 0 }
1468-
};
1500+
const expectedDefaults = {
1501+
target: null,
1502+
horizontalDirection: HorizontalAlignment.Right,
1503+
verticalDirection: VerticalAlignment.Bottom,
1504+
horizontalStartPoint: HorizontalAlignment.Left,
1505+
verticalStartPoint: VerticalAlignment.Bottom,
1506+
openAnimation: scaleInVerTop,
1507+
closeAnimation: scaleOutVerTop,
1508+
minSize: { width: 0, height: 0 }
1509+
};
14691510

1470-
expect(strategy.settings).toEqual(expectedDefaults);
1471-
});
1511+
expect(strategy.settings).toEqual(expectedDefaults);
1512+
});
14721513

14731514
// adding more than one component to show in igx-overlay:
14741515
it('Should render the component exactly on top of the previous one when adding a new instance with default settings.', () => {
@@ -3801,6 +3842,20 @@ export class EmptyPageComponent {
38013842
}
38023843
}
38033844

3845+
@Component({
3846+
template: `
3847+
<button #button>Show Overlay</button>
3848+
<div igxOverlayOutlet #outlet></div>
3849+
`,
3850+
encapsulation: ViewEncapsulation.ShadowDom
3851+
})
3852+
export class EmptyPageInShadowDomComponent {
3853+
constructor(@Inject(IgxOverlayService) public overlay: IgxOverlayService) { }
3854+
3855+
@ViewChild('button', { static: true }) buttonElement: ElementRef;
3856+
@ViewChild('outlet', { static: true }) outletElement: ElementRef;
3857+
}
3858+
38043859
@Component({
38053860
template: `<button #button (click)='click($event)'>Show Overlay</button>`,
38063861
styles: [`button {
@@ -3886,7 +3941,6 @@ export class TwoButtonsComponent {
38863941
ev.stopPropagation();
38873942
}
38883943
}
3889-
38903944
@Component({
38913945
template: `
38923946
<div style="width: 420px; height: 280px;">
@@ -3986,6 +4040,7 @@ export class FlexContainerComponent {
39864040
const DYNAMIC_COMPONENTS = [
39874041
EmptyPageComponent,
39884042
SimpleRefComponent,
4043+
EmptyPageInShadowDomComponent,
39894044
SimpleDynamicComponent,
39904045
SimpleBigSizeComponent,
39914046
DownRightButtonComponent,

projects/igniteui-angular/src/lib/services/overlay/overlay.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ export class IgxOverlayService implements OnDestroy {
269269
const info: OverlayInfo = this.getOverlayById(id);
270270

271271
if (!info) {
272-
return;
272+
return;
273273
}
274274

275275
info.transformX += deltaX;
@@ -447,7 +447,7 @@ export class IgxOverlayService implements OnDestroy {
447447

448448
private getOverlayElement(info: OverlayInfo): HTMLElement {
449449
if (info.settings.outlet) {
450-
return info.settings.outlet.nativeElement;
450+
return info.settings.outlet.nativeElement || info.settings.outlet;
451451
}
452452
if (!this._overlayElement) {
453453
this._overlayElement = this._document.createElement('div');
@@ -658,14 +658,14 @@ export class IgxOverlayService implements OnDestroy {
658658
return;
659659
}
660660
if (info.settings.closeOnOutsideClick) {
661-
const target = ev.target as any;
661+
const target = ev.composed ? ev.composedPath()[0] : ev.target;
662662
// if the click is on the element do not close this overlay
663663
if (!info.elementRef.nativeElement.contains(target)) {
664664
// if we should exclude position target check if the click is over it. If so do not close overlay
665665
const positionTarget = info.settings.positionStrategy.settings.target as HTMLElement;
666666
let clickOnPositionTarget = false;
667667
if (positionTarget && positionTarget.contains) {
668-
clickOnPositionTarget = positionTarget.contains(target);
668+
clickOnPositionTarget = positionTarget.contains(target as any);
669669
}
670670

671671
if (!(info.settings.excludePositionTarget && clickOnPositionTarget)) {

0 commit comments

Comments
 (0)