Skip to content

Commit 8123278

Browse files
committed
ENGAGE-243
1 parent 8d2d6f0 commit 8123278

24 files changed

Lines changed: 633 additions & 97 deletions

projects/orcid-registry-ui/src/lib/components/import-works-dialog/import-works-dialog.component.html

Lines changed: 102 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -13,47 +13,69 @@
1313
</a>
1414
</div>
1515

16-
<section class="import-works-dialog__section" *ngIf="certifiedLinks.length">
16+
<section class="import-works-dialog__section" *ngIf="loading || certifiedLinks.length">
1717
<h3 class="import-works-dialog__section-heading">
18-
ORCID Certified Services
18+
{{ certifiedSectionHeading }}
1919
</h3>
2020
<div class="import-works-dialog__card-list">
21-
<div
22-
*ngFor="let link of certifiedLinks"
23-
class="import-works-dialog__card import-works-dialog__card--certified"
24-
>
25-
<div class="import-works-dialog__card-icon" *ngIf="link.imageUrl || link.icon">
26-
<img *ngIf="link.imageUrl" [src]="link.imageUrl" [alt]="link.name + ' logo'" class="import-works-dialog__card-img" />
27-
<mat-icon *ngIf="!link.imageUrl && link.icon" [attr.aria-hidden]="true">{{ link.icon }}</mat-icon>
28-
</div>
29-
<div class="import-works-dialog__card-content">
30-
<p class="import-works-dialog__card-title">{{ link.name }}</p>
31-
<p class="import-works-dialog__card-description">
32-
{{ link.description }}
33-
</p>
21+
<!-- Shimmer skeleton cards while loading -->
22+
<ng-container *ngIf="loading">
23+
<div
24+
*ngFor="let _ of certifiedSkeletonCount"
25+
class="import-works-dialog__card import-works-dialog__card--certified import-works-dialog__card--skeleton"
26+
aria-hidden="true"
27+
>
28+
<div class="import-works-dialog__card-icon">
29+
<orcid-skeleton-placeholder [accentBackground]="false" shape="square" width="48px" height="48px"></orcid-skeleton-placeholder>
30+
</div>
31+
<div class="import-works-dialog__card-content">
32+
<orcid-skeleton-placeholder [accentBackground]="false" class="import-works-dialog__skeleton-title" height="27px" width="60%"></orcid-skeleton-placeholder>
33+
<orcid-skeleton-placeholder [accentBackground]="false" class="import-works-dialog__skeleton-line" height="21px" width="85%"></orcid-skeleton-placeholder>
34+
</div>
35+
<div class="import-works-dialog__card-actions">
36+
<orcid-skeleton-placeholder [accentBackground]="false" class="import-works-dialog__skeleton-btn" height="36px" width="120px"></orcid-skeleton-placeholder>
37+
</div>
3438
</div>
35-
<div class="import-works-dialog__card-actions">
36-
<ng-container *ngIf="link.connected; else connectCertified">
37-
<span class="import-works-dialog__connected">
38-
<span class="import-works-dialog__connected-dot" aria-hidden="true"></span>
39-
Connected
40-
</span>
41-
</ng-container>
42-
<ng-template #connectCertified>
43-
<button
44-
mat-flat-button
45-
class="import-works-dialog__btn-connect"
46-
(click)="openInNewTab(link.url)"
47-
>
48-
Connect now
49-
</button>
50-
</ng-template>
39+
</ng-container>
40+
<!-- Real certified links -->
41+
<ng-container *ngIf="!loading">
42+
<div
43+
*ngFor="let link of certifiedLinks"
44+
class="import-works-dialog__card import-works-dialog__card--certified"
45+
>
46+
<div class="import-works-dialog__card-icon" *ngIf="link.imageUrl || link.icon">
47+
<img *ngIf="link.imageUrl" [src]="link.imageUrl" [alt]="link.name + ' logo'" class="import-works-dialog__card-img" />
48+
<mat-icon *ngIf="!link.imageUrl && link.icon" [attr.aria-hidden]="true">{{ link.icon }}</mat-icon>
49+
</div>
50+
<div class="import-works-dialog__card-content">
51+
<p class="import-works-dialog__card-title">{{ link.name }}</p>
52+
<p class="import-works-dialog__card-description">
53+
{{ link.description }}
54+
</p>
55+
</div>
56+
<div class="import-works-dialog__card-actions">
57+
<ng-container *ngIf="link.connected; else connectCertified">
58+
<span class="import-works-dialog__connected">
59+
<span class="import-works-dialog__connected-dot" aria-hidden="true"></span>
60+
{{ connectedLabel }}
61+
</span>
62+
</ng-container>
63+
<ng-template #connectCertified>
64+
<button
65+
mat-flat-button
66+
class="import-works-dialog__btn-connect"
67+
(click)="openInNewTab(link.url)"
68+
>
69+
{{ connectNowLabel }}
70+
</button>
71+
</ng-template>
72+
</div>
5173
</div>
52-
</div>
74+
</ng-container>
5375
</div>
5476
</section>
5577

56-
<section class="import-works-dialog__section import-works-dialog__section--more" *ngIf="moreServicesLinks.length">
78+
<section class="import-works-dialog__section import-works-dialog__section--more" *ngIf="loading || moreServicesLinks.length">
5779
<div class="import-works-dialog__more-panel">
5880
<button
5981
type="button"
@@ -67,7 +89,8 @@ <h3 class="import-works-dialog__section-heading">
6789
{{ moreServicesExpanded ? 'expand_more' : 'expand_less' }}
6890
</mat-icon>
6991
<span class="import-works-dialog__more-heading-text">
70-
More Services <span class="import-works-dialog__more-count">({{ moreServicesLinks.length }})</span>
92+
{{ moreServicesHeading }}
93+
<span class="import-works-dialog__more-count" *ngIf="!loading">({{ moreServicesLinks.length }})</span>
7194
</span>
7295
</button>
7396
<div
@@ -77,30 +100,53 @@ <h3 class="import-works-dialog__section-heading">
77100
[attr.aria-labelledby]="'import-works-more-heading'"
78101
[hidden]="!moreServicesExpanded"
79102
>
80-
<div
81-
*ngFor="let link of moreServicesLinks"
82-
class="import-works-dialog__card import-works-dialog__card--more"
83-
>
84-
<div class="import-works-dialog__card-icon" *ngIf="link.imageUrl || link.icon">
85-
<img *ngIf="link.imageUrl" [src]="link.imageUrl" [alt]="link.name + ' logo'" class="import-works-dialog__card-img" />
86-
<mat-icon *ngIf="!link.imageUrl && link.icon" [attr.aria-hidden]="true">{{ link.icon }}</mat-icon>
103+
<!-- Shimmer skeleton cards while loading -->
104+
<ng-container *ngIf="loading">
105+
<div
106+
*ngFor="let _ of moreServicesSkeletonCount"
107+
class="import-works-dialog__card import-works-dialog__card--more import-works-dialog__card--skeleton"
108+
aria-hidden="true"
109+
>
110+
<div class="import-works-dialog__card-icon">
111+
<orcid-skeleton-placeholder [accentBackground]="false" shape="square" width="48px" height="48px"></orcid-skeleton-placeholder>
112+
</div>
113+
<div class="import-works-dialog__card-content">
114+
<orcid-skeleton-placeholder [accentBackground]="false" class="import-works-dialog__skeleton-title" height="27px" width="55%"></orcid-skeleton-placeholder>
115+
<orcid-skeleton-placeholder [accentBackground]="false" class="import-works-dialog__skeleton-line" height="21px" width="100%"></orcid-skeleton-placeholder>
116+
<orcid-skeleton-placeholder [accentBackground]="false" class="import-works-dialog__skeleton-line" height="21px" width="80%"></orcid-skeleton-placeholder>
117+
</div>
118+
<div class="import-works-dialog__card-actions">
119+
<orcid-skeleton-placeholder [accentBackground]="false" class="import-works-dialog__skeleton-btn" height="36px" width="120px"></orcid-skeleton-placeholder>
120+
</div>
87121
</div>
88-
<div class="import-works-dialog__card-content">
89-
<p class="import-works-dialog__card-title">{{ link.name }}</p>
90-
<p class="import-works-dialog__card-description">
91-
{{ link.description }}
92-
</p>
93-
</div>
94-
<div class="import-works-dialog__card-actions">
95-
<button
96-
mat-stroked-button
97-
class="import-works-dialog__btn-connect-more"
98-
(click)="openInNewTab(link.url)"
99-
>
100-
Connect now
101-
</button>
122+
</ng-container>
123+
<!-- Real more services links -->
124+
<ng-container *ngIf="!loading">
125+
<div
126+
*ngFor="let link of moreServicesLinks"
127+
class="import-works-dialog__card import-works-dialog__card--more"
128+
>
129+
<div class="import-works-dialog__card-icon" *ngIf="link.imageUrl || link.icon">
130+
<img *ngIf="link.imageUrl" [src]="link.imageUrl" [alt]="link.name + ' logo'" class="import-works-dialog__card-img" />
131+
<mat-icon *ngIf="!link.imageUrl && link.icon" [attr.aria-hidden]="true">{{ link.icon }}</mat-icon>
132+
</div>
133+
<div class="import-works-dialog__card-content">
134+
<p class="import-works-dialog__card-title">{{ link.name }}</p>
135+
<p class="import-works-dialog__card-description">
136+
{{ link.description }}
137+
</p>
138+
</div>
139+
<div class="import-works-dialog__card-actions">
140+
<button
141+
mat-stroked-button
142+
class="import-works-dialog__btn-connect-more"
143+
(click)="openInNewTab(link.url)"
144+
>
145+
{{ connectNowLabel }}
146+
</button>
147+
</div>
102148
</div>
103-
</div>
149+
</ng-container>
104150
</div>
105151
</div>
106152
</section>

projects/orcid-registry-ui/src/lib/components/import-works-dialog/import-works-dialog.component.scss

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,30 @@
144144
flex-shrink: 0;
145145
}
146146

147+
// Skeleton (shimmer) card placeholders
148+
.import-works-dialog__card--skeleton {
149+
pointer-events: none;
150+
151+
.import-works-dialog__skeleton-title {
152+
margin-bottom: 2px;
153+
display: block;
154+
}
155+
156+
.import-works-dialog__skeleton-line {
157+
margin-top: 4px;
158+
display: block;
159+
160+
&:first-of-type {
161+
margin-top: 0;
162+
}
163+
}
164+
165+
.import-works-dialog__skeleton-btn {
166+
border-radius: 4px;
167+
display: block;
168+
}
169+
}
170+
147171

148172
.import-works-dialog__btn-connect {
149173
font-size: var(--orcid-font-size-body-small, 14px) !important;

projects/orcid-registry-ui/src/lib/components/import-works-dialog/import-works-dialog.component.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'
66
import {
77
OrcidModalComponent,
88
ORCID_MODAL_DIALOG_PANEL_CLASS,
9+
SkeletonPlaceholderComponent,
910
} from '@orcid/ui'
1011
import type {
1112
ImportWorksDialogData,
@@ -20,6 +21,13 @@ export type {
2021
ImportWorksMoreLink,
2122
} from './import-works-dialog.types'
2223

24+
/** Number of skeleton cards to show in the certified section while loading. */
25+
/** Base on production data, we have 2 certified links */
26+
const CERTIFIED_SKELETON_COUNT = 2
27+
/** Number of skeleton cards to show in the more services section while loading. */
28+
/** Base on production data, we have 15 more services links */
29+
const MORE_SERVICES_SKELETON_COUNT = 15
30+
2331
@Component({
2432
selector: 'orcid-registry-import-works-dialog',
2533
standalone: true,
@@ -29,6 +37,7 @@ export type {
2937
MatButtonModule,
3038
MatIconModule,
3139
OrcidModalComponent,
40+
SkeletonPlaceholderComponent,
3241
],
3342
templateUrl: './import-works-dialog.component.html',
3443
styleUrls: ['./import-works-dialog.component.scss'],
@@ -37,6 +46,11 @@ export class ImportWorksDialogComponent {
3746
/** Whether the "More Services" section is expanded. Default true. */
3847
moreServicesExpanded = true
3948

49+
/** Array used to render N skeleton cards in the certified section. */
50+
certifiedSkeletonCount = Array(CERTIFIED_SKELETON_COUNT)
51+
/** Array used to render N skeleton cards in the more services section. */
52+
moreServicesSkeletonCount = Array(MORE_SERVICES_SKELETON_COUNT)
53+
4054
constructor(
4155
private _dialogRef: MatDialogRef<ImportWorksDialogComponent>,
4256
@Inject(MAT_DIALOG_DATA) public data: ImportWorksDialogData
@@ -58,6 +72,26 @@ export class ImportWorksDialogComponent {
5872
return this.data?.supportLink
5973
}
6074

75+
get certifiedSectionHeading(): string {
76+
return this.data?.certifiedSectionHeading ?? 'ORCID Certified Services'
77+
}
78+
79+
get moreServicesHeading(): string {
80+
return this.data?.moreServicesHeading ?? 'More Services'
81+
}
82+
83+
get connectNowLabel(): string {
84+
return this.data?.connectNowLabel ?? 'Connect now'
85+
}
86+
87+
get connectedLabel(): string {
88+
return this.data?.connectedLabel ?? 'Connected'
89+
}
90+
91+
get loading(): boolean {
92+
return this.data?.loading === true
93+
}
94+
6195
get certifiedLinks(): ImportWorksCertifiedLink[] {
6296
return this.data?.certifiedLinks ?? []
6397
}

projects/orcid-registry-ui/src/lib/components/import-works-dialog/import-works-dialog.types.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,16 +28,27 @@ export interface ImportWorksMoreLink {
2828

2929
/**
3030
* Data passed into the Import your works dialog via MAT_DIALOG_DATA.
31+
* All user-facing strings (title, introText, labels) should be passed in so the host app can supply translatable text.
3132
*/
3233
export interface ImportWorksDialogData {
34+
/** When true, shows shimmer skeleton placeholders for the link sections instead of content. */
35+
loading?: boolean
3336
/** Header/title of the dialog. */
3437
title: string
3538
/** Optional intro paragraph above the sections. */
3639
introText?: string
37-
/** Optional support link shown below intro (e.g. "Find out more about importing works..."). */
40+
/** Optional support link shown below intro. */
3841
supportLink?: { url: string; label: string }
39-
/** Links shown under "ORCID Certified Services". */
42+
/** Section heading for certified services (e.g. "ORCID Certified Services"). */
43+
certifiedSectionHeading?: string
44+
/** Section heading for non-certified services (e.g. "More Services"). */
45+
moreServicesHeading?: string
46+
/** Button label for connecting to a service (e.g. "Connect now"). */
47+
connectNowLabel?: string
48+
/** Label shown when a certified service is already connected (e.g. "Connected"). */
49+
connectedLabel?: string
50+
/** Links shown under the certified section heading. */
4051
certifiedLinks: ImportWorksCertifiedLink[]
41-
/** Ordered list of links shown under "More Services". */
52+
/** Ordered list of links shown under the more services heading. */
4253
moreServicesLinks: ImportWorksMoreLink[]
4354
}

projects/orcid-ui-docs/src/app/pages/orcid-registry-ui/import-works-dialog-page.component.html

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ <h4>More services links</h4>
6868
<button mat-flat-button type="button" (click)="openDialog()">
6969
Open Import your works dialog
7070
</button>
71+
<button mat-stroked-button type="button" (click)="openDialogWithLoading()">
72+
Open with loading (shimmer)
73+
</button>
7174
</div>
7275
</div>
7376

@@ -81,6 +84,23 @@ <h4>More services links</h4>
8184

8285
// ...
8386

87+
// Open with loading state (shimmer), then update when data is ready:
88+
const dialogRef = this.dialog.open(ImportWorksDialogComponent, &#123;
89+
panelClass: ORCID_MODAL_DIALOG_PANEL_CLASS,
90+
data: &#123;
91+
loading: true,
92+
title: 'Import your works',
93+
certifiedLinks: [],
94+
moreServicesLinks: [],
95+
&#125; as ImportWorksDialogData,
96+
width: '850px',
97+
maxHeight: '90vh',
98+
&#125;)
99+
this.loadDialogData().subscribe((data) => &#123;
100+
dialogRef.componentInstance.data = &#123; ...data, loading: false &#125;
101+
&#125;)
102+
103+
// Or open with data already loaded:
84104
this.dialog.open(ImportWorksDialogComponent, &#123;
85105
panelClass: ORCID_MODAL_DIALOG_PANEL_CLASS,
86106
data: &#123;
@@ -90,6 +110,8 @@ <h4>More services links</h4>
90110
certifiedLinks: [&#123; name, description, url, connected &#125;],
91111
moreServicesLinks: [&#123; name, description, url &#125;],
92112
&#125; as ImportWorksDialogData,
113+
width: '850px',
114+
maxHeight: '90vh',
93115
&#125;)</code></pre>
94116
</div>
95117

@@ -99,6 +121,7 @@ <h4>More services links</h4>
99121
<code>ImportWorksDialogData</code>.
100122
</p>
101123
<ul>
124+
<li><code>loading</code>: Optional. When <code>true</code>, the dialog shows shimmer skeleton placeholders for both sections (certified and more services) instead of link content. Use when fetching data asynchronously; set to <code>false</code> when data is ready (e.g. <code>dialogRef.componentInstance.data = &#123; ...data, loading: false &#125;</code>). Uses <code>orcid-skeleton-placeholder</code> from <code>@orcid/ui</code>.</li>
102125
<li><code>title</code>: Header text.</li>
103126
<li><code>introText</code>: Optional intro paragraph.</li>
104127
<li><code>supportLink</code>: Optional &#123; url, label &#125; link below intro.</li>

0 commit comments

Comments
 (0)