Skip to content

Conversation

alok-traceable
Copy link
Contributor

@alok-traceable alok-traceable commented Aug 17, 2021

Description

New gif loader.

Checklist:

  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • Any dependent changes have been merged and published in downstream modules

Usages

As directive

*htLoadAsync="applicationInsights.apiDistributionByRiskSeries$ as apiDistributionByRiskSeries; loaderType: '${LoaderTypes.LargeSquare}'"

As component

<ht-loader [type]="${LoaderTypes.LargeSquare}"></ht-loader>

@alok-traceable alok-traceable requested a review from a team as a code owner August 17, 2021 06:35
@codecov
Copy link

codecov bot commented Aug 17, 2021

Codecov Report

Merging #1071 (76d5ffd) into main (594a8db) will increase coverage by 0.00%.
The diff coverage is 91.30%.

Impacted file tree graph

@@           Coverage Diff           @@
##             main    #1071   +/-   ##
=======================================
  Coverage   85.08%   85.09%           
=======================================
  Files         825      825           
  Lines       17066    17086   +20     
  Branches     2216     2220    +4     
=======================================
+ Hits        14521    14539   +18     
- Misses       2514     2516    +2     
  Partials       31       31           
Impacted Files Coverage Δ
...ts/components/src/not-found/not-found.component.ts 100.00% <ø> (ø)
...mponents/src/load-async/loader/loader.component.ts 88.23% <88.23%> (-11.77%) ⬇️
projects/assets-library/src/public-api.ts 100.00% <100.00%> (ø)
.../components/src/load-async/load-async.directive.ts 100.00% <100.00%> (ø)
...load-async/wrapper/load-async-wrapper.component.ts 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 594a8db...76d5ffd. Read the comment docs.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@anandtiwary
Copy link
Contributor

Discussed this in our call today. Overall a good change.

Few Action items:

  • Rename size to type
  • Rename Large, Medium, Small to better names like Page, widget etc. We can look into how UX team is planning to use them and then decide the appropriate name
  • Add a similar type property to htLoadAsync directive and propagate it to the loader wrapper and the loader component. Set an appropriate default value that would take care of majority of cases.
  • Then go and set specific value to any loader which needs a different loader than the default one.

@github-actions

This comment has been minimized.

@alok-traceable
Copy link
Contributor Author

alok-traceable commented Aug 19, 2021

Discussed this in our call today. Overall a good change.

Few Action items:

  • Rename size to type
  • Rename Large, Medium, Small to better names like Page, widget etc. We can look into how UX team is planning to use them and then decide the appropriate name
  • Add a similar type property to htLoadAsync directive and propagate it to the loader wrapper and the loader component. Set an appropriate default value that would take care of majority of cases.
  • Then go and set specific value to any loader which needs a different loader than the default one.

updated, please check the description I have added usage section

@github-actions

This comment has been minimized.

// Second param for structural directive is undefined until this.data$ is defined
// So putting this assignment in constuctor will not work
// This will execute only once as this method is called only
this.wrapperInjector = Injector.create({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to create a new injector. It would be problematic and also it wouldn't work since right now we are only creating a new instance of component when data$ reference itself change. New emits on data$ is automatically passed via the state$ property.

What I would recommend it to modify line 45 instead and add loaderType$ as a property in LoadAsyncWrapperParameters.

Create a ReplaySubject for loaderType ->

private readonly loaderTypeSubject: BehaviourSubject<LoaderType> = new BehaviourSubject(<default-value>);
private readonly loaderType$: Observable<LoaderType>= this.loaderTypeSubject.asObservable();

Then use add loaderType$ on like 46.

and add a separate ngOnChanges trigger for loaderType, that would just emit on this subject.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

LoaderHorizontal = 'assets/images/loader-horizontal.gif'
}

export const enum LoaderTypes {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoaderType

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

export const enum LoaderTypes {
SmallCircle = 'small-circle',
Horizontal = 'horizontal',
LargeSquare = 'large-square'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Spoke to Michael.

Let's use smallCircle directly in Spinner. We can remove it from here

Horizontal can be renamed to ExpandableRow. LargeSquare can be renamed to 'Page'.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, we would need to bring back the old svg icon approach for other skeleton loaders.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, will update the names,

Do you mean to use the small-circle as default? Not sure what is meant by the Spinner.

One thing I want to highlight here there is no need to use both ht-icon and image tag. An image tag can also work with an SVG source path.

But if we put it back then we need to put more logic on what type of loader goes where and then computing the image path and icon path(in case there are multiple) will go in 2 totally separate directions. I would prefer to keep one kind of logic (either SVG or IMG tag) as it would be straightforward and won't create any confusion.


public ngOnChanges(): void {
// Unfortunatly passing undefined is overwriting default value
this.type = this.type ?? LoaderTypes.Horizontal;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't be modifying the input property. From where, is it getting the undefined value?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@github-actions

This comment has been minimized.

@alok-traceable
Copy link
Contributor Author

I will continue on writing test cases, is there anything else pending on this PR?
ping: @anandtiwary

@@ -23,8 +24,11 @@ import {
export class LoadAsyncDirective implements OnChanges, OnDestroy {
@Input('htLoadAsync')
public data$?: Observable<unknown>;
@Input()
public htLoadAsyncLoaderType!: LoaderType;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be optional. You can assign a default value here

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

private readonly wrapperParamsSubject: ReplaySubject<LoadAsyncWrapperParameters> = new ReplaySubject(1);
private readonly wrapperInjector: Injector;
private wrapperInjector!: Injector;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason for removing the readonly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

export class LoaderComponent implements OnChanges {
@Input()
public type: LoaderType = LoaderType.ExpandableRow;
public imagePath: ImagesAssetPath = ImagesAssetPath.LoaderExpandableRow;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is formatting right? Can add a new line after L17

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

export class LoaderComponent {}
export class LoaderComponent implements OnChanges {
@Input()
public type: LoaderType = LoaderType.ExpandableRow;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be the default?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

@@ -39,9 +40,13 @@ export class LoadAsyncWrapperComponent {

public content?: TemplateRef<LoadAsyncContext>;

private readonly loaderTypeSubject: BehaviorSubject<LoaderType> = new BehaviorSubject<LoaderType>(LoaderType.Spinner);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are setting different default options at multiple places. It would be useful if we set it at only one place

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

then, I need to put undefined as the initial value.

@github-actions

This comment has been minimized.

private readonly wrapperParamsSubject: ReplaySubject<LoadAsyncWrapperParameters> = new ReplaySubject(1);
private readonly wrapperInjector: Injector;
private readonly wrapperInjector!: Injector;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this required?

@Component({
selector: 'ht-load-async-wrapper',
template: `
<div *ngIf="this.state$ | async as state" class="fill-container" [ngSwitch]="state.type">
<ng-container *ngSwitchCase="'${LoadAsyncStateType.Loading}'">
<ht-loader></ht-loader>
<ng-container *ngIf="this.loaderType$ | async as loaderType">
<ht-loader [type]="loaderType"></ht-loader>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ng-container is not needed. You can use async pipe directly with ht-loader

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can do <ht-loader [type]="loaderType$ | async"></ht-loader>

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

loaderType here will be defined since we are setting default value.

public constructor(@Inject(ASYNC_WRAPPER_PARAMETERS$) parameters$: Observable<LoadAsyncWrapperParameters>) {
this.state$ = parameters$.pipe(
tap(params => (this.content = params.content)),
tap(params => this.loaderTypeSubject.next(params.loaderType)),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we gain anything by storing it as observable? If we store the loaderType similar to content, would it cause any issues?

export class LoaderComponent {}
export class LoaderComponent implements OnChanges {
@Input()
public type!: LoaderType;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property can be optional right?

case LoaderType.Spinner:
return ImagesAssetPath.LoaderSpinner;
default:
return assertUnreachable(this.type);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

may be we can leave type as optional and in default section here, we can assign the default image path instead of throwing exception

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, No. When a consumer is passing a wrong value it should throw an error, on the other hand, if the consumer is not passing a value it should show default value.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

import { createHostFactory, SpectatorHost } from '@ngneat/spectator/jest';
import { LoaderComponent } from './loader.component';

describe('Link component', () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

name

@@ -23,8 +24,11 @@ import {
export class LoadAsyncDirective implements OnChanges, OnDestroy {
@Input('htLoadAsync')
public data$?: Observable<unknown>;
@Input()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Input('htLoadAsyncLoaderType')
public loaderType: LoaderType;

This should work

@@ -49,6 +53,7 @@ export class LoadAsyncDirective implements OnChanges, OnDestroy {
this.wrapperView = this.wrapperView || this.buildWrapperView();
this.wrapperParamsSubject.next({
state$: this.loadAsyncService.mapObservableState(this.data$),
loaderType: this.htLoadAsyncLoaderType,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So basically, if htLoadAsyncLoaderType changes, then this if block will not be triggered? So, how would you handle the case when loaderType changes after init?

Why don't you send loaderType to mapObservableState as another param? then this function can add this loaderType in interface LoadingAsyncState { type: LoadAsyncStateType.Loading; }
Also, update L52 to also include change on LoaderType

@Input()
public type: LoaderType = LoaderType.Spinner;

public imagePath: ImagesAssetPath = ImagesAssetPath.LoaderSpinner;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we keep it unassigned, would it get the same value from getImagePathFromType call inside ngOnChanges?

public constructor(@Inject(ASYNC_WRAPPER_PARAMETERS$) parameters$: Observable<LoadAsyncWrapperParameters>) {
this.state$ = parameters$.pipe(
tap(params => (this.content = params.content)),
tap(params => this.loaderTypeSubject.next(params.loaderType)),
switchMap(parameter => parameter.state$),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As per prev comments, we can add loadertype as part of LoadingState. We wouldn't need to store another subject here. We can reference it from the state object directly.

@github-actions
Copy link

Unit Test Results

    4 files  ±0  268 suites  +1   17m 31s ⏱️ +57s
966 tests +4  966 ✔️ +4  0 💤 ±0  0 ❌ ±0 
973 runs  +4  973 ✔️ +4  0 💤 ±0  0 ❌ ±0 

Results for commit 76d5ffd. ± Comparison against base commit 594a8db.

@anandtiwary
Copy link
Contributor

anandtiwary commented Sep 22, 2021

@alok-traceable I am guessing this is not ready for another round of review, right?

@anandtiwary
Copy link
Contributor

@itssharmasandeep Can we close this one?

@itssharmasandeep
Copy link
Contributor

Closing this, as #1217 contains the same changes from this one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants