Skip to content

Commit

Permalink
fix(tabs): make tabs ssr ready (#1400)
Browse files Browse the repository at this point in the history
  • Loading branch information
Alexander Sawtschuk authored and GitHub Enterprise committed Dec 5, 2024
1 parent 9a92304 commit 77f4235
Show file tree
Hide file tree
Showing 4 changed files with 46 additions and 12 deletions.
17 changes: 11 additions & 6 deletions projects/ng-aquila/src/tabs/scrollable-tab-bar.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Direction, Directionality } from '@angular/cdk/bidi';
import { AfterContentInit, ChangeDetectorRef, Directive, ElementRef, OnDestroy, Optional, QueryList } from '@angular/core';
import { Platform } from '@angular/cdk/platform';
import { AfterContentInit, ChangeDetectorRef, Directive, ElementRef, inject, Injector, OnDestroy, Optional, QueryList } from '@angular/core';
import { NxViewportService } from '@aposin/ng-aquila/utils';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
Expand All @@ -18,6 +19,8 @@ export abstract class NxScrollableTabBar implements AfterContentInit, OnDestroy
_isScrolledToEnd = true;

private readonly _destroyed = new Subject<void>();
protected readonly _platform = inject(Platform);
protected readonly _injector = inject(Injector);

constructor(
protected readonly _cdr: ChangeDetectorRef,
Expand All @@ -37,11 +40,13 @@ export abstract class NxScrollableTabBar implements AfterContentInit, OnDestroy
}

ngAfterContentInit(): void {
this.tabButtons.changes.pipe(takeUntil(this._destroyed)).subscribe(() => setTimeout(() => this._updateScrollButtons()));
setTimeout(() => {
this.scrollableTabsList.nativeElement.addEventListener('scroll', this._scrollHandler);
this._updateScrollButtons();
});
if (this._platform.isBrowser) {
this.tabButtons.changes.pipe(takeUntil(this._destroyed)).subscribe(() => setTimeout(() => this._updateScrollButtons()));
setTimeout(() => {
this.scrollableTabsList.nativeElement.addEventListener('scroll', this._scrollHandler);
this._updateScrollButtons();
});
}
}

ngOnDestroy(): void {
Expand Down
2 changes: 1 addition & 1 deletion projects/ng-aquila/src/tabs/tab-header.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export class NxTabHeaderComponent extends NxScrollableTabBar implements AfterCon
}

scrollToButton(index: number) {
if (!this.labels || !this.scrollableTabsList) {
if (!this._platform.isBrowser || !this.labels || !this.scrollableTabsList) {
return;
}
const container = this.scrollableTabsList.nativeElement;
Expand Down
18 changes: 16 additions & 2 deletions projects/ng-aquila/src/utils/viewport.service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { fakeAsync, inject, tick } from '@angular/core/testing';
import { Subscription } from 'rxjs';
import { PLATFORM_ID } from '@angular/core';
import { fakeAsync, inject, TestBed, tick } from '@angular/core/testing';
import { EMPTY, Subscription } from 'rxjs';
import { take } from 'rxjs/operators';

import { NxBreakpoints, NxViewportService } from './viewport.service';
Expand Down Expand Up @@ -408,4 +409,17 @@ describe('NxViewportService', () => {
expect(isTablet).toBeTrue();
}));
});

it('Platform != Browser: should return EMPTY observable on all methods', () => {
TestBed.resetTestingModule();
TestBed.configureTestingModule({ providers: [{ provide: PLATFORM_ID, useValue: 'NOT_BROWSER' }] });

const service = TestBed.inject(NxViewportService);
expect(service.viewportChange$).toBe(EMPTY);
expect(service.min(NxBreakpoints.BREAKPOINT_LARGE)).toBe(EMPTY);
expect(service.max(NxBreakpoints.BREAKPOINT_LARGE)).toBe(EMPTY);
expect(service.between(NxBreakpoints.BREAKPOINT_LARGE, NxBreakpoints.BREAKPOINT_3XLARGE)).toBe(EMPTY);

TestBed.resetTestingModule();
});
});
21 changes: 18 additions & 3 deletions projects/ng-aquila/src/utils/viewport.service.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Injectable } from '@angular/core';
import { asyncScheduler, fromEvent, Observable } from 'rxjs';
import { Platform } from '@angular/cdk/platform';
import { inject, Injectable } from '@angular/core';
import { asyncScheduler, EMPTY, fromEvent, Observable } from 'rxjs';
import { map, startWith, throttleTime } from 'rxjs/operators';

/** Available breakpoints to subscribe to. */
Expand All @@ -25,10 +26,16 @@ const DEFAULT_THROTTLE_TIME = 200;
providedIn: 'root',
})
export class NxViewportService {
readonly viewportChange$: Observable<number> = fromEvent(window, 'resize').pipe(map(() => window.innerWidth));
private _platform = inject(Platform);

readonly viewportChange$: Observable<number> = this._platform.isBrowser ? fromEvent(window, 'resize').pipe(map(() => window.innerWidth)) : EMPTY;

/** Returns whether the current viewport width is greater than or equal (>=) to minSize. */
min(minSize: NxBreakpoints, throttleTimeMs = DEFAULT_THROTTLE_TIME): Observable<boolean> {
if (!this._platform.isBrowser) {
return EMPTY;
}

return this.viewportChange$.pipe(
startWith(window.innerWidth),
throttleTime(throttleTimeMs, asyncScheduler, { trailing: true }),
Expand All @@ -38,6 +45,10 @@ export class NxViewportService {

/** Returns whether the current viewport width is lower (<) than maxSize. */
max(maxSize: NxBreakpoints, throttleTimeMs = DEFAULT_THROTTLE_TIME): Observable<boolean> {
if (!this._platform.isBrowser) {
return EMPTY;
}

return this.viewportChange$.pipe(
startWith(window.innerWidth),
throttleTime(throttleTimeMs, asyncScheduler, { trailing: true }),
Expand All @@ -47,6 +58,10 @@ export class NxViewportService {

/** Returns whether the current viewport width is greater than or equal (>=) to minSize and lower (<) than maxSize. */
between(minSize: NxBreakpoints, maxSize: NxBreakpoints, throttleTimeMs = DEFAULT_THROTTLE_TIME): Observable<boolean> {
if (!this._platform.isBrowser) {
return EMPTY;
}

return this.viewportChange$.pipe(
startWith(window.innerWidth),
throttleTime(throttleTimeMs, asyncScheduler, { trailing: true }),
Expand Down

0 comments on commit 77f4235

Please sign in to comment.