@@ -30,10 +40,17 @@ export class ApplicationHeaderComponent {
@Input()
public showTimeRange: boolean = true;
+ public pageLevelTimeRangeDisabled$: Observable
;
+
public constructor(
@Inject(GLOBAL_HEADER_HEIGHT) public readonly height: string,
- private readonly navigationService: NavigationService
- ) {}
+ private readonly navigationService: NavigationService,
+ private readonly featureStateResolver: FeatureStateResolver
+ ) {
+ this.pageLevelTimeRangeDisabled$ = this.featureStateResolver
+ .getFeatureState(ApplicationFeature.PageTimeRange)
+ .pipe(map(featureState => featureState === FeatureState.Disabled));
+ }
public onLogoClick(): void {
this.navigationService.navigateWithinApp(['']); // Empty route so we go to default screen
diff --git a/projects/components/src/header/page/page-header.component.scss b/projects/components/src/header/page/page-header.component.scss
index 03e4ffb89..07ecdfb42 100644
--- a/projects/components/src/header/page/page-header.component.scss
+++ b/projects/components/src/header/page/page-header.component.scss
@@ -5,7 +5,6 @@
margin: 24px 24px 0;
display: flex;
flex-direction: column;
-
.column-alignment {
display: flex;
flex-direction: column;
@@ -16,12 +15,14 @@
justify-content: space-between;
}
- &.bottom-border {
- padding-bottom: 16px;
- border-bottom: 1px solid $color-border;
+ .primary-row {
+ display: flex;
+ justify-content: flex-end;
+ width: 100%;
}
.breadcrumb-container {
+ margin-right: auto;
align-items: center;
.breadcrumb-separator {
@@ -43,6 +44,20 @@
}
}
+ .time-range {
+ margin-left: 10px;
+ }
+
+ .row-alignment {
+ display: flex;
+ justify-content: space-between;
+ }
+
+ &.bottom-border {
+ padding-bottom: 16px;
+ border-bottom: 1px solid $color-border;
+ }
+
.tabs {
padding-top: 16px;
}
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 48787020d..2077e08c6 100644
--- a/projects/components/src/header/page/page-header.component.test.ts
+++ b/projects/components/src/header/page/page-header.component.test.ts
@@ -1,7 +1,16 @@
-import { NavigationService, PreferenceService, SubscriptionLifecycle } from '@hypertrace/common';
+import {
+ FeatureState,
+ FeatureStateResolver,
+ NavigationService,
+ PreferenceService,
+ SubscriptionLifecycle
+} from '@hypertrace/common';
import { createHostFactory, mockProvider, Spectator } from '@ngneat/spectator/jest';
+import { MockComponent } from 'ng-mocks';
import { of } from 'rxjs';
import { BreadcrumbsService } from '../../breadcrumbs/breadcrumbs.service';
+import { FeatureConfigCheckModule } from '../../feature-check/feature-config-check.module';
+import { PageTimeRangeComponent } from '../../page-time-range/page-time-range.component';
import { PageHeaderComponent } from './page-header.component';
describe('Page Header Component', () => {
@@ -9,11 +18,16 @@ describe('Page Header Component', () => {
const createHost = createHostFactory({
component: PageHeaderComponent,
+ imports: [FeatureConfigCheckModule],
+ declarations: [MockComponent(PageTimeRangeComponent)],
shallow: true,
providers: [
mockProvider(NavigationService),
mockProvider(PreferenceService),
mockProvider(SubscriptionLifecycle),
+ mockProvider(FeatureStateResolver, {
+ getCombinedFeatureState: () => of(FeatureState.Disabled)
+ }),
mockProvider(BreadcrumbsService, {
breadcrumbs$: of([
{
@@ -33,4 +47,28 @@ describe('Page Header Component', () => {
spectator = createHost('');
expect(spectator.query('.beta')).not.toExist();
});
+
+ test('should display page time range component when feature flag is enabled', () => {
+ spectator = createHost('', {
+ providers: [
+ mockProvider(FeatureStateResolver, {
+ getCombinedFeatureState: () => of(FeatureState.Enabled)
+ })
+ ]
+ });
+
+ expect(spectator.query(PageTimeRangeComponent)).toExist();
+ });
+
+ test('should not display any time range if FF is disabled', () => {
+ spectator = createHost('', {
+ providers: [
+ mockProvider(FeatureStateResolver, {
+ getCombinedFeatureState: () => of(FeatureState.Disabled)
+ })
+ ]
+ });
+
+ expect(spectator.query(PageTimeRangeComponent)).not.toExist();
+ });
});
diff --git a/projects/components/src/header/page/page-header.component.ts b/projects/components/src/header/page/page-header.component.ts
index 2a3aa1405..5d6d7e302 100644
--- a/projects/components/src/header/page/page-header.component.ts
+++ b/projects/components/src/header/page/page-header.component.ts
@@ -1,5 +1,6 @@
import { ChangeDetectionStrategy, Component, Input, OnInit } from '@angular/core';
import {
+ ApplicationFeature,
Breadcrumb,
isNonEmptyString,
NavigationService,
@@ -23,7 +24,49 @@ import { NavigableTab } from '../../tabs/navigable/navigable-tab';
class="page-header"
[class.bottom-border]="!this.tabs?.length"
>
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ tab.label }}
+
+
+
+
+
+
+
+
@@ -39,20 +82,7 @@ import { NavigableTab } from '../../tabs/navigable/navigable-tab';
-
-
-
-
-
-
- {{ tab.label }}
-
-
+
`
})
diff --git a/projects/components/src/header/page/page-header.module.ts b/projects/components/src/header/page/page-header.module.ts
index d22db7bb3..cadb9326b 100644
--- a/projects/components/src/header/page/page-header.module.ts
+++ b/projects/components/src/header/page/page-header.module.ts
@@ -2,8 +2,10 @@ import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { BetaTagModule } from '../../beta-tag/beta-tag.module';
import { BreadcrumbsModule } from '../../breadcrumbs/breadcrumbs.module';
+import { FeatureConfigCheckModule } from '../../feature-check/feature-config-check.module';
import { IconModule } from '../../icon/icon.module';
import { LabelModule } from '../../label/label.module';
+import { PageTimeRangeModule } from '../../page-time-range/page-time-range.module';
import { NavigableTabModule } from '../../tabs/navigable/navigable-tab.module';
import { TimeRangeModule } from '../../time-range/time-range.module';
import { PageHeaderComponent } from './page-header.component';
@@ -18,7 +20,9 @@ import { PageHeaderComponent } from './page-header.component';
BreadcrumbsModule,
LabelModule,
NavigableTabModule,
- BetaTagModule
+ BetaTagModule,
+ FeatureConfigCheckModule,
+ PageTimeRangeModule
]
})
export class PageHeaderModule {}
diff --git a/projects/components/src/navigation/navigation-list-component.service.test.ts b/projects/components/src/navigation/navigation-list-component.service.test.ts
index 11929127c..bcc77d047 100644
--- a/projects/components/src/navigation/navigation-list-component.service.test.ts
+++ b/projects/components/src/navigation/navigation-list-component.service.test.ts
@@ -1,4 +1,11 @@
-import { FeatureState, FeatureStateResolver } from '@hypertrace/common';
+import {
+ FeatureState,
+ FeatureStateResolver,
+ PageTimeRangePreferenceService,
+ RelativeTimeRange,
+ TimeDuration,
+ TimeUnit
+} from '@hypertrace/common';
import { NavItemConfig, NavItemType } from '@hypertrace/components';
import { runFakeRxjs } from '@hypertrace/test-utils';
import { createServiceFactory, mockProvider } from '@ngneat/spectator/jest';
@@ -31,11 +38,17 @@ describe('Navigation List Component Service', () => {
}
];
+ const mockTimeRangeResolver = () => new RelativeTimeRange(new TimeDuration(1, TimeUnit.Hour));
+
const createService = createServiceFactory({
service: NavigationListComponentService,
providers: [
mockProvider(FeatureStateResolver, {
- getCombinedFeatureState: jest.fn().mockReturnValue(of(FeatureState.Enabled))
+ getCombinedFeatureState: jest.fn().mockReturnValue(of(FeatureState.Enabled)),
+ getFeatureState: jest.fn().mockReturnValue(of(FeatureState.Enabled))
+ }),
+ mockProvider(PageTimeRangePreferenceService, {
+ getTimeRangePreferenceForPage: jest.fn().mockReturnValue(of(mockTimeRangeResolver))
})
]
});
@@ -53,4 +66,64 @@ describe('Navigation List Component Service', () => {
});
});
});
+
+ test('should return nav items with time ranges on them', () => {
+ const mockNavItems: NavItemConfig[] = [
+ {
+ type: NavItemType.Header,
+ label: 'header 1'
+ },
+ {
+ type: NavItemType.Link,
+ icon: 'icon',
+ label: 'label-1',
+ features: ['feature'],
+ matchPaths: ['']
+ },
+ {
+ type: NavItemType.Link,
+ icon: 'icon',
+ label: 'label-2',
+ matchPaths: ['']
+ },
+ {
+ type: NavItemType.Header,
+ label: 'header 2'
+ }
+ ];
+ const spectator = createService();
+ const resolvedItems$ = spectator.service.resolveNavItemConfigTimeRanges(mockNavItems);
+
+ runFakeRxjs(({ expectObservable }) => {
+ expectObservable(resolvedItems$).toBe('(x|)', {
+ x: [
+ {
+ type: NavItemType.Header,
+ label: 'header 1'
+ },
+ {
+ type: NavItemType.Link,
+ icon: 'icon',
+ label: 'label-1',
+ features: ['feature'],
+ matchPaths: [''],
+ timeRangeResolver: mockTimeRangeResolver,
+ pageLevelTimeRangeIsEnabled: true
+ },
+ {
+ type: NavItemType.Link,
+ icon: 'icon',
+ label: 'label-2',
+ matchPaths: [''],
+ timeRangeResolver: mockTimeRangeResolver,
+ pageLevelTimeRangeIsEnabled: true
+ },
+ {
+ type: NavItemType.Header,
+ label: 'header 2'
+ }
+ ]
+ });
+ });
+ });
});
diff --git a/projects/components/src/navigation/navigation-list-component.service.ts b/projects/components/src/navigation/navigation-list-component.service.ts
index 92c5e402d..32e27c772 100644
--- a/projects/components/src/navigation/navigation-list-component.service.ts
+++ b/projects/components/src/navigation/navigation-list-component.service.ts
@@ -1,13 +1,21 @@
import { Injectable } from '@angular/core';
-import { FeatureState, FeatureStateResolver } from '@hypertrace/common';
+import {
+ ApplicationFeature,
+ FeatureState,
+ FeatureStateResolver,
+ PageTimeRangePreferenceService
+} from '@hypertrace/common';
import { isEmpty } from 'lodash-es';
import { combineLatest, Observable, of } from 'rxjs';
-import { map } from 'rxjs/operators';
+import { map, switchMap } from 'rxjs/operators';
import { NavItemConfig, NavItemHeaderConfig, NavItemLinkConfig, NavItemType } from './navigation.config';
@Injectable({ providedIn: 'root' })
export class NavigationListComponentService {
- public constructor(private readonly featureStateResolver: FeatureStateResolver) {}
+ public constructor(
+ private readonly featureStateResolver: FeatureStateResolver,
+ private readonly pageTimeRangePreferenceService: PageTimeRangePreferenceService
+ ) {}
public resolveFeaturesAndUpdateVisibilityForNavItems(navItems: NavItemConfig[]): NavItemConfig[] {
const updatedItems = this.updateLinkNavItemsVisibility(navItems);
@@ -26,6 +34,31 @@ export class NavigationListComponentService {
return updatedItems;
}
+ public resolveNavItemConfigTimeRanges(navItems: NavItemConfig[]): Observable