Skip to content

Commit e15b309

Browse files
committed
feat(sign-up-design): create initial form layout
1 parent 1aa3a94 commit e15b309

File tree

13 files changed

+363
-42
lines changed

13 files changed

+363
-42
lines changed
Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,128 @@
11
<section class="sign-up-container">
22
<h2>Create A Free Account</h2>
33

4-
<form [formGroup]="signUpForm"></form>
4+
<form [formGroup]="signUpForm" (ngSubmit)="onSubmit()">
5+
<div class="btn-group">
6+
<p-button label="Sign up through ORCID" class="form-btn btn-full-width">
7+
<img
8+
ngSrc="assets/icons/system/orchid.svg"
9+
alt="ORCID icon"
10+
height="20"
11+
width="20"
12+
/>
13+
</p-button>
14+
<p-button
15+
label="Sign up through Institution"
16+
class="form-btn btn-full-width"
17+
>
18+
<img
19+
ngSrc="assets/icons/menu/institutions.svg"
20+
alt="Institutions icon"
21+
height="20"
22+
width="20"
23+
/>
24+
</p-button>
25+
</div>
26+
27+
<p-divider align="center">
28+
<span class="divider-text">or</span>
29+
</p-divider>
30+
31+
<div class="form-fields">
32+
<div class="field">
33+
<label for="fullName">Full Name</label>
34+
<input
35+
id="fullName"
36+
type="text"
37+
pInputText
38+
formControlName="fullName"
39+
[ngClass]="{
40+
'ng-invalid ng-dirty':
41+
signUpForm.get('fullName')?.invalid &&
42+
signUpForm.get('fullName')?.touched,
43+
}"
44+
placeholder="John Doe"
45+
/>
46+
</div>
47+
48+
<div class="field">
49+
<label
50+
for="email"
51+
[ngClass]="{
52+
'ng-invalid ng-dirty':
53+
signUpForm.get('email')?.invalid &&
54+
signUpForm.get('email')?.touched,
55+
}"
56+
>Email</label
57+
>
58+
<input
59+
id="email"
60+
type="email"
61+
pInputText
62+
formControlName="email"
63+
[ngClass]="{
64+
'ng-invalid ng-dirty':
65+
signUpForm.get('email')?.invalid &&
66+
signUpForm.get('email')?.touched,
67+
}"
68+
placeholder="email@example.com"
69+
/>
70+
</div>
71+
72+
<div class="field">
73+
<label for="password">Password</label>
74+
<p-password
75+
id="password"
76+
formControlName="password"
77+
[toggleMask]="true"
78+
[feedback]="false"
79+
></p-password>
80+
@if (
81+
signUpForm.controls["password"].errors &&
82+
signUpForm.get("password")?.touched
83+
) {
84+
<small class="text-danger">
85+
Your password needs to be at least 8 characters long, include both
86+
lower- and upper-case characters, and have at least one number or
87+
special character
88+
</small>
89+
}
90+
</div>
91+
92+
<div class="field">
93+
<label for="confirmPassword">Confirm Password</label>
94+
<p-password
95+
id="confirmPassword"
96+
formControlName="confirmPassword"
97+
[toggleMask]="true"
98+
[feedback]="false"
99+
></p-password>
100+
@if (
101+
signUpForm.get("confirmPassword")?.errors?.["passwordMismatch"] &&
102+
signUpForm.get("confirmPassword")?.touched
103+
) {
104+
<small class="text-danger">Passwords must match</small>
105+
}
106+
</div>
107+
108+
<div class="field-checkbox">
109+
<p-checkbox
110+
formControlName="agreeToTerms"
111+
[binary]="true"
112+
id="agreeToTerms"
113+
></p-checkbox>
114+
<label for="agreeToTerms">
115+
I agree to the <a href="/terms">Terms of Use</a> and
116+
<a href="/privacy">Privacy Policy</a>
117+
</label>
118+
</div>
119+
</div>
120+
121+
<p-button
122+
class="btn-full-width"
123+
type="submit"
124+
label="Sign Up"
125+
[disabled]="!signUpForm.valid"
126+
></p-button>
127+
</form>
5128
</section>

src/app/features/auth/sign-up/sign-up.component.scss

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,70 @@
66
flex: 1;
77

88
.sign-up-container {
9-
@include mix.flex-column-center;
9+
@include mix.flex-column;
1010
flex: 1;
11-
width: 30rem;
12-
border-radius: 0.9rem;
13-
margin: 0.5rem 0;
14-
justify-content: center;
15-
background-color: white;
16-
border: 1px solid var.$grey-2;
11+
color: var.$dark-blue-1;
12+
width: 32rem;
13+
margin: 2rem 0;
14+
padding: 2rem;
15+
background: white;
16+
border-radius: 0.6rem;
17+
box-shadow: 0 2px 4px var.$grey-outline;
18+
19+
h2 {
20+
text-align: center;
21+
}
22+
23+
form {
24+
@include mix.flex-column;
25+
flex: 1;
26+
27+
.divider-text {
28+
background: white;
29+
}
30+
31+
.btn-group {
32+
@include mix.flex-column;
33+
gap: 1rem;
34+
margin-top: 2rem;
35+
color: var.$dark-blue-1;
36+
}
37+
38+
.form-fields {
39+
flex: 1;
40+
41+
.field {
42+
margin-bottom: 1rem;
43+
44+
.text-danger {
45+
color: var.$red-1;
46+
font-weight: 600;
47+
}
48+
}
49+
50+
.field-checkbox {
51+
@include mix.flex-align-center;
52+
margin: 1.5rem 0;
53+
54+
label {
55+
margin-bottom: 0;
56+
margin-left: 0.5rem;
57+
58+
a {
59+
font-weight: 700;
60+
color: var.$pr-blue-1;
61+
62+
&:hover {
63+
text-decoration: underline;
64+
}
65+
}
66+
}
67+
}
68+
}
69+
70+
.btn-full-width {
71+
justify-self: baseline;
72+
}
73+
}
1774
}
1875
}

src/app/features/auth/sign-up/sign-up.component.ts

Lines changed: 29 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, inject } from '@angular/core';
1+
import { Component, inject, OnInit } from '@angular/core';
22
import {
33
FormBuilder,
44
FormGroup,
@@ -10,7 +10,8 @@ import { InputTextModule } from 'primeng/inputtext';
1010
import { PasswordModule } from 'primeng/password';
1111
import { CheckboxModule } from 'primeng/checkbox';
1212
import { DividerModule } from 'primeng/divider';
13-
import { CommonModule } from '@angular/common';
13+
import { CommonModule, NgOptimizedImage } from '@angular/common';
14+
import { PASSWORD_REGEX, passwordMatchValidator } from './sign-up.helper';
1415

1516
@Component({
1617
selector: 'osf-sign-up',
@@ -23,25 +24,40 @@ import { CommonModule } from '@angular/common';
2324
PasswordModule,
2425
CheckboxModule,
2526
DividerModule,
27+
NgOptimizedImage,
2628
],
2729
templateUrl: './sign-up.component.html',
2830
styleUrl: './sign-up.component.scss',
2931
})
30-
export class SignUpComponent {
31-
signUpForm: FormGroup;
32+
export class SignUpComponent implements OnInit {
33+
signUpForm: FormGroup = new FormGroup({});
34+
3235
fb: FormBuilder = inject(FormBuilder);
36+
passwordRegex: RegExp = PASSWORD_REGEX;
37+
38+
ngOnInit(): void {
39+
this.initializeForm();
40+
}
3341

34-
constructor() {
35-
this.signUpForm = this.fb.group({
36-
fullName: ['', Validators.required],
37-
email: ['', [Validators.required, Validators.email]],
38-
password: ['', [Validators.required, Validators.minLength(8)]],
39-
confirmPassword: ['', Validators.required],
40-
agreeToTerms: [false, Validators.requiredTrue],
41-
});
42+
initializeForm(): void {
43+
this.signUpForm = this.fb.group(
44+
{
45+
fullName: ['', Validators.required],
46+
email: ['', [Validators.required, Validators.email]],
47+
password: [
48+
'',
49+
[Validators.required, Validators.pattern(this.passwordRegex)],
50+
],
51+
confirmPassword: ['', Validators.required],
52+
agreeToTerms: [false, Validators.requiredTrue],
53+
},
54+
{
55+
validators: passwordMatchValidator,
56+
},
57+
);
4258
}
4359

44-
onSubmit() {
60+
onSubmit(): void {
4561
if (this.signUpForm.valid) {
4662
console.log('Form submitted:', this.signUpForm.value);
4763
} else {
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { AbstractControl, ValidationErrors } from '@angular/forms';
2+
3+
export const PASSWORD_REGEX =
4+
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;
5+
6+
export const passwordMatchValidator = (
7+
control: AbstractControl,
8+
): ValidationErrors | null => {
9+
const password = control.get('password');
10+
const confirmPassword = control.get('confirmPassword');
11+
12+
if (password && confirmPassword && password.value !== confirmPassword.value) {
13+
confirmPassword.setErrors({ passwordMismatch: true });
14+
return { passwordMismatch: true };
15+
}
16+
17+
return null;
18+
};
File renamed without changes.

src/app/features/auth/sign-in/sign-in.component.spec.ts renamed to src/app/features/home/home.component.spec.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { ComponentFixture, TestBed } from '@angular/core/testing';
22

3-
import { SignInComponent } from './sign-in.component';
3+
import { HomeComponent } from './home.component';
44

5-
describe('SignInComponent', () => {
6-
let component: SignInComponent;
7-
let fixture: ComponentFixture<SignInComponent>;
5+
describe('HomeComponent', () => {
6+
let component: HomeComponent;
7+
let fixture: ComponentFixture<HomeComponent>;
88

99
beforeEach(async () => {
1010
await TestBed.configureTestingModule({
11-
imports: [SignInComponent],
11+
imports: [HomeComponent],
1212
}).compileComponents();
1313

14-
fixture = TestBed.createComponent(SignInComponent);
14+
fixture = TestBed.createComponent(HomeComponent);
1515
component = fixture.componentInstance;
1616
fixture.detectChanges();
1717
});
Lines changed: 12 additions & 12 deletions
Loading

src/assets/styles/_mixins.scss

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@
1010
align-items: center;
1111
}
1212

13+
@mixin flex-align-center {
14+
display: flex;
15+
align-items: center;
16+
}
17+
1318
@mixin flex-center-right {
1419
display: flex;
1520
justify-content: flex-end;

src/assets/styles/overrides/button.scss

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,8 @@
8989
}
9090

9191
.btn-icon-text,
92-
.btn-icon-text-success {
92+
.btn-icon-text-success,
93+
.form-btn {
9394
.p-button {
9495
background-color: transparent;
9596
color: var.$pr-blue-1;
@@ -126,3 +127,23 @@
126127
}
127128
}
128129
}
130+
131+
.btn-full-width {
132+
.p-button {
133+
width: 100%;
134+
}
135+
}
136+
137+
.form-btn {
138+
.p-button {
139+
color: var.$dark-blue-1;
140+
141+
&:hover {
142+
color: var.$dark-blue-2;
143+
}
144+
145+
.p-button-label {
146+
font-weight: 400;
147+
}
148+
}
149+
}

0 commit comments

Comments
 (0)