Skip to content

Commit de79bb9

Browse files
feat: custom messages for load async (#1217)
1 parent 981e14b commit de79bb9

File tree

16 files changed

+188
-29
lines changed

16 files changed

+188
-29
lines changed
30.2 KB
Loading
72.7 KB
Loading
7.73 KB
Loading
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const enum ImagesAssetPath {
2+
ErrorPage = 'assets/images/error-page.svg',
3+
LoaderSpinner = 'assets/images/loader-spinner.gif',
4+
LoaderPage = 'assets/images/loader-page.gif',
5+
LoaderExpandableRow = 'assets/images/loader-expandable-row.gif'
6+
}

projects/assets-library/src/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ export * from './icons/icon-type';
66
export * from './icons/icon-registry.service';
77
export * from './icons/icon-library.module';
88
export * from './icons/testing/icon-library-testing.module';
9+
export * from './images/image-type';

projects/components/src/load-async/load-async.directive.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
ViewContainerRef
1111
} from '@angular/core';
1212
import { Observable, ReplaySubject } from 'rxjs';
13-
import { LoadAsyncContext, LoadAsyncService } from './load-async.service';
13+
import { LoadAsyncConfig, LoadAsyncContext, LoadAsyncService } from './load-async.service';
1414
import {
1515
ASYNC_WRAPPER_PARAMETERS$,
1616
LoadAsyncWrapperComponent,
@@ -23,6 +23,10 @@ import {
2323
export class LoadAsyncDirective implements OnChanges, OnDestroy {
2424
@Input('htLoadAsync')
2525
public data$?: Observable<unknown>;
26+
27+
// tslint:disable-next-line:no-input-rename
28+
@Input('htLoadAsyncConfig')
29+
public config?: LoadAsyncConfig;
2630
private readonly wrapperParamsSubject: ReplaySubject<LoadAsyncWrapperParameters> = new ReplaySubject(1);
2731
private readonly wrapperInjector: Injector;
2832
private wrapperView?: ComponentRef<LoadAsyncWrapperComponent>;
@@ -49,7 +53,8 @@ export class LoadAsyncDirective implements OnChanges, OnDestroy {
4953
this.wrapperView = this.wrapperView || this.buildWrapperView();
5054
this.wrapperParamsSubject.next({
5155
state$: this.loadAsyncService.mapObservableState(this.data$),
52-
content: this.templateRef
56+
content: this.templateRef,
57+
config: this.config
5358
});
5459
} else {
5560
// If observable is cleared, clear the DOM

projects/components/src/load-async/load-async.service.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Injectable } from '@angular/core';
2+
import { IconType } from '@hypertrace/assets-library';
23
import { CustomError } from '@hypertrace/common';
34
import { Observable, of } from 'rxjs';
45
import { catchError, defaultIfEmpty, map, startWith } from 'rxjs/operators';
@@ -50,18 +51,39 @@ export interface LoadAsyncContext {
5051
$implicit: unknown;
5152
}
5253

53-
export type AsyncState = ErrorAsyncState | SuccessAsyncState | LoadingAsyncState;
54+
export interface LoadAsyncConfig {
55+
load?: LoadingStateConfig;
56+
noData?: NoDataOrErrorStateConfig;
57+
error?: NoDataOrErrorStateConfig;
58+
}
59+
60+
export type AsyncState = LoadingAsyncState | SuccessAsyncState | NoDataOrErrorAsyncState;
61+
62+
export const enum LoaderType {
63+
Spinner = 'spinner',
64+
ExpandableRow = 'expandable-row',
65+
Page = 'page'
66+
}
5467

5568
interface LoadingAsyncState {
5669
type: LoadAsyncStateType.Loading;
5770
}
58-
5971
interface SuccessAsyncState {
6072
type: LoadAsyncStateType.Success;
6173
context: LoadAsyncContext;
6274
}
6375

64-
export interface ErrorAsyncState {
76+
interface NoDataOrErrorAsyncState {
6577
type: LoadAsyncStateType.GenericError | LoadAsyncStateType.NoData;
6678
description?: string;
6779
}
80+
81+
interface LoadingStateConfig {
82+
loaderType?: LoaderType;
83+
}
84+
85+
interface NoDataOrErrorStateConfig {
86+
icon?: IconType;
87+
title?: string;
88+
description?: string;
89+
}

projects/components/src/load-async/loader/loader.component.scss

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,19 @@
77
flex-direction: column;
88
justify-content: center;
99
align-items: center;
10+
11+
.page {
12+
height: 50px;
13+
width: 50px;
14+
}
15+
16+
.spinner {
17+
height: 20px;
18+
width: 20px;
19+
}
20+
21+
.expandable-row {
22+
height: 20px;
23+
width: auto;
24+
}
1025
}
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
import { CommonModule } from '@angular/common';
2+
import { ImagesAssetPath } from '@hypertrace/assets-library';
3+
import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
4+
import { LoaderType } from '../load-async.service';
5+
import { LoaderComponent } from './loader.component';
6+
7+
describe('Loader component', () => {
8+
let spectator: SpectatorHost<LoaderComponent>;
9+
10+
const createHost = createHostFactory({
11+
component: LoaderComponent,
12+
imports: [CommonModule]
13+
});
14+
15+
test('Loader component when loader type is page', () => {
16+
spectator = createHost(`<ht-loader [loaderType]="'${LoaderType.Page}'"></ht-loader>`);
17+
18+
expect(spectator.query('.ht-loader')).toExist();
19+
expect(spectator.query('.ht-loader img')).toExist();
20+
expect(spectator.query('.ht-loader img')).toHaveClass(LoaderType.Page);
21+
expect(spectator.query('.ht-loader img')).toHaveAttribute('src', ImagesAssetPath.LoaderPage);
22+
});
23+
24+
test('Loader component when loader type is not passed', () => {
25+
spectator = createHost(`<ht-loader></ht-loader>`);
26+
27+
expect(spectator.query('.ht-loader')).toExist();
28+
expect(spectator.query('.ht-loader img')).toExist();
29+
expect(spectator.query('.ht-loader img')).toHaveClass(LoaderType.Spinner);
30+
expect(spectator.query('.ht-loader img')).toHaveAttribute('src', ImagesAssetPath.LoaderSpinner);
31+
});
32+
33+
test('Loader component when loader type is spinner', () => {
34+
spectator = createHost(`<ht-loader [loaderType]="'${LoaderType.Spinner}'"></ht-loader>`);
35+
36+
expect(spectator.query('.ht-loader')).toExist();
37+
expect(spectator.query('.ht-loader img')).toExist();
38+
expect(spectator.query('.ht-loader img')).toHaveClass(LoaderType.Spinner);
39+
expect(spectator.query('.ht-loader img')).toHaveAttribute('src', ImagesAssetPath.LoaderSpinner);
40+
});
41+
42+
test('Loader component loader type is expandable row', () => {
43+
spectator = createHost(`<ht-loader [loaderType]="'${LoaderType.ExpandableRow}'"></ht-loader>`);
44+
45+
expect(spectator.query('.ht-loader')).toExist();
46+
expect(spectator.query('.ht-loader img')).toExist();
47+
expect(spectator.query('.ht-loader img')).toHaveClass(LoaderType.ExpandableRow);
48+
expect(spectator.query('.ht-loader img')).toHaveAttribute('src', ImagesAssetPath.LoaderExpandableRow);
49+
});
50+
});
Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,36 @@
1-
import { ChangeDetectionStrategy, Component } from '@angular/core';
2-
import { IconType } from '@hypertrace/assets-library';
3-
import { IconSize } from '../../icon/icon-size';
1+
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core';
2+
import { ImagesAssetPath } from '@hypertrace/assets-library';
3+
import { LoaderType } from '../load-async.service';
44

55
@Component({
66
selector: 'ht-loader',
77
styleUrls: ['./loader.component.scss'],
8+
changeDetection: ChangeDetectionStrategy.OnPush,
89
template: `
910
<div class="ht-loader">
10-
<ht-icon icon="${IconType.Loading}" size="${IconSize.Large}"></ht-icon>
11+
<img [ngClass]="[this.currentLoaderType]" [src]="this.getImagePathFromType(this.currentLoaderType)" />
1112
</div>
12-
`,
13-
changeDetection: ChangeDetectionStrategy.OnPush
13+
`
1414
})
15-
export class LoaderComponent {}
15+
export class LoaderComponent implements OnChanges {
16+
@Input()
17+
public loaderType?: LoaderType;
18+
19+
public currentLoaderType: LoaderType = LoaderType.Spinner;
20+
21+
public ngOnChanges(): void {
22+
this.currentLoaderType = this.loaderType ?? LoaderType.Spinner;
23+
}
24+
25+
public getImagePathFromType(loaderType: LoaderType): ImagesAssetPath {
26+
switch (loaderType) {
27+
case LoaderType.ExpandableRow:
28+
return ImagesAssetPath.LoaderExpandableRow;
29+
case LoaderType.Page:
30+
return ImagesAssetPath.LoaderPage;
31+
case LoaderType.Spinner:
32+
default:
33+
return ImagesAssetPath.LoaderSpinner;
34+
}
35+
}
36+
}

0 commit comments

Comments
 (0)