Fixed user authentication/registration accessibility issue

- Added the correct autocomplete value
- Removed dangling labels, because aria labels already describe those input fields & we can't use ids in this component because otherwise there are duplicate ids on the /login page
This commit is contained in:
Alexandre Vryghem
2023-12-20 21:18:56 +01:00
committed by Tim Donohue
parent 138e163fa8
commit 63fa8f39f5
7 changed files with 26 additions and 20 deletions

View File

@@ -1,13 +1,13 @@
<div class="container" *ngIf="(registration$ |async)"> <div class="container" *ngIf="(registration$ |async)">
<h3 class="mb-4">{{'forgot-password.form.head' | translate}}</h3> <h1 class="mb-4">{{'forgot-password.form.head' | translate}}</h1>
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header">{{'forgot-password.form.identification.header' | translate}}</div> <div class="card-header">{{'forgot-password.form.identification.header' | translate}}</div>
<div class="card-body"> <div class="card-body">
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<label class="font-weight-bold" <span class="font-weight-bold">{{'forgot-password.form.identification.email' | translate}} </span>
for="email">{{'forgot-password.form.identification.email' | translate}}</label> <span [attr.data-test]="'email' | dsBrowserOnly">{{(registration$ |async).email}}</span>
<span id="email">{{(registration$ |async).email}}</span></div> </div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -21,6 +21,7 @@ import {
createSuccessfulRemoteDataObject$ createSuccessfulRemoteDataObject$
} from '../../shared/remote-data.utils'; } from '../../shared/remote-data.utils';
import { CoreState } from '../../core/core-state.model'; import { CoreState } from '../../core/core-state.model';
import { BrowserOnlyPipe } from '../../shared/utils/browser-only.pipe';
describe('ForgotPasswordFormComponent', () => { describe('ForgotPasswordFormComponent', () => {
let comp: ForgotPasswordFormComponent; let comp: ForgotPasswordFormComponent;
@@ -54,7 +55,10 @@ describe('ForgotPasswordFormComponent', () => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), ReactiveFormsModule], imports: [CommonModule, RouterTestingModule.withRoutes([]), TranslateModule.forRoot(), ReactiveFormsModule],
declarations: [ForgotPasswordFormComponent], declarations: [
BrowserOnlyPipe,
ForgotPasswordFormComponent,
],
providers: [ providers: [
{provide: Router, useValue: router}, {provide: Router, useValue: router},
{provide: ActivatedRoute, useValue: route}, {provide: ActivatedRoute, useValue: route},
@@ -75,7 +79,7 @@ describe('ForgotPasswordFormComponent', () => {
describe('init', () => { describe('init', () => {
it('should initialise mail address', () => { it('should initialise mail address', () => {
const elem = fixture.debugElement.queryAll(By.css('span#email'))[0].nativeElement; const elem = fixture.debugElement.queryAll(By.css('span[data-test="email"]'))[0].nativeElement;
expect(elem.innerHTML).toContain('test@email.org'); expect(elem.innerHTML).toContain('test@email.org');
}); });
}); });

View File

@@ -43,7 +43,8 @@ export class ProfilePageMetadataFormComponent implements OnInit {
new DynamicInputModel({ new DynamicInputModel({
id: 'email', id: 'email',
name: 'email', name: 'email',
readOnly: true readOnly: true,
disabled: true,
}), }),
new DynamicInputModel({ new DynamicInputModel({
id: 'firstname', id: 'firstname',
@@ -55,6 +56,7 @@ export class ProfilePageMetadataFormComponent implements OnInit {
errorMessages: { errorMessages: {
required: 'This field is required' required: 'This field is required'
}, },
autoComplete: 'given-name',
}), }),
new DynamicInputModel({ new DynamicInputModel({
id: 'lastname', id: 'lastname',
@@ -66,10 +68,12 @@ export class ProfilePageMetadataFormComponent implements OnInit {
errorMessages: { errorMessages: {
required: 'This field is required' required: 'This field is required'
}, },
autoComplete: 'family-name',
}), }),
new DynamicInputModel({ new DynamicInputModel({
id: 'phone', id: 'phone',
name: 'eperson.phone' name: 'eperson.phone',
autoComplete: 'tel',
}), }),
new DynamicSelectModel<string>({ new DynamicSelectModel<string>({
id: 'language', id: 'language',

View File

@@ -39,12 +39,14 @@ export class ProfilePageSecurityFormComponent implements OnInit {
new DynamicInputModel({ new DynamicInputModel({
id: 'password', id: 'password',
name: 'password', name: 'password',
inputType: 'password' inputType: 'password',
autoComplete: 'new-password',
}), }),
new DynamicInputModel({ new DynamicInputModel({
id: 'passwordrepeat', id: 'passwordrepeat',
name: 'passwordrepeat', name: 'passwordrepeat',
inputType: 'password' inputType: 'password',
autoComplete: 'new-password',
}) })
]; ];
@@ -79,7 +81,8 @@ export class ProfilePageSecurityFormComponent implements OnInit {
id: 'current-password', id: 'current-password',
name: 'current-password', name: 'current-password',
inputType: 'password', inputType: 'password',
required: true required: true,
autoComplete: 'current-password',
})); }));
} }
if (this.passwordCanBeEmpty) { if (this.passwordCanBeEmpty) {

View File

@@ -1,8 +1,7 @@
<ng-container *ngVar="(user$ | async) as user"> <ng-container *ngVar="(user$ | async) as user">
<div class="container" *ngIf="user"> <div class="container" *ngIf="user">
<h1>{{'profile.title' | translate}}</h1> <h1>{{'profile.title' | translate}}</h1>
<ng-container *ngIf="isResearcherProfileEnabled() | async"> <ng-container>
<h2 class="mb-4">{{'profile.head' | translate}}</h2>
<div class="card mb-4"> <div class="card mb-4">
<div class="card-header">{{'profile.card.researcher' | translate}}</div> <div class="card-header">{{'profile.card.researcher' | translate}}</div>
<div class="card-body"> <div class="card-body">

View File

@@ -1,9 +1,8 @@
<form class="form-login" <form class="form-login"
(ngSubmit)="submit()" (ngSubmit)="submit()"
[formGroup]="form" novalidate> [formGroup]="form" novalidate>
<label class="sr-only">{{"login.form.email" | translate}}</label>
<input [attr.aria-label]="'login.form.email' |translate" <input [attr.aria-label]="'login.form.email' |translate"
autocomplete="off" autocomplete="username"
autofocus autofocus
class="form-control form-control-lg position-relative" class="form-control form-control-lg position-relative"
formControlName="email" formControlName="email"
@@ -11,9 +10,8 @@
required required
type="email" type="email"
[attr.data-test]="'email' | dsBrowserOnly"> [attr.data-test]="'email' | dsBrowserOnly">
<label class="sr-only">{{"login.form.password" | translate}}</label>
<input [attr.aria-label]="'login.form.password' |translate" <input [attr.aria-label]="'login.form.password' |translate"
autocomplete="off" autocomplete="current-password"
class="form-control form-control-lg position-relative mb-3" class="form-control form-control-lg position-relative mb-3"
placeholder="{{'login.form.password' | translate}}" placeholder="{{'login.form.password' | translate}}"
formControlName="password" formControlName="password"

View File

@@ -1710,7 +1710,7 @@
"forgot-password.form.label.passwordrepeat": "Retype to confirm", "forgot-password.form.label.passwordrepeat": "Retype to confirm",
"forgot-password.form.error.empty-password": "Please enter a password in the box below.", "forgot-password.form.error.empty-password": "Please enter a password in the boxes above.",
"forgot-password.form.error.matching-passwords": "The passwords do not match.", "forgot-password.form.error.matching-passwords": "The passwords do not match.",
@@ -3340,8 +3340,6 @@
"profile.special.groups.head": "Authorization special groups you belong to", "profile.special.groups.head": "Authorization special groups you belong to",
"profile.head": "Update Profile",
"profile.metadata.form.error.firstname.required": "First Name is required", "profile.metadata.form.error.firstname.required": "First Name is required",
"profile.metadata.form.error.lastname.required": "Last Name is required", "profile.metadata.form.error.lastname.required": "Last Name is required",