Skip to content

Commit 594d987

Browse files
authored
Merge branch 'main' into entity-changes
2 parents c997f0b + cea9d92 commit 594d987

File tree

11 files changed

+131
-29
lines changed

11 files changed

+131
-29
lines changed

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM nginx:1.20-alpine
1+
FROM nginx:1.21-alpine
22
COPY dist/hypertrace-ui /usr/share/nginx/html
33
COPY conf/default.conf /etc/nginx/conf.d/default.conf
44
EXPOSE 2020

projects/common/src/navigation/navigation.service.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import { Location } from '@angular/common';
22
import { Router, UrlSegment } from '@angular/router';
33
import { RouterTestingModule } from '@angular/router/testing';
4+
import { IconType } from '@hypertrace/assets-library';
5+
import { NavItemType } from '@hypertrace/components';
46
import { patchRouterNavigateForTest } from '@hypertrace/test-utils';
57
import { createServiceFactory, mockProvider, SpectatorService } from '@ngneat/spectator/jest';
68
import {
@@ -47,6 +49,7 @@ describe('Navigation Service', () => {
4749
RouterTestingModule.withRoutes([
4850
{
4951
path: 'root',
52+
data: { features: ['test-feature'] },
5053
children: [firstChildRouteConfig, secondChildRouteConfig]
5154
}
5255
])
@@ -285,4 +288,34 @@ describe('Navigation Service', () => {
285288
);
286289
}
287290
});
291+
292+
test('decorating navItem with features work as expected', () => {
293+
expect(
294+
spectator.service.decorateNavItem(
295+
{
296+
type: NavItemType.Header,
297+
label: 'Label'
298+
},
299+
spectator.service.getCurrentActivatedRoute()
300+
)
301+
).toEqual({ type: NavItemType.Header, label: 'Label' });
302+
303+
expect(
304+
spectator.service.decorateNavItem(
305+
{
306+
type: NavItemType.Link,
307+
label: 'Label',
308+
icon: IconType.None,
309+
matchPaths: ['root']
310+
},
311+
spectator.service.rootRoute()
312+
)
313+
).toEqual({
314+
type: NavItemType.Link,
315+
label: 'Label',
316+
icon: IconType.None,
317+
matchPaths: ['root'],
318+
features: ['test-feature']
319+
});
320+
});
288321
});

projects/common/src/navigation/navigation.service.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ import {
1313
UrlSegment,
1414
UrlTree
1515
} from '@angular/router';
16+
import { NavItemConfig, NavItemType } from '@hypertrace/components';
17+
import { uniq } from 'lodash-es';
1618
import { from, Observable, of } from 'rxjs';
1719
import { distinctUntilChanged, filter, map, share, skip, startWith, switchMap, take } from 'rxjs/operators';
1820
import { isEqualIgnoreFunctions, throwIfNil } from '../utilities/lang/lang-utils';
@@ -229,6 +231,26 @@ export class NavigationService {
229231
return this.findRouteConfig(path, childRoutes ? childRoutes : []);
230232
}
231233

234+
public decorateNavItem(navItem: NavItemConfig, activatedRoute: ActivatedRoute): NavItemConfig {
235+
if (navItem.type !== NavItemType.Link) {
236+
return { ...navItem };
237+
}
238+
const features = navItem.matchPaths
239+
.map(path => this.getRouteConfig([path], activatedRoute))
240+
.filter((maybeRoute): maybeRoute is HtRoute => maybeRoute !== undefined)
241+
.flatMap(route => this.getFeaturesForRoute(route))
242+
.concat(navItem.features || []);
243+
244+
return {
245+
...navItem,
246+
features: uniq(features)
247+
};
248+
}
249+
250+
private getFeaturesForRoute(route: HtRoute): string[] {
251+
return (route.data && route.data.features) || [];
252+
}
253+
232254
public rootRoute(): ActivatedRoute {
233255
return this.router.routerState.root;
234256
}

projects/components/src/select/select-option.component.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export class SelectOptionComponent<V> implements OnChanges, SelectOption<V> {
2727
@Input()
2828
public iconColor?: string;
2929

30+
@Input()
31+
public disabled?: boolean;
32+
3033
private readonly optionChangeSubject$: Subject<V> = new Subject<V>();
3134
public readonly optionChange$: Observable<V> = this.optionChangeSubject$.asObservable();
3235

projects/components/src/select/select-option.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,5 @@ export interface SelectOption<V> {
44
selectedLabel?: string;
55
icon?: string;
66
iconColor?: string;
7+
disabled?: boolean;
78
}

projects/components/src/select/select.component.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,11 @@
165165
justify-content: space-between;
166166
white-space: nowrap;
167167

168+
&.disabled {
169+
color: $gray-5;
170+
cursor: not-allowed;
171+
}
172+
168173
.select-option-info {
169174
display: flex;
170175
flex-direction: row;

projects/components/src/select/select.component.test.ts

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
22
import { fakeAsync, flush } from '@angular/core/testing';
33
import { By } from '@angular/platform-browser';
44
import { IconLibraryTestingModule, IconType } from '@hypertrace/assets-library';
5-
import { NavigationService } from '@hypertrace/common';
5+
import { MemoizeModule, NavigationService } from '@hypertrace/common';
66
import { IconComponent } from '@hypertrace/components';
77
import { createHostFactory, mockProvider, SpectatorHost } from '@ngneat/spectator/jest';
88
import { MockComponent } from 'ng-mocks';
@@ -15,7 +15,7 @@ import { SelectModule } from './select.module';
1515
describe('Select Component', () => {
1616
const hostFactory = createHostFactory<SelectComponent<string>>({
1717
component: SelectComponent,
18-
imports: [SelectModule, HttpClientTestingModule, IconLibraryTestingModule],
18+
imports: [SelectModule, HttpClientTestingModule, IconLibraryTestingModule, MemoizeModule],
1919
declareComponent: false,
2020
declarations: [MockComponent(IconComponent)],
2121
providers: [
@@ -282,4 +282,44 @@ describe('Select Component', () => {
282282
expect(onChange).toHaveBeenCalledWith('none-id');
283283
flush();
284284
}));
285+
286+
test('should disable select options as expected', fakeAsync(() => {
287+
const onChange = jest.fn();
288+
289+
spectator = hostFactory(
290+
`
291+
<ht-select (selectedChange)="onChange($event)">
292+
<ht-select-option *ngFor="let option of options" [label]="option.label" [value]="option.value" [disabled]="option.disabled"></ht-select-option>
293+
</ht-select>`,
294+
{
295+
hostProps: {
296+
options: [
297+
{ label: 'first', value: 'first-value' },
298+
{ label: 'second', value: 'second-value', disabled: true },
299+
{
300+
label: 'third',
301+
value: 'third-value',
302+
selectedLabel: 'Third Value!!!',
303+
icon: 'test-icon',
304+
iconColor: 'red'
305+
}
306+
],
307+
onChange: onChange
308+
}
309+
}
310+
);
311+
312+
spectator.tick();
313+
spectator.click('.trigger-content');
314+
315+
const optionElements = spectator.queryAll('.select-option', { root: true });
316+
expect(optionElements.length).toBe(3);
317+
expect(optionElements[0]).not.toHaveClass('disabled');
318+
expect(optionElements[1]).toHaveClass('disabled');
319+
expect(optionElements[2]).not.toHaveClass('disabled');
320+
spectator.click(optionElements[1]);
321+
322+
expect(onChange).not.toHaveBeenCalled();
323+
flush();
324+
}));
285325
});

projects/components/src/select/select.component.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ import { SelectSize } from './select-size';
114114
*ngFor="let item of items"
115115
(click)="this.onSelectionChange(item)"
116116
class="select-option"
117-
[ngClass]="this.size"
117+
[ngClass]="this.getStyleClassesForSelectItem | htMemoize: this.size:item"
118118
>
119119
<div class="select-option-info">
120120
<ht-icon
@@ -241,6 +241,10 @@ export class SelectComponent<V> implements AfterContentInit, OnChanges {
241241
}
242242

243243
public onSelectionChange(item: SelectOptionComponent<V>): void {
244+
if (item.disabled) {
245+
return;
246+
}
247+
244248
this.selected = item.value;
245249
this.selected$ = this.buildObservableOfSelected();
246250
this.selectedChange.emit(this.selected);
@@ -255,6 +259,16 @@ export class SelectComponent<V> implements AfterContentInit, OnChanges {
255259

256260
return this.items.find(item => item.value === value);
257261
}
262+
263+
public getStyleClassesForSelectItem(size: SelectSize, item: SelectOptionComponent<V>): string[] {
264+
const styles: string[] = [size];
265+
266+
if (item.disabled) {
267+
styles.push('disabled');
268+
}
269+
270+
return styles;
271+
}
258272
}
259273

260274
export const enum SelectTriggerDisplayMode {

projects/components/src/select/select.module.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { CommonModule } from '@angular/common';
22
import { NgModule } from '@angular/core';
33
import { FormsModule } from '@angular/forms';
4+
import { MemoizeModule } from '@hypertrace/common';
45
import { DividerModule } from '../divider/divider.module';
56
import { IconModule } from '../icon/icon.module';
67
import { LabelModule } from '../label/label.module';
@@ -21,7 +22,8 @@ import { SelectComponent } from './select.component';
2122
LetAsyncModule,
2223
PopoverModule,
2324
TooltipModule,
24-
DividerModule
25+
DividerModule,
26+
MemoizeModule
2527
],
2628
declarations: [SelectComponent, SelectOptionComponent, SelectGroupComponent, SelectControlOptionComponent],
2729
exports: [SelectComponent, SelectOptionComponent, SelectGroupComponent, SelectControlOptionComponent]

src/app/shared/navigation/navigation.component.test.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ describe('NavigationComponent', () => {
1818
data: {
1919
features: ['example-feature']
2020
}
21-
})
21+
}),
22+
decorateNavItem: jest.fn().mockImplementation(navItem => ({ ...navItem, features: ['example-feature'] }))
2223
}),
2324
mockProvider(ActivatedRoute),
2425
mockProvider(PreferenceService, { get: jest.fn().mockReturnValue(of(false)) })

0 commit comments

Comments
 (0)