Skip to content

Commit 3f1106c

Browse files
fix(dialog): render asynchronous open state in zoneless apps (#17447)
* fix(dialog): render asynchronous open state in zoneless apps * fix(dialog): handle potential null for dialog window client rects --------- Co-authored-by: Radoslav Karaivanov <rkaraivanov@infragistics.com>
1 parent 771b480 commit 3f1106c

2 files changed

Lines changed: 60 additions & 2 deletions

File tree

projects/igniteui-angular/dialog/src/dialog/dialog.component.spec.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, ViewChild, ChangeDetectionStrategy } from '@angular/core';
1+
import { Component, ViewChild, ChangeDetectionStrategy, provideZonelessChangeDetection, signal } from '@angular/core';
22
import { TestBed, fakeAsync, tick, waitForAsync } from '@angular/core/testing';
33
import { By } from '@angular/platform-browser';
44
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
@@ -691,3 +691,59 @@ class PositionSettingsDialogComponent {
691691
};
692692

693693
}
694+
695+
@Component({
696+
template: `
697+
<igx-dialog #dialog title="dialog" message="message" [isOpen]="open()"></igx-dialog>`,
698+
changeDetection: ChangeDetectionStrategy.Eager,
699+
imports: [IgxDialogComponent]
700+
})
701+
class ZonelessDialogHostComponent {
702+
@ViewChild('dialog', { static: true }) public dialog: IgxDialogComponent;
703+
public readonly open = signal(false);
704+
}
705+
706+
describe('Dialog - zoneless change detection', () => {
707+
const DIALOG_WINDOW = '.igx-dialog__window';
708+
709+
const nextFrame = () => new Promise<void>((resolve) => requestAnimationFrame(() => resolve()));
710+
711+
beforeEach(waitForAsync(() => {
712+
TestBed.configureTestingModule({
713+
imports: [NoopAnimationsModule, ZonelessDialogHostComponent],
714+
providers: [provideZonelessChangeDetection()]
715+
}).compileComponents();
716+
}));
717+
718+
afterEach(() => {
719+
UIInteractions.clearOverlay();
720+
});
721+
722+
it('renders the dialog when isOpen is set from an asynchronous callback', async () => {
723+
const fixture = TestBed.createComponent(ZonelessDialogHostComponent);
724+
fixture.detectChanges();
725+
726+
const host = fixture.debugElement.query(By.css('igx-dialog')).nativeElement as HTMLElement;
727+
const dialog = fixture.componentInstance.dialog;
728+
729+
await new Promise<void>(resolve => {
730+
setTimeout(() => {
731+
fixture.componentInstance.open.set(true);
732+
resolve();
733+
});
734+
});
735+
await fixture.whenStable();
736+
await nextFrame();
737+
await fixture.whenStable();
738+
739+
const dialogWindow = document.querySelector<HTMLElement>(DIALOG_WINDOW);
740+
expect(dialog.isOpen).toBeTrue();
741+
expect(dialog.isCollapsed).toBeFalse();
742+
expect(dialogWindow).withContext('dialog window is in the DOM').not.toBeNull();
743+
expect(dialogWindow?.getClientRects().length ?? 0)
744+
.withContext('dialog window is actually rendered').toBeGreaterThan(0);
745+
expect(host.classList.contains('igx-dialog--hidden'))
746+
.withContext('host class mirrors isCollapsed')
747+
.toBeFalse();
748+
});
749+
});

projects/igniteui-angular/dialog/src/dialog/dialog.component.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, ElementRef, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output, ViewChild, AfterContentInit, booleanAttribute, inject, ChangeDetectionStrategy } from '@angular/core';
1+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, EventEmitter, HostBinding, Input, OnDestroy, OnInit, Output, ViewChild, AfterContentInit, booleanAttribute, inject } from '@angular/core';
22
import { Subject } from 'rxjs';
33
import { takeUntil } from 'rxjs/operators';
44
import { IgxNavigationService, IToggleView } from 'igniteui-angular/core';
@@ -46,6 +46,7 @@ let DIALOG_ID = 0;
4646
imports: [IgxToggleDirective, IgxFocusTrapDirective, IgxFocusDirective, IgxButtonDirective, IgxRippleDirective]
4747
})
4848
export class IgxDialogComponent implements IToggleView, OnInit, OnDestroy, AfterContentInit {
49+
private cdr = inject(ChangeDetectorRef);
4950
private elementRef = inject(ElementRef);
5051
private navService = inject(IgxNavigationService, { optional: true });
5152

@@ -370,6 +371,7 @@ export class IgxDialogComponent implements IToggleView, OnInit, OnDestroy, After
370371
if (value) {
371372
requestAnimationFrame(() => {
372373
this.open();
374+
this.cdr.markForCheck();
373375
});
374376
} else {
375377
this.close();

0 commit comments

Comments
 (0)