diff --git a/projects/igniteui-angular/src/lib/dialog/dialog.component.spec.ts b/projects/igniteui-angular/src/lib/dialog/dialog.component.spec.ts index e6e6d196f5f..7e393815e81 100644 --- a/projects/igniteui-angular/src/lib/dialog/dialog.component.spec.ts +++ b/projects/igniteui-angular/src/lib/dialog/dialog.component.spec.ts @@ -345,7 +345,7 @@ describe('Dialog', () => { fix.detectChanges(); dialog.open(); - tick(); + tick(16); fix.detectChanges(); overlaydiv = document.getElementsByClassName(OVERLAY_MAIN_CLASS)[0]; diff --git a/projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts b/projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts index b051f22f9c3..e77bb2c38db 100644 --- a/projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts +++ b/projects/igniteui-angular/src/lib/services/overlay/overlay.spec.ts @@ -51,6 +51,7 @@ const CLASS_OVERLAY_WRAPPER_MODAL = 'igx-overlay__wrapper--modal'; const CLASS_OVERLAY_WRAPPER_FLEX = 'igx-overlay__wrapper--flex'; const CLASS_OVERLAY_MAIN = 'igx-overlay'; const CLASS_SCROLLABLE_DIV = 'scrollableDiv'; +const DEBOUNCE_TIME = 16; // Utility function to get all applied to element css from all sources. const css = (element) => { @@ -258,19 +259,19 @@ describe('igxOverlay', () => { mockFactoryResolver = { resolveComponentFactory: () => ({ create: () => ({ - hostView: '', - location: mockElementRef, - changeDetectorRef: { detectChanges: () => { } }, - destroy: () => { } - }) + hostView: '', + location: mockElementRef, + changeDetectorRef: { detectChanges: () => { } }, + destroy: () => { } }) + }) }; mockApplicationRef = { attachView: () => { }, detachView: () => { } }; mockInjector = {}; mockAnimationBuilder = {}; mockDocument = { body: mockElement, - listeners: { }, + listeners: {}, defaultView: mockElement, // this is used be able to properly invoke rxjs `fromEvent` operator, which, turns out // just adds an event listener to the element and emits accordingly @@ -437,7 +438,7 @@ describe('igxOverlay', () => { fixture.detectChanges(); fixture.componentInstance.buttonElement.nativeElement.click(); - tick(); + tick(DEBOUNCE_TIME); const overlayElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_MAIN)[0] as HTMLElement; @@ -634,8 +635,8 @@ describe('igxOverlay', () => { const bottom = 200; const mockElement = document.createElement('div'); spyOn(mockElement, 'getBoundingClientRect').and.callFake(() => ({ - left, top, width, height, right, bottom - } as DOMRect)); + left, top, width, height, right, bottom + } as DOMRect)); const mockItem = document.createElement('div'); mockElement.append(mockItem); @@ -833,7 +834,7 @@ describe('igxOverlay', () => { SimpleDynamicComponent, { positionStrategy: new ConnectedPositioningStrategy({ target: buttonElement }) }); overlayInstance.show(id); - tick(); + tick(DEBOUNCE_TIME); let contentElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_CONTENT_MODAL)[0] as HTMLElement; @@ -849,7 +850,7 @@ describe('igxOverlay', () => { getPointSpy.and.callThrough().and.returnValue(rect); window.resizeBy(200, 200); window.dispatchEvent(new Event('resize')); - tick(); + tick(DEBOUNCE_TIME); contentElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_CONTENT_MODAL)[0] as HTMLElement; @@ -869,7 +870,7 @@ describe('igxOverlay', () => { // remove SimpleRefComponent HTML element from the DOM tree fixture.elementRef.nativeElement.parentElement.removeChild(fixture.elementRef.nativeElement); overlay.show(overlay.attach(fixture.elementRef)); - tick(); + tick(DEBOUNCE_TIME); const componentElement = fixture.nativeElement as HTMLElement; expect(componentElement).toBeDefined(); @@ -1390,7 +1391,7 @@ describe('igxOverlay', () => { fixture.detectChanges(); fixture.componentInstance.buttonElement.nativeElement.click(); - tick(); + tick(DEBOUNCE_TIME); fixture.detectChanges(); const wrapperElement = (fixture.nativeElement as HTMLElement) @@ -1488,7 +1489,7 @@ describe('igxOverlay', () => { overlay.show(overlay.attach(SimpleDynamicComponent)); overlay.show(overlay.attach(SimpleDynamicComponent)); - tick(); + tick(DEBOUNCE_TIME); const wrapperElements = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL) as HTMLCollectionOf; const wrapperElement1 = wrapperElements[0]; @@ -1532,7 +1533,7 @@ describe('igxOverlay', () => { const overlay = fixture.componentInstance.overlay; overlay.show(overlay.attach(SimpleDynamicComponent)); - tick(); + tick(DEBOUNCE_TIME); fixture.detectChanges(); // overlay container IS NOT a child of the debugElement (attached to body, not app-root) @@ -1660,67 +1661,71 @@ describe('igxOverlay', () => { }); // adding more than one component to show in igx-overlay: - it('Should render the component exactly on top of the previous one when adding a new instance with default settings.', () => { - const fixture = TestBed.createComponent(TopLeftOffsetComponent); - const overlay = fixture.componentInstance.overlay; - const overlaySettings: OverlaySettings = { - positionStrategy: new ConnectedPositioningStrategy() - }; - overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); - overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); - fixture.detectChanges(); + it('Should render the component exactly on top of the previous one when adding a new instance with default settings.', + fakeAsync(() => { + const fixture = TestBed.createComponent(TopLeftOffsetComponent); + const overlay = fixture.componentInstance.overlay; + const overlaySettings: OverlaySettings = { + positionStrategy: new ConnectedPositioningStrategy() + }; + overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); + overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); + tick(DEBOUNCE_TIME); + fixture.detectChanges(); - const wrapperElements = (fixture.nativeElement as HTMLElement) - .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL) as HTMLCollectionOf; - const wrapperElement1 = wrapperElements[0]; - const wrapperElement2 = wrapperElements[1]; - const componentElement1 = wrapperElement1.getElementsByTagName('component')[0] as HTMLElement; - const componentElement2 = wrapperElement2.getElementsByTagName('component')[0] as HTMLElement; - const componentRect1 = componentElement1.getBoundingClientRect(); - const componentRect2 = componentElement2.getBoundingClientRect(); - expect(componentRect1.left).toEqual(0); - expect(componentRect1.left).toEqual(componentRect2.left); - expect(componentRect1.top).toEqual(0); - expect(componentRect1.top).toEqual(componentRect2.top); - expect(componentRect1.width).toEqual(componentRect2.width); - expect(componentRect1.height).toEqual(componentRect2.height); - }); + const wrapperElements = (fixture.nativeElement as HTMLElement) + .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL) as HTMLCollectionOf; + const wrapperElement1 = wrapperElements[0]; + const wrapperElement2 = wrapperElements[1]; + const componentElement1 = wrapperElement1.getElementsByTagName('component')[0] as HTMLElement; + const componentElement2 = wrapperElement2.getElementsByTagName('component')[0] as HTMLElement; + const componentRect1 = componentElement1.getBoundingClientRect(); + const componentRect2 = componentElement2.getBoundingClientRect(); + expect(componentRect1.left).toEqual(0); + expect(componentRect1.left).toEqual(componentRect2.left); + expect(componentRect1.top).toEqual(0); + expect(componentRect1.top).toEqual(componentRect2.top); + expect(componentRect1.width).toEqual(componentRect2.width); + expect(componentRect1.height).toEqual(componentRect2.height); + })); - it('Should render the component exactly on top of the previous one when adding a new instance with the same options.', () => { - const fixture = TestBed.createComponent(TopLeftOffsetComponent); - const x = 200; - const y = 300; + it('Should render the component exactly on top of the previous one when adding a new instance with the same options.', + fakeAsync(() => { + const fixture = TestBed.createComponent(TopLeftOffsetComponent); + const x = 200; + const y = 300; - const overlay = fixture.componentInstance.overlay; - const positionSettings: PositionSettings = { - horizontalDirection: HorizontalAlignment.Left, - verticalDirection: VerticalAlignment.Top, - horizontalStartPoint: HorizontalAlignment.Left, - verticalStartPoint: VerticalAlignment.Bottom, - }; - const overlaySettings: OverlaySettings = { - target: new Point(x, y), - positionStrategy: new ConnectedPositioningStrategy(positionSettings) - }; - overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); - overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); - fixture.detectChanges(); + const overlay = fixture.componentInstance.overlay; + const positionSettings: PositionSettings = { + horizontalDirection: HorizontalAlignment.Left, + verticalDirection: VerticalAlignment.Top, + horizontalStartPoint: HorizontalAlignment.Left, + verticalStartPoint: VerticalAlignment.Bottom, + }; + const overlaySettings: OverlaySettings = { + target: new Point(x, y), + positionStrategy: new ConnectedPositioningStrategy(positionSettings) + }; + overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); + overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); + tick(DEBOUNCE_TIME); + fixture.detectChanges(); - const wrapperElements = (fixture.nativeElement as HTMLElement) - .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL) as HTMLCollectionOf; - const wrapperElement1 = wrapperElements[0]; - const wrapperElement2 = wrapperElements[1]; - const componentElement1 = wrapperElement1.getElementsByTagName('component')[0] as HTMLElement; - const componentElement2 = wrapperElement2.getElementsByTagName('component')[0] as HTMLElement; - const componentRect1 = componentElement1.getBoundingClientRect(); - const componentRect2 = componentElement2.getBoundingClientRect(); - expect(componentRect1.left).toEqual(x - componentRect1.width); - expect(componentRect1.left).toEqual(componentRect2.left); - expect(componentRect1.top).toEqual(y - componentRect1.height); - expect(componentRect1.top).toEqual(componentRect2.top); - expect(componentRect1.width).toEqual(componentRect2.width); - expect(componentRect1.height).toEqual(componentRect2.height); - }); + const wrapperElements = (fixture.nativeElement as HTMLElement) + .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL) as HTMLCollectionOf; + const wrapperElement1 = wrapperElements[0]; + const wrapperElement2 = wrapperElements[1]; + const componentElement1 = wrapperElement1.getElementsByTagName('component')[0] as HTMLElement; + const componentElement2 = wrapperElement2.getElementsByTagName('component')[0] as HTMLElement; + const componentRect1 = componentElement1.getBoundingClientRect(); + const componentRect2 = componentElement2.getBoundingClientRect(); + expect(componentRect1.left).toEqual(x - componentRect1.width); + expect(componentRect1.left).toEqual(componentRect2.left); + expect(componentRect1.top).toEqual(y - componentRect1.height); + expect(componentRect1.top).toEqual(componentRect2.top); + expect(componentRect1.width).toEqual(componentRect2.width); + expect(componentRect1.height).toEqual(componentRect2.height); + })); it(`Should change the state of the component to closed when reaching threshold and closing scroll strategy is used.`, fakeAsync(() => { @@ -3010,7 +3015,7 @@ describe('igxOverlay', () => { }; overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); - tick(); + tick(DEBOUNCE_TIME); let wrapperElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL)[0] as HTMLElement; @@ -3020,7 +3025,7 @@ describe('igxOverlay', () => { tick(); wrapperElement = (fixture.nativeElement as HTMLElement) - .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL)[0] as HTMLElement; + .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER)[0] as HTMLElement; expect(wrapperElement.style.visibility).toEqual('hidden'); })); @@ -3032,14 +3037,14 @@ describe('igxOverlay', () => { }; overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); - tick(); + tick(DEBOUNCE_TIME); let wrapperElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL)[0] as HTMLElement; expect(wrapperElement.style.visibility).toEqual(''); UIInteractions.triggerKeyDownEvtUponElem('Escape', document); - tick(); + tick(DEBOUNCE_TIME); wrapperElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL)[0] as HTMLElement; @@ -3110,26 +3115,26 @@ describe('igxOverlay', () => { }; overlay.show(overlay.attach(SimpleDynamicComponent, overlaySettings)); - tick(); + tick(DEBOUNCE_TIME); let wrapperElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL)[0] as HTMLElement; expect(wrapperElement.style.visibility).toEqual(''); UIInteractions.triggerKeyDownEvtUponElem('Enter', document); - tick(); + tick(DEBOUNCE_TIME); wrapperElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL)[0] as HTMLElement; expect(wrapperElement.style.visibility).toEqual(''); UIInteractions.triggerKeyDownEvtUponElem('a', document); - tick(); + tick(DEBOUNCE_TIME); wrapperElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL)[0] as HTMLElement; expect(wrapperElement.style.visibility).toEqual(''); UIInteractions.triggerKeyDownEvtUponElem('ArrowUp', document); - tick(); + tick(DEBOUNCE_TIME); wrapperElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL)[0] as HTMLElement; expect(wrapperElement.style.visibility).toEqual(''); @@ -3137,7 +3142,7 @@ describe('igxOverlay', () => { UIInteractions.triggerKeyDownEvtUponElem('Escape', document); tick(); wrapperElement = (fixture.nativeElement as HTMLElement) - .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER_MODAL)[0] as HTMLElement; + .parentElement.getElementsByClassName(CLASS_OVERLAY_WRAPPER)[0] as HTMLElement; expect(wrapperElement.style.visibility).toEqual('hidden'); })); @@ -3206,7 +3211,7 @@ describe('igxOverlay', () => { expect(fixture.componentInstance.customComponent.nativeElement.getBoundingClientRect().height).toEqual(280); fixture.componentInstance.buttonElement.nativeElement.click(); tick(); - const componentElement =(fixture.nativeElement as HTMLElement) + const componentElement = (fixture.nativeElement as HTMLElement) .parentElement.getElementsByClassName('customList')[0] as HTMLElement; expect(componentElement).toBeDefined(); expect(componentElement.style.width).toEqual('100%'); @@ -4019,12 +4024,12 @@ describe('igxOverlay', () => { expect(overlay.closing.emit).toHaveBeenCalledTimes(1); expect(overlay.closed.emit).toHaveBeenCalledTimes(1); expect(overlay.closing.emit) - .toHaveBeenCalledWith({ - id: callId, - componentRef: jasmine.any(ComponentRef) as any, - cancel: false, - event: undefined - }); + .toHaveBeenCalledWith({ + id: callId, + componentRef: jasmine.any(ComponentRef) as any, + cancel: false, + event: undefined + }); overlay.detachAll(); overlaySettings.excludeFromOutsideClick = []; @@ -4040,12 +4045,12 @@ describe('igxOverlay', () => { expect(overlay.closing.emit).toHaveBeenCalledTimes(2); expect(overlay.closed.emit).toHaveBeenCalledTimes(2); expect(overlay.closing.emit) - .toHaveBeenCalledWith({ - id: callId, - componentRef: jasmine.any(ComponentRef) as any, - cancel: false, - event: jasmine.any(Event) as any - }); + .toHaveBeenCalledWith({ + id: callId, + componentRef: jasmine.any(ComponentRef) as any, + cancel: false, + event: jasmine.any(Event) as any + }); })); }); diff --git a/projects/igniteui-angular/src/lib/services/overlay/overlay.ts b/projects/igniteui-angular/src/lib/services/overlay/overlay.ts index 36b05b656d6..9c3452f679a 100644 --- a/projects/igniteui-angular/src/lib/services/overlay/overlay.ts +++ b/projects/igniteui-angular/src/lib/services/overlay/overlay.ts @@ -111,7 +111,7 @@ export class IgxOverlayService implements OnDestroy { * } * ``` */ - public contentAppended = new EventEmitter(); + public contentAppended = new EventEmitter(); /** * Emitted just before the overlay animation start. @@ -345,7 +345,6 @@ export class IgxOverlayService implements OnDestroy { this.addOutsideClickListener(info); this.addResizeHandler(); this.addCloseOnEscapeListener(info); - this.addModalClasses(info); this.buildAnimationPlayers(info); return info.id; } @@ -708,6 +707,7 @@ export class IgxOverlayService implements OnDestroy { // to eliminate flickering show the element just before animation start info.wrapperElement.style.visibility = ''; info.visible = true; + this.addModalClasses(info); info.openAnimationPlayer.play(); } @@ -735,6 +735,7 @@ export class IgxOverlayService implements OnDestroy { this.animationStarting.emit({ id: info.id, animationPlayer: info.closeAnimationPlayer, animationType: 'close' }); info.event = event; + this.removeModalClasses(info); info.closeAnimationPlayer.play(); } @@ -883,7 +884,17 @@ export class IgxOverlayService implements OnDestroy { const wrapperElement = info.elementRef.nativeElement.parentElement.parentElement; wrapperElement.classList.remove('igx-overlay__wrapper'); this.applyAnimationParams(wrapperElement, info.settings.positionStrategy.settings.openAnimation); - wrapperElement.classList.add('igx-overlay__wrapper--modal'); + requestAnimationFrame(() => { + wrapperElement.classList.add('igx-overlay__wrapper--modal'); + }); + } + } + + private removeModalClasses(info: OverlayInfo) { + if (info.settings.modal) { + const wrapperElement = info.elementRef.nativeElement.parentElement.parentElement; + wrapperElement.classList.remove('igx-overlay__wrapper--modal'); + wrapperElement.classList.add('igx-overlay__wrapper'); } } @@ -914,7 +925,7 @@ export class IgxOverlayService implements OnDestroy { } } - private openAnimationDone(info: OverlayInfo){ + private openAnimationDone(info: OverlayInfo) { this.opened.emit({ id: info.id, componentRef: info.componentRef }); if (info.openAnimationPlayer) { info.openAnimationPlayer.reset(); @@ -930,7 +941,7 @@ export class IgxOverlayService implements OnDestroy { } } - private closeAnimationDone(info: OverlayInfo){ + private closeAnimationDone(info: OverlayInfo) { this.closeDone(info); if (info.closeAnimationPlayer) { info.closeAnimationPlayer.reset(); diff --git a/src/app/overlay/overlay.sample.html b/src/app/overlay/overlay.sample.html index fe223423e48..8a61cb89f69 100644 --- a/src/app/overlay/overlay.sample.html +++ b/src/app/overlay/overlay.sample.html @@ -70,6 +70,10 @@ + + + +
diff --git a/src/app/overlay/overlay.sample.ts b/src/app/overlay/overlay.sample.ts index 9f4915991c8..92a323848bb 100644 --- a/src/app/overlay/overlay.sample.ts +++ b/src/app/overlay/overlay.sample.ts @@ -11,7 +11,8 @@ import { NoOpScrollStrategy, ElasticPositionStrategy, IgxDragDirective, - ContainerPositionStrategy + ContainerPositionStrategy, + IAnimationParams } from 'igniteui-angular'; @Component({ @@ -48,6 +49,7 @@ export class OverlaySampleComponent implements OnInit { public closeOnOutsideClick = true; public modal = true; public useOutlet = false; + public animationLength = 300; // in ms private xAddition = 0; private yAddition = 0; @@ -341,6 +343,10 @@ export class OverlaySampleComponent implements OnInit { this.cdr.detectChanges(); this.onChange2(); this._overlaySettings.target = this.button.nativeElement; + (this._overlaySettings.positionStrategy.settings.openAnimation.options.params as IAnimationParams).duration + = `${this.animationLength}ms`; + (this._overlaySettings.positionStrategy.settings.closeAnimation.options.params as IAnimationParams).duration + = `${this.animationLength}ms`; } this.igxDropDown.toggle(this._overlaySettings); }