Skip to content
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

feat: Add a tooltip for password checks #3281

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<div class="tooltip-container">
<ng-container>
<div class="tooltip" role="tooltip">
<div class="tooltip__text" role="heading">Password should contain:</div>
<ul class="tooltip__list" role="list" aria-label="Password requirements checklist">
bistaastha marked this conversation as resolved.
Show resolved Hide resolved
<li class="tooltip__list__check" *ngFor="let criteria of passwordCriteria">
<ion-icon
*ngIf="!criteria.isValid"
class="tooltip__list__check__icon tooltip__list__check__invalid"
src="../../../assets/svg/check-circle-outline.svg"
></ion-icon>
<ion-icon
*ngIf="criteria.isValid"
class="tooltip__list__check__icon tooltip__list__check__valid"
src="../../../assets/svg/check-circle-fill.svg"
></ion-icon>
bistaastha marked this conversation as resolved.
Show resolved Hide resolved
<span>{{ criteria.message }}</span>
</li>
</ul>
</div>
</ng-container>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
@import '../../../../theme/colors';
.tooltip-container {
position: absolute;
z-index: 1000;
border-radius: 8px;
background: $pure-white;
box-shadow: 0 0 8px rgba(0, 0, 0, 0.1);
color: $blue-black;

.tooltip {
display: inline-flex;
padding: 16px;
flex-direction: column;
align-items: flex-start;
gap: 8px;
border-radius: 8px;
background: $white;

bistaastha marked this conversation as resolved.
Show resolved Hide resolved
&::before {
content: '';
position: absolute;
bottom: 100%; // Position the arrow above the tooltip box
left: 50%;
transform: translateX(-50%);
border-width: 8px;
border-style: solid;
border-color: transparent transparent $white transparent;
filter: drop-shadow(0 -2px 2px rgba(0, 0, 0, 0.1));
}

&__text {
font-size: 12px;
font-style: normal;
font-weight: 500;
line-height: 1.25;
}
bistaastha marked this conversation as resolved.
Show resolved Hide resolved

&__list {
list-style: none;
padding: 0;
margin: 0;
font-size: 12px;
line-height: 1.25;

&__check {
display: flex;
align-items: center;
align-content: center;
gap: 4px;
flex-wrap: wrap;

&__icon {
width: 12px;
height: 12px;
}

&__valid {
fill: $green;
}

&__invalid {
fill: $grey-light;
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
import { IonicModule } from '@ionic/angular';

import { PasswordCheckTooltipComponent } from './password-check-tooltip.component';
bistaastha marked this conversation as resolved.
Show resolved Hide resolved

describe('PasswordCheckTooltipComponent', () => {
let component: PasswordCheckTooltipComponent;
let fixture: ComponentFixture<PasswordCheckTooltipComponent>;

beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({
declarations: [PasswordCheckTooltipComponent],
imports: [IonicModule.forRoot()],
}).compileComponents();

fixture = TestBed.createComponent(PasswordCheckTooltipComponent);
component = fixture.componentInstance;
fixture.detectChanges();
}));
bistaastha marked this conversation as resolved.
Show resolved Hide resolved

it('should create', () => {
expect(component).toBeTruthy();
});
bistaastha marked this conversation as resolved.
Show resolved Hide resolved
});
bistaastha marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { Component, Input, Output, EventEmitter, OnChanges, OnInit } from '@angular/core';
import { PasswordChecks } from './password-checks.model';
import { PasswordCriteria } from './password-criteria.model';

@Component({
selector: 'app-password-check-tooltip',
templateUrl: './password-check-tooltip.component.html',
styleUrls: ['./password-check-tooltip.component.scss'],
})
export class PasswordCheckTooltipComponent implements OnChanges, OnInit {
@Input() password: string;

@Output() isPasswordValid = new EventEmitter<boolean>();
bistaastha marked this conversation as resolved.
Show resolved Hide resolved

passwordChecks: PasswordChecks = {
lengthValid: false,
uppercaseValid: false,
lowercaseValid: false,
numberValid: false,
specialCharValid: false,
};
bistaastha marked this conversation as resolved.
Show resolved Hide resolved

passwordCriteria: PasswordCriteria[] = [
{
isValid: this.passwordChecks.lengthValid,
message: '12 to 32 characters',
},
{
isValid: this.passwordChecks.uppercaseValid,
message: '1 uppercase character',
},
{
isValid: this.passwordChecks.lowercaseValid,
message: '1 lowercase character',
},
{
isValid: this.passwordChecks.numberValid,
message: '1 number',
},
{
isValid: this.passwordChecks.specialCharValid,
message: '1 special character',
},
];
bistaastha marked this conversation as resolved.
Show resolved Hide resolved

ngOnChanges(): void {
this.validatePassword();
}
bistaastha marked this conversation as resolved.
Show resolved Hide resolved

ngOnInit(): void {
this.validatePassword();
}
bistaastha marked this conversation as resolved.
Show resolved Hide resolved

validatePassword(): void {
const specialCharRegex = /[!@#$%^&*()+\-:;<=>{}|~?]/;

this.passwordChecks.lengthValid = this.password.length >= 12 && this.password.length <= 32;
this.passwordChecks.uppercaseValid = /[A-Z]/.test(this.password);
this.passwordChecks.lowercaseValid = /[a-z]/.test(this.password);
this.passwordChecks.numberValid = /[0-9]/.test(this.password);
this.passwordChecks.specialCharValid = specialCharRegex.test(this.password);

const allValid = Object.values(this.passwordChecks).every(Boolean);
this.isPasswordValid.emit(allValid);
}
bistaastha marked this conversation as resolved.
Show resolved Hide resolved
bistaastha marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export interface PasswordChecks {
bistaastha marked this conversation as resolved.
Show resolved Hide resolved
lengthValid: boolean;
uppercaseValid: boolean;
lowercaseValid: boolean;
numberValid: boolean;
specialCharValid: boolean;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export interface PasswordCriteria {
isValid: boolean;
message: string;
}
bistaastha marked this conversation as resolved.
Show resolved Hide resolved
Loading