From d2b65510967f4d5cf4ad6e513d869a45aed129d2 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Thu, 23 Sep 2021 15:40:57 -0700 Subject: [PATCH 1/6] feat: make page header configurable to persist active tab --- .../src/header/page/page-header.component.ts | 53 ++++++++++++++++--- .../navigable-tab-group.component.ts | 17 ++++-- 2 files changed, 60 insertions(+), 10 deletions(-) diff --git a/projects/components/src/header/page/page-header.component.ts b/projects/components/src/header/page/page-header.component.ts index 211b80e52..2d5997b9f 100644 --- a/projects/components/src/header/page/page-header.component.ts +++ b/projects/components/src/header/page/page-header.component.ts @@ -1,7 +1,7 @@ -import { ChangeDetectionStrategy, Component, Input } from '@angular/core'; -import { Breadcrumb } from '@hypertrace/common'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; +import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; +import { Breadcrumb, isNonEmptyString, NavigationService, PreferenceService } from '@hypertrace/common'; +import { Observable, of } from 'rxjs'; +import { first, map } from 'rxjs/operators'; import { BreadcrumbsService } from '../../breadcrumbs/breadcrumbs.service'; import { IconSize } from '../../icon/icon-size'; import { NavigableTab } from '../../tabs/navigable/navigable-tab'; @@ -34,7 +34,7 @@ import { NavigableTab } from '../../tabs/navigable/navigable-tab'; - + ` }) -export class PageHeaderComponent { +export class PageHeaderComponent implements OnInit { + @Input() + public persistenceId?: string; + @Input() public tabs?: NavigableTab[] = []; @@ -62,5 +65,41 @@ export class PageHeaderComponent { map(breadcrumbs => (breadcrumbs.length > 0 ? breadcrumbs[breadcrumbs.length - 1] : {})) ); - public constructor(protected readonly breadcrumbsService: BreadcrumbsService) {} + public constructor( + protected readonly navigationService: NavigationService, + protected readonly preferenceService: PreferenceService, + protected readonly breadcrumbsService: BreadcrumbsService + ) {} + + public ngOnInit(): void { + this.getPreferences().subscribe(preferences => { + if (isNonEmptyString(this.persistenceId) && isNonEmptyString(preferences.selectedTabPath)) { + this.navigationService.navigateWithinApp(preferences.selectedTabPath, this.navigationService.getCurrentActivatedRoute().parent!); + } + }) + } + + public onTabChange(path?: string): void { + this.setPreferences(path); + } + + private getPreferences(): Observable { + return isNonEmptyString(this.persistenceId) + ? this.preferenceService.get(this.persistenceId, {}).pipe( + first() + ) + : of({}); + } + + private setPreferences(selectedTabPath?: string): void { + if (isNonEmptyString(this.persistenceId)) { + this.preferenceService.set(this.persistenceId, { + selectedTabPath: selectedTabPath + }); + } + } +} + +interface PageHeaderPreferences { + selectedTabPath?: string; } diff --git a/projects/components/src/tabs/navigable/navigable-tab-group.component.ts b/projects/components/src/tabs/navigable/navigable-tab-group.component.ts index 5230ff0cd..04d005e18 100644 --- a/projects/components/src/tabs/navigable/navigable-tab-group.component.ts +++ b/projects/components/src/tabs/navigable/navigable-tab-group.component.ts @@ -1,8 +1,15 @@ -import { AfterContentInit, ChangeDetectionStrategy, Component, ContentChildren, QueryList } from '@angular/core'; +import { + AfterContentInit, + ChangeDetectionStrategy, + Component, + ContentChildren, EventEmitter, + Output, + QueryList +} from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Color, FeatureState, NavigationParams, NavigationParamsType, NavigationService } from '@hypertrace/common'; import { merge, Observable } from 'rxjs'; -import { map, startWith } from 'rxjs/operators'; +import { map, startWith, tap } from 'rxjs/operators'; import { NavigableTabComponent } from './navigable-tab.component'; @Component({ @@ -47,6 +54,9 @@ export class NavigableTabGroupComponent implements AfterContentInit { @ContentChildren(NavigableTabComponent) public tabs!: QueryList; + @Output() + public readonly tabChange: EventEmitter = new EventEmitter(); + public activeTab$?: Observable; public constructor( @@ -57,7 +67,8 @@ export class NavigableTabGroupComponent implements AfterContentInit { public ngAfterContentInit(): void { this.activeTab$ = merge(this.navigationService.navigation$, this.tabs.changes).pipe( startWith(undefined), - map(() => this.findActiveTab()) + map(() => this.findActiveTab()), + tap(activeTab => this.tabChange.emit(activeTab?.path)) ); } From f0ed30ac31ab5287f58e6926550170c77bd1e354 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Thu, 23 Sep 2021 15:48:56 -0700 Subject: [PATCH 2/6] style: prettier --- .../src/header/page/page-header.component.ts | 11 ++++++----- .../tabs/navigable/navigable-tab-group.component.ts | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/projects/components/src/header/page/page-header.component.ts b/projects/components/src/header/page/page-header.component.ts index 2d5997b9f..2f923cef5 100644 --- a/projects/components/src/header/page/page-header.component.ts +++ b/projects/components/src/header/page/page-header.component.ts @@ -74,9 +74,12 @@ export class PageHeaderComponent implements OnInit { public ngOnInit(): void { this.getPreferences().subscribe(preferences => { if (isNonEmptyString(this.persistenceId) && isNonEmptyString(preferences.selectedTabPath)) { - this.navigationService.navigateWithinApp(preferences.selectedTabPath, this.navigationService.getCurrentActivatedRoute().parent!); + this.navigationService.navigateWithinApp( + preferences.selectedTabPath, + this.navigationService.getCurrentActivatedRoute().parent! + ); } - }) + }); } public onTabChange(path?: string): void { @@ -85,9 +88,7 @@ export class PageHeaderComponent implements OnInit { private getPreferences(): Observable { return isNonEmptyString(this.persistenceId) - ? this.preferenceService.get(this.persistenceId, {}).pipe( - first() - ) + ? this.preferenceService.get(this.persistenceId, {}).pipe(first()) : of({}); } diff --git a/projects/components/src/tabs/navigable/navigable-tab-group.component.ts b/projects/components/src/tabs/navigable/navigable-tab-group.component.ts index 04d005e18..998a51a3c 100644 --- a/projects/components/src/tabs/navigable/navigable-tab-group.component.ts +++ b/projects/components/src/tabs/navigable/navigable-tab-group.component.ts @@ -2,7 +2,8 @@ import { AfterContentInit, ChangeDetectionStrategy, Component, - ContentChildren, EventEmitter, + ContentChildren, + EventEmitter, Output, QueryList } from '@angular/core'; From c78733ccfcb652ea7ec581a8d778e400387eccac Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Thu, 23 Sep 2021 16:49:27 -0700 Subject: [PATCH 3/6] test: add mockProviders --- .../components/src/header/page/page-header.component.test.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/projects/components/src/header/page/page-header.component.test.ts b/projects/components/src/header/page/page-header.component.test.ts index fd4f534c5..470586fd9 100644 --- a/projects/components/src/header/page/page-header.component.test.ts +++ b/projects/components/src/header/page/page-header.component.test.ts @@ -1,3 +1,4 @@ +import { NavigationService, PreferenceService } from '@hypertrace/common'; import { createHostFactory, mockProvider, Spectator } from '@ngneat/spectator/jest'; import { of } from 'rxjs'; import { BreadcrumbsService } from '../../breadcrumbs/breadcrumbs.service'; @@ -10,6 +11,8 @@ describe('Page Header Component', () => { component: PageHeaderComponent, shallow: true, providers: [ + mockProvider(NavigationService), + mockProvider(PreferenceService), mockProvider(BreadcrumbsService, { breadcrumbs$: of([ { From 8f5faaeefe735467388ac4e42adf8ce1462a9f3c Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Mon, 27 Sep 2021 12:24:00 -0700 Subject: [PATCH 4/6] feat: add subscription lifecycle --- .../header/page/page-header.component.test.ts | 3 +- .../src/header/page/page-header.component.ts | 29 +++++++++++++------ 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/projects/components/src/header/page/page-header.component.test.ts b/projects/components/src/header/page/page-header.component.test.ts index 470586fd9..48787020d 100644 --- a/projects/components/src/header/page/page-header.component.test.ts +++ b/projects/components/src/header/page/page-header.component.test.ts @@ -1,4 +1,4 @@ -import { NavigationService, PreferenceService } from '@hypertrace/common'; +import { NavigationService, PreferenceService, SubscriptionLifecycle } from '@hypertrace/common'; import { createHostFactory, mockProvider, Spectator } from '@ngneat/spectator/jest'; import { of } from 'rxjs'; import { BreadcrumbsService } from '../../breadcrumbs/breadcrumbs.service'; @@ -13,6 +13,7 @@ describe('Page Header Component', () => { providers: [ mockProvider(NavigationService), mockProvider(PreferenceService), + mockProvider(SubscriptionLifecycle), mockProvider(BreadcrumbsService, { breadcrumbs$: of([ { diff --git a/projects/components/src/header/page/page-header.component.ts b/projects/components/src/header/page/page-header.component.ts index 2f923cef5..26dbb5afd 100644 --- a/projects/components/src/header/page/page-header.component.ts +++ b/projects/components/src/header/page/page-header.component.ts @@ -1,5 +1,11 @@ import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core'; -import { Breadcrumb, isNonEmptyString, NavigationService, PreferenceService } from '@hypertrace/common'; +import { + Breadcrumb, + isNonEmptyString, + NavigationService, + PreferenceService, + SubscriptionLifecycle +} from '@hypertrace/common'; import { Observable, of } from 'rxjs'; import { first, map } from 'rxjs/operators'; import { BreadcrumbsService } from '../../breadcrumbs/breadcrumbs.service'; @@ -68,18 +74,23 @@ export class PageHeaderComponent implements OnInit { public constructor( protected readonly navigationService: NavigationService, protected readonly preferenceService: PreferenceService, + protected readonly subscriptionLifecycle: SubscriptionLifecycle, protected readonly breadcrumbsService: BreadcrumbsService ) {} public ngOnInit(): void { - this.getPreferences().subscribe(preferences => { - if (isNonEmptyString(this.persistenceId) && isNonEmptyString(preferences.selectedTabPath)) { - this.navigationService.navigateWithinApp( - preferences.selectedTabPath, - this.navigationService.getCurrentActivatedRoute().parent! - ); - } - }); + this.subscriptionLifecycle.add( + this.getPreferences().subscribe(preferences => this.navigateIfPersistedActiveTab(preferences)) + ); + } + + private navigateIfPersistedActiveTab(preferences: PageHeaderPreferences): void { + if (isNonEmptyString(this.persistenceId) && isNonEmptyString(preferences.selectedTabPath)) { + this.navigationService.navigateWithinApp( + preferences.selectedTabPath, + this.navigationService.getCurrentActivatedRoute().parent! + ); + } } public onTabChange(path?: string): void { From e763c0cf68c85731d3540ea0335fa034c1ec8614 Mon Sep 17 00:00:00 2001 From: Jake Bassett Date: Mon, 27 Sep 2021 12:47:39 -0700 Subject: [PATCH 5/6] feat: fix provider --- projects/components/src/header/page/page-header.component.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/projects/components/src/header/page/page-header.component.ts b/projects/components/src/header/page/page-header.component.ts index 26dbb5afd..4d63c9354 100644 --- a/projects/components/src/header/page/page-header.component.ts +++ b/projects/components/src/header/page/page-header.component.ts @@ -16,6 +16,7 @@ import { NavigableTab } from '../../tabs/navigable/navigable-tab'; selector: 'ht-page-header', styleUrls: ['./page-header.component.scss'], changeDetection: ChangeDetectionStrategy.OnPush, + providers: [SubscriptionLifecycle], template: `
Date: Mon, 27 Sep 2021 16:21:46 -0700 Subject: [PATCH 6/6] feat: add distinctUntilChanged --- .../src/tabs/navigable/navigable-tab-group.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/projects/components/src/tabs/navigable/navigable-tab-group.component.ts b/projects/components/src/tabs/navigable/navigable-tab-group.component.ts index 998a51a3c..8b0150a44 100644 --- a/projects/components/src/tabs/navigable/navigable-tab-group.component.ts +++ b/projects/components/src/tabs/navigable/navigable-tab-group.component.ts @@ -10,7 +10,7 @@ import { import { ActivatedRoute } from '@angular/router'; import { Color, FeatureState, NavigationParams, NavigationParamsType, NavigationService } from '@hypertrace/common'; import { merge, Observable } from 'rxjs'; -import { map, startWith, tap } from 'rxjs/operators'; +import { distinctUntilChanged, map, startWith, tap } from 'rxjs/operators'; import { NavigableTabComponent } from './navigable-tab.component'; @Component({ @@ -69,6 +69,7 @@ export class NavigableTabGroupComponent implements AfterContentInit { this.activeTab$ = merge(this.navigationService.navigation$, this.tabs.changes).pipe( startWith(undefined), map(() => this.findActiveTab()), + distinctUntilChanged(), tap(activeTab => this.tabChange.emit(activeTab?.path)) ); }