From 6d57965bb987d803e02aab2aa871fdee921a95c6 Mon Sep 17 00:00:00 2001 From: giulio-leone Date: Wed, 25 Mar 2026 07:33:57 +0100 Subject: [PATCH] fix(navscroll): do not auto-scroll to first section on page load The selected observable subscription in #initViewScrollerSubscription used to scroll to the first section immediately on init because NavscrollStore.init() programmatically sets the first item as selected. Add skip(1) after filter(Boolean) to ignore the initial programmatic selection and only scroll on subsequent user-initiated selections. Closes #597 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../navscroll/navscroll.component.spec.ts | 78 +++++++++++++++++++ .../navscroll/navscroll.component.ts | 5 +- 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.spec.ts diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.spec.ts b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.spec.ts new file mode 100644 index 00000000..d2adf72c --- /dev/null +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.spec.ts @@ -0,0 +1,78 @@ +import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; +import { ViewportScroller } from '@angular/common'; +import { RouterTestingModule } from '@angular/router/testing'; +import { TranslateModule } from '@ngx-translate/core'; + +import { ItNavscrollComponent } from './navscroll.component'; +import { NavscrollItem } from './navscroll.model'; + +function makeItems(): NavscrollItem[] { + return [ + { title: 'Section One', href: 'section-1', text: 'Content one', childs: [] }, + { title: 'Section Two', href: 'section-2', text: 'Content two', childs: [] }, + { title: 'Section Three', href: 'section-3', text: 'Content three', childs: [] }, + ]; +} + +describe('ItNavscrollComponent', () => { + let fixture: ComponentFixture; + let component: ItNavscrollComponent; + let scroller: ViewportScroller; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ItNavscrollComponent, RouterTestingModule, TranslateModule.forRoot()], + }).compileComponents(); + + scroller = TestBed.inject(ViewportScroller); + fixture = TestBed.createComponent(ItNavscrollComponent); + component = fixture.componentInstance; + }); + + it('should create', () => { + component.items = makeItems(); + fixture.detectChanges(); + expect(component).toBeTruthy(); + }); + + it('should NOT auto-scroll to first section on initial load', fakeAsync(() => { + const scrollSpy = spyOn(scroller, 'scrollToAnchor'); + component.items = makeItems(); + fixture.detectChanges(); + tick(100); + + // The programmatic init selection must NOT trigger scrollToAnchor + expect(scrollSpy).not.toHaveBeenCalled(); + })); + + it('should render all section headings', () => { + component.items = makeItems(); + fixture.detectChanges(); + + const el: HTMLElement = fixture.nativeElement; + const headings = el.querySelectorAll('.it-page-section'); + expect(headings.length).toBe(3); + expect(headings[0].textContent?.trim()).toBe('Section One'); + expect(headings[1].textContent?.trim()).toBe('Section Two'); + expect(headings[2].textContent?.trim()).toBe('Section Three'); + }); + + it('should display the header text', () => { + component.items = makeItems(); + component.header = 'Navigation'; + fixture.detectChanges(); + + const el: HTMLElement = fixture.nativeElement; + const h3 = el.querySelector('h3'); + expect(h3?.textContent?.trim()).toBe('Navigation'); + }); + + it('should render nav links for each item', () => { + component.items = makeItems(); + fixture.detectChanges(); + + const el: HTMLElement = fixture.nativeElement; + const links = el.querySelectorAll('.nav-link'); + expect(links.length).toBe(3); + }); +}); diff --git a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts index b9cbf6b0..8f89750d 100644 --- a/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts +++ b/projects/design-angular-kit/src/lib/components/navigation/navscroll/navscroll.component.ts @@ -14,7 +14,7 @@ import { import { takeUntilDestroyed } from '@angular/core/rxjs-interop'; import { RouterLink, RouterLinkActive, RouterLinkWithHref } from '@angular/router'; import { TranslateModule } from '@ngx-translate/core'; -import { delay, filter, map, tap, withLatestFrom } from 'rxjs'; +import { delay, filter, map, skip, tap, withLatestFrom } from 'rxjs'; import { ItNavscrollListItemsComponent } from './navscroll-list-items.component'; import { NavscrollItem } from './navscroll.model'; import { NavscrollStore } from './navscroll.store'; @@ -128,6 +128,9 @@ export class ItNavscrollComponent implements OnInit { takeUntilDestroyed(this.#destroyRef), filter(selected => Boolean(selected)), map(v => v as NavscrollItem), + // Skip the programmatic selection from store.init() so the page + // does not scroll to the first section on initial load (#597). + skip(1), delay(0), //WA tap({ next: ({ href }) => {