Chorehancement: update to Angular v22, 'zoneless' / 'reactive' (#13114)

This commit is contained in:
shamoon
2026-07-10 00:42:16 -07:00
committed by GitHub
parent f244442c65
commit 106b41a15c
213 changed files with 5363 additions and 5842 deletions
@@ -1,7 +1,7 @@
<li ngbDropdown class="nav-item mx-1" (openChange)="onOpenChange($event)">
@if (toasts.length) {
<span class="badge rounded-pill z-3 pe-none bg-secondary me-2 position-absolute top-0 left-0">{{ toasts.length }}</span>
@if (toasts().length) {
<span class="badge rounded-pill z-3 pe-none bg-secondary me-2 position-absolute top-0 left-0">{{ toasts().length }}</span>
}
<button class="btn border-0" id="notificationsDropdown" ngbDropdownToggle>
<i-bs width="1.3em" height="1.3em" name="bell"></i-bs>
@@ -12,15 +12,15 @@
<div class="btn-group ms-auto">
<button class="btn btn-sm btn-outline-secondary mb-2 ms-auto"
(click)="toastService.clearToasts()"
[disabled]="toasts.length === 0"
[disabled]="toasts().length === 0"
i18n>Clear All</button>
</div>
</div>
@if (toasts.length === 0) {
@if (toasts().length === 0) {
<p class="text-center mb-0 small text-muted"><em i18n>No notifications</em></p>
}
<div class="scroll-list">
@for (toast of toasts; track toast.id) {
@for (toast of toasts(); track toast.id) {
<pngx-toast [autohide]="false" [toast]="toast" (hidden)="onHidden(toast)" (closed)="toastService.closeToast(toast)"></pngx-toast>
}
</div>
@@ -1,12 +1,6 @@
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'
import { provideHttpClientTesting } from '@angular/common/http/testing'
import {
ComponentFixture,
TestBed,
discardPeriodicTasks,
fakeAsync,
flush,
} from '@angular/core/testing'
import { ComponentFixture, TestBed } from '@angular/core/testing'
import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons'
import { Subject } from 'rxjs'
import { Toast, ToastService } from 'src/app/services/toast.service'
@@ -42,7 +36,8 @@ describe('ToastsDropdownComponent', () => {
let component: ToastsDropdownComponent
let fixture: ComponentFixture<ToastsDropdownComponent>
let toastService: ToastService
let toastsSubject: Subject<Toast[]> = new Subject()
let toastsSubject: Subject<Toast[]>
let getToastsSpy: jest.SpyInstance
beforeEach(async () => {
TestBed.configureTestingModule({
@@ -56,57 +51,43 @@ describe('ToastsDropdownComponent', () => {
],
}).compileComponents()
fixture = TestBed.createComponent(ToastsDropdownComponent)
toastService = TestBed.inject(ToastService)
jest.spyOn(toastService, 'getToasts').mockReturnValue(toastsSubject)
toastsSubject = new Subject()
getToastsSpy = jest
.spyOn(toastService, 'getToasts')
.mockReturnValue(toastsSubject)
fixture = TestBed.createComponent(ToastsDropdownComponent)
component = fixture.componentInstance
fixture.detectChanges()
})
it('should call getToasts and return toasts', fakeAsync(() => {
const spy = jest.spyOn(toastService, 'getToasts')
component.ngOnInit()
it('should call getToasts and return toasts', () => {
toastsSubject.next(toasts)
fixture.detectChanges()
expect(spy).toHaveBeenCalled()
expect(component.toasts).toContainEqual({
expect(getToastsSpy).toHaveBeenCalled()
expect(component.toasts()).toContainEqual({
id: 'abc-123',
content: 'foo bar',
delay: 5000,
})
})
component.ngOnDestroy()
flush()
discardPeriodicTasks()
}))
it('should show a toast', fakeAsync(() => {
component.ngOnInit()
it('should show a toast', () => {
toastsSubject.next(toasts)
fixture.detectChanges()
expect(fixture.nativeElement.textContent).toContain('foo bar')
})
component.ngOnDestroy()
flush()
discardPeriodicTasks()
}))
it('should toggle suppressPopupToasts', fakeAsync((finish) => {
component.ngOnInit()
it('should toggle suppressPopupToasts', () => {
fixture.detectChanges()
toastsSubject.next(toasts)
const spy = jest.spyOn(toastService, 'suppressPopupToasts', 'set')
component.onOpenChange(true)
expect(spy).toHaveBeenCalledWith(true)
component.ngOnDestroy()
flush()
discardPeriodicTasks()
}))
})
})
@@ -1,10 +1,11 @@
import { Component, OnDestroy, OnInit, inject } from '@angular/core'
import { Component, inject } from '@angular/core'
import { toSignal } from '@angular/core/rxjs-interop'
import {
NgbDropdownModule,
NgbProgressbarModule,
} from '@ng-bootstrap/ng-bootstrap'
import { NgxBootstrapIconsModule } from 'ngx-bootstrap-icons'
import { Subscription } from 'rxjs'
import { map } from 'rxjs'
import { Toast, ToastService } from 'src/app/services/toast.service'
import { ToastComponent } from '../../common/toast/toast.component'
@@ -19,22 +20,13 @@ import { ToastComponent } from '../../common/toast/toast.component'
NgxBootstrapIconsModule,
],
})
export class ToastsDropdownComponent implements OnInit, OnDestroy {
export class ToastsDropdownComponent {
toastService = inject(ToastService)
private subscription: Subscription
public toasts: Toast[] = []
ngOnDestroy(): void {
this.subscription?.unsubscribe()
}
ngOnInit(): void {
this.subscription = this.toastService.getToasts().subscribe((toasts) => {
this.toasts = [...toasts]
})
}
readonly toasts = toSignal(
this.toastService.getToasts().pipe(map((toasts) => [...toasts])),
{ initialValue: [] as Toast[] }
)
onOpenChange(open: boolean): void {
this.toastService.suppressPopupToasts = open