Skip to content

Commit 045a515

Browse files
authored
PD-5147 add space and update docs (#2811)
Co-authored-by: andrej romanov <50377758+auumgn@users.noreply.github.com>
1 parent e6b18bf commit 045a515

3 files changed

Lines changed: 72 additions & 35 deletions

File tree

projects/orcid-registry-ui/src/lib/components/auth-challenge/auth-challenge.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ <h1 class="orc-font-heading-small text-center font-normal" i18n>
258258
</a>
259259
</div>
260260
}
261-
<div class="text-center orc-font-body-small mt-4">
261+
<div class="text-center orc-font-body-small mt-4!">
262262
<p i18n="@@ngOrcid.signin.2fa.noDeviceOrRecovery" class="m-0 leading-6">
263263
Don't have your device or recovery code?
264264
</p>

projects/orcid-ui-docs/src/app/pages/orcid-registry-ui/auth-challenge-page.component.html

Lines changed: 63 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
<app-documentation-page
22
title="Auth Challenge Component"
3-
description="The AuthChallengeComponent is a Dialog that validates user credentials (password) and manages 2FA/recovery code inputs before allowing sensitive actions."
3+
description="The AuthChallengeComponent validates user credentials (password) and manages 2FA/recovery code inputs before allowing sensitive actions. It can be used as a Dialog, dynamically attached via CdkPortalOutlet, or embedded directly in HTML."
44
>
55
<div controls>
6-
<p>Customize the dialog to preview different configurations:</p>
6+
<p>Customize the component to preview different configurations:</p>
77

88
<div style="display: grid; gap: 16px; margin-bottom: 24px">
99
<mat-checkbox [(ngModel)]="showPasswordField">
@@ -24,6 +24,11 @@
2424
<input matInput [(ngModel)]="boldText" />
2525
</mat-form-field>
2626

27+
<mat-form-field appearance="outline">
28+
<mat-label>Trailing text</mat-label>
29+
<input matInput [(ngModel)]="trailingText" />
30+
</mat-form-field>
31+
2732
<div>
2833
<button
2934
mat-flat-button
@@ -52,19 +57,26 @@
5257

5358
<div usage style="font-size: 14px">
5459
<p>
55-
This component is a <strong>MatDialog</strong>. It manages the UI
56-
switching and validation logic between the password field, 2FA code, and
57-
recovery code inputs. You must pass a parent <code>[formGroup]</code> into
58-
it via the dialog data so it can attach its controls.
60+
This component manages the UI switching and validation logic between the
61+
password field, 2FA code, and recovery code inputs. You must pass a parent
62+
<code>[formGroup]</code> into it so it can attach its controls.
5963
</p>
6064

61-
<p>To implement this component, use the code highlighted in yellow.</p>
65+
<p>
66+
It can be rendered as a <strong>MatDialog</strong>, injected dynamically
67+
via <strong>CdkPortalOutlet</strong>, or used as a standard HTML tag.
68+
<em
69+
>Note: When using MatDialog, pass configuration properties inside the
70+
<code>data</code> object. Otherwise, bind them as standard
71+
<code>&#64;Input()</code> properties.</em
72+
>
73+
</p>
6274

6375
<h4>1. Form Configuration</h4>
6476
<p>
6577
Initialize the controls in your parent component. Note that
6678
<code>Validators.required</code> for the 2FA fields is managed
67-
automatically by the dialog component based on the fields being shown.
79+
automatically by the component based on the fields currently being shown.
6880
</p>
6981
<pre><code class="language-typescript">this.form = this.fb.group(&#123;
7082
// ... other controls like 'id'
@@ -73,12 +85,7 @@ <h4>1. Form Configuration</h4>
7385
<span style="background-color: #fff59d; color: #000;">twoFactorRecoveryCode: [null, [Validators.minLength(10), Validators.maxLength(10)]],</span>
7486
&#125;);</code></pre>
7587

76-
<h4>2. Opening and Handling the Dialog</h4>
77-
<p>
78-
Use the following method to open the dialog, listen for the verify button
79-
click, trigger your backend request, and handle the final success/cancel
80-
state. You can copy and paste this directly into your component.
81-
</p>
88+
<h4>2A. Usage via MatDialog</h4>
8289
<pre><code class="language-typescript">openAuthChallenge() &#123;
8390
// 1. Open the dialog
8491
const dialogRef = this._matDialog.open&lt;AuthChallengeComponent&gt;(
@@ -88,6 +95,7 @@ <h4>2. Opening and Handling the Dialog</h4>
8895
parentForm: this.form,
8996
actionDescription: 'unlink the alternate sign in account',
9097
boldText: 'Google',
98+
trailingText: 'to continue.',
9199
showTwoFactorField: this.twoFactorState, // true or false
92100
&#125; as AuthChallengeFormData,
93101
&#125;
@@ -102,37 +110,53 @@ <h4>2. Opening and Handling the Dialog</h4>
102110
.subscribe(&#123;
103111
next: (response: any) => &#123;
104112
if (response.success) &#123;
105-
// Close the dialog and pass true for success
106113
dialogRef.close(true);
107114
&#125; else &#123;
108-
// Pass backend response back to the dialog to display errors
109-
dialogRef.componentInstance.loading = false;
110115
dialogRef.componentInstance.processBackendResponse(response);
111116
&#125;
112117
&#125;,
113118
&#125;);
114119

115-
// 3. Listen for when the dialog actually closes
120+
// 3. Listen for cancel or close
116121
dialogRef.afterClosed().subscribe((success) => &#123;
117122
this.form.reset();
118-
119-
if (success) &#123;
120-
// Action completed successfully
121-
this.success = true;
122-
&#125; else &#123;
123-
// User canceled or closed the dialog
124-
this.cancel = true;
125-
&#125;
126123
&#125;);
127124
&#125;</code></pre>
128125

129-
<h4>3. Interface Definitions</h4>
126+
<h4>2B. Usage via CdkPortalOutlet</h4>
130127
<p>
131-
The endpoint payload/response object needs to extend the
132-
<strong><code>AuthChallenge</code></strong> interface. The dialog
133-
configuration uses the
134-
<strong><code>AuthChallengeFormData</code></strong> interface.
128+
First, add the outlet container to your HTML and query it in your TS file.
135129
</p>
130+
<pre><code class="language-html">&lt;!-- In your parent HTML --&gt;
131+
&lt;ng-container #authChallengeOutlet cdkPortalOutlet&gt;&lt;/ng-container&gt;</code></pre>
132+
133+
<pre><code class="language-typescript">// In your parent TS file
134+
&#64;ViewChild('authChallengeOutlet', &#123; static: false, read: CdkPortalOutlet &#125;) outlet!: CdkPortalOutlet;
135+
136+
showAuthenticationChallenge(): void &#123;
137+
const portal = new ComponentPortal&lt;AuthChallengeComponent&gt;(AuthChallengeComponent);
138+
const componentRef = this.outlet.attachComponentPortal(portal);
139+
140+
// Bind inputs directly to the instance
141+
componentRef.instance.parentForm = this.form;
142+
componentRef.instance.actionDescription = 'complete your password reset';
143+
componentRef.instance.showPasswordField = false;
144+
145+
componentRef.instance.submitAttempt.subscribe(() => &#123;
146+
// Handle submission and call componentRef.instance.processBackendResponse(res) on error.
147+
// On success, navigate away.
148+
this.router.navigate(['/success']);
149+
&#125;);
150+
151+
componentRef.instance.cancelAttempt.subscribe(() => &#123;
152+
this.form.reset();
153+
// TIP: If staying on the same page, use this.outlet.detach() to close the view.
154+
// If routing away, DO NOT detach it manually to avoid a UI flash. Angular will clean it up!
155+
this.router.navigate(['/signin']);
156+
&#125;);
157+
&#125;</code></pre>
158+
159+
<h4>3. Interface Definitions</h4>
136160
<pre><code class="language-typescript">export interface AuthChallenge &#123;
137161
success?: boolean;
138162
invalidPassword?: boolean;
@@ -146,14 +170,14 @@ <h4>3. Interface Definitions</h4>
146170

147171
export interface AuthChallengeFormData &#123;
148172
actionDescription?: string;
173+
boldText?: string;
174+
trailingText?: string;
149175
showPasswordField?: boolean;
150176
showTwoFactorField?: boolean;
151177
codeControlName?: string;
152178
recoveryControlName?: string;
153179
passwordControlName?: string;
154180
parentForm?: UntypedFormGroup;
155-
boldText?: string;
156-
trailingText?: string;
157181
&#125;</code></pre>
158182

159183
<h4>4. Backend Implementation</h4>
@@ -193,9 +217,14 @@ <h4>4. Backend Implementation</h4>
193217
(e.g. <code>'unlink the alternate sign in account'</code>).
194218
</li>
195219
<li>
196-
<code style="font-weight: bold">memberName</code>: <code>string</code>.
220+
<code style="font-weight: bold">boldText</code>: <code>string</code>.
197221
The target of the action to be bolded (e.g. <code>'Google'</code>).
198222
</li>
223+
<li>
224+
<code style="font-weight: bold">trailingText</code>:
225+
<code>string</code>. Optional text that appears after the bolded text
226+
(e.g. <code>'to complete your password reset'</code>).
227+
</li>
199228
<li>
200229
<code style="font-weight: bold">showPasswordField</code>:
201230
<code>boolean</code>. Whether to display the password input field.

projects/orcid-ui-docs/src/app/pages/orcid-registry-ui/auth-challenge-page.component.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export class AuthChallengePageComponent implements OnInit {
4242
showTwoFactorField = true
4343
actionDescription = 'perform this action on'
4444
boldText = 'Example Account'
45+
trailingText = 'to continue.'
4546
form: UntypedFormGroup
4647

4748
constructor(private _fb: UntypedFormBuilder, private _dialog: MatDialog) {}
@@ -63,6 +64,7 @@ export class AuthChallengePageComponent implements OnInit {
6364
parentForm: this.form,
6465
actionDescription: this.actionDescription,
6566
boldText: this.boldText,
67+
trailingText: this.trailingText,
6668
showPasswordField: this.showPasswordField,
6769
showTwoFactorField: this.showTwoFactorField,
6870
},
@@ -83,6 +85,12 @@ export class AuthChallengePageComponent implements OnInit {
8385
}, 1000)
8486
})
8587

88+
dialogRef.componentInstance.cancelAttempt
89+
.pipe(takeUntil(dialogRef.afterClosed()))
90+
.subscribe(() => {
91+
dialogRef.close()
92+
})
93+
8694
dialogRef.afterClosed().subscribe(() => {
8795
this.form.reset()
8896
})

0 commit comments

Comments
 (0)