-
Notifications
You must be signed in to change notification settings - Fork 11
feat: Skeleton loader for LoadAsync directive #1373
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
602cd22
3a3d19b
cba76a7
e81d1df
e879a4e
24015a1
3ef1fe2
5fd455a
7f1cbcc
91a4832
bf2b846
a6f5b7e
1048339
3d048dd
39ed6a3
dbdd5cf
6d90a76
d416769
91c7214
17b08ee
86212a4
8f19a6d
2e2048d
48418e0
c99cebe
429fc03
12f0047
6a07033
65a867e
0b7c6b4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,25 +1,66 @@ | ||
import { ChangeDetectionStrategy, Component, Input, OnChanges } from '@angular/core'; | ||
import { ImagesAssetPath } from '@hypertrace/assets-library'; | ||
import { assertUnreachable } from '@hypertrace/common'; | ||
import { SkeletonType } from '../../skeleton/skeleton.component'; | ||
import { LoaderType } from '../load-async.service'; | ||
|
||
@Component({ | ||
selector: 'ht-loader', | ||
styleUrls: ['./loader.component.scss'], | ||
changeDetection: ChangeDetectionStrategy.OnPush, | ||
template: ` | ||
<div class="ht-loader"> | ||
<img [ngClass]="[this.currentLoaderType]" [src]="this.getImagePathFromType(this.currentLoaderType)" /> | ||
<div class="ht-loader" [ngClass]="{ 'flex-centered': this.isOldLoaderType }"> | ||
<ng-container *ngIf="!this.isOldLoaderType; else oldLoaderTemplate"> | ||
<ht-skeleton [skeletonType]="this.skeletonType"></ht-skeleton> | ||
</ng-container> | ||
|
||
<ng-template #oldLoaderTemplate> | ||
<img [ngClass]="[this.currentLoaderType]" [src]="this.imagePath" /> | ||
</ng-template> | ||
</div> | ||
` | ||
}) | ||
export class LoaderComponent implements OnChanges { | ||
@Input() | ||
public loaderType?: LoaderType; | ||
|
||
public skeletonType: SkeletonType = SkeletonType.Rectangle; | ||
|
||
public currentLoaderType: LoaderType = LoaderType.Spinner; | ||
|
||
public imagePath: ImagesAssetPath = ImagesAssetPath.LoaderSpinner; | ||
|
||
public isOldLoaderType: boolean = true; | ||
|
||
public ngOnChanges(): void { | ||
this.currentLoaderType = this.loaderType ?? LoaderType.Spinner; | ||
|
||
if (this.determineIfOldLoaderType(this.currentLoaderType)) { | ||
this.isOldLoaderType = true; | ||
this.imagePath = this.getImagePathFromType(this.currentLoaderType); | ||
} else { | ||
this.isOldLoaderType = false; | ||
this.skeletonType = this.getSkeletonTypeForLoader(this.currentLoaderType); | ||
} | ||
} | ||
|
||
public determineIfOldLoaderType(loaderType: LoaderType): boolean { | ||
switch (loaderType) { | ||
case LoaderType.Spinner: | ||
case LoaderType.ExpandableRow: | ||
case LoaderType.Page: | ||
return true; | ||
case LoaderType.Circle: | ||
case LoaderType.Text: | ||
case LoaderType.ListItem: | ||
case LoaderType.Rectangle: | ||
case LoaderType.Square: | ||
case LoaderType.TableRow: | ||
case LoaderType.Donut: | ||
return false; | ||
default: | ||
return assertUnreachable(loaderType); | ||
} | ||
} | ||
|
||
public getImagePathFromType(loaderType: LoaderType): ImagesAssetPath { | ||
|
@@ -33,4 +74,23 @@ export class LoaderComponent implements OnChanges { | |
return ImagesAssetPath.LoaderSpinner; | ||
} | ||
} | ||
|
||
public getSkeletonTypeForLoader(curLoaderType: LoaderType): SkeletonType { | ||
switch (curLoaderType) { | ||
case LoaderType.Text: | ||
return SkeletonType.Text; | ||
case LoaderType.Circle: | ||
return SkeletonType.Circle; | ||
case LoaderType.Square: | ||
return SkeletonType.Square; | ||
case LoaderType.TableRow: | ||
return SkeletonType.TableRow; | ||
case LoaderType.ListItem: | ||
return SkeletonType.ListItem; | ||
case LoaderType.Donut: | ||
return SkeletonType.Donut; | ||
default: | ||
return SkeletonType.Rectangle; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't expect to hit this, so assertUnreachable would be a good fit here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. wouldn't we hit it if the input loader type is either ExpandableRow, Page, or Spinner ? I attempted adding it but it wouldn't let me pass in
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I missed that not every case was listed here, but that's actually the point of assertUnreachable - it will give that compiler error you saw if a case isn't handled. So what I'd suggest is changing default:
return SkeletonType.Rectangle; to case LoaderType.ExpandableRow:
case LoaderType.Page:
case LoaderType.Spinner:
return SkeletonType.Rectangle;
default:
return assertUnreachable(curLoaderType); Although the behavior is currently identical, when a new loader type is added we're forcing the author to come look at this switch statement and make a decision on how to map it, rather than them potentially incorrectly being mapped to the default case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
These cases wouldn't be used anyway since we only show skeleton component when we see the new "loader type". There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah, as Anand said, we only end up using the return type from this switch if the loader type is not one of the three older types, so including them here is irrelevant, except for purpose of using the assertUnreachable. And with recent changes coming the switch statement won't even be hit if loader type is one of the three old types. So i'm going to revert it back to the original version using the default case. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's trading a compiler check for a logic check - never a good trade. If you want the best of both worlds, the types can actually be improved to represent the behavior you want - make the loader type a union of two enums, one representing the old values, one the new. This function would only accept the new ones (or you may not even need to map types, since the same skeleton type enum can be used by both) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, we could do that. But In a follow-up PR, we plan to migrate existing usages to new skeleton loaders, and then we will remove all the three older loader types and associated code. If you prefer, we can bring these cases back for correctness in the meantime.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If they're going to go away in a followup PR, the style isn't a big deal since that's just for maintainability. In that case either way is fine with me as long as the logic is right. |
||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.