Skip to content
Open
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
Expand Up @@ -84,8 +84,9 @@ <h3 id="search" class="border-bottom pb-2">
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
<div class="flex-grow-1 me-3">
<div class="mb-3 input-group me-3">
<input type="text" name="query" id="query" formControlName="query"
class="form-control" aria-label="Search input">
<label for="members-list-query" class="visually-hidden">{{messagePrefix + '.search.input' | translate}}</label>
<input type="text" name="query" id="members-list-query" formControlName="query"
class="form-control" [attr.aria-label]="messagePrefix + '.search.input' | translate">
<span class="input-group-append">
<button type="submit" class="search-button btn btn-primary">
<i class="fas fa-search"></i> {{ messagePrefix + '.search.button' | translate }}</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,19 @@ describe('MembersListComponent', () => {
expect(comp).toBeDefined();
}));

it('should label its search input with an id of its own, not the generic one', () => {
const input: HTMLInputElement = fixture.nativeElement.querySelector('input[name="query"]');
const label: HTMLLabelElement = fixture.nativeElement.querySelector('label[for="' + input.id + '"]');

expect(input.id).toBeTruthy();
// ds-members-list and ds-subgroups-list render side by side on the group edit page,
// so a shared generic id makes one label point at the other component's input
expect(input.id).not.toEqual('query');
expect(label).toBeTruthy();
expect(label.htmlFor).toEqual(input.id);
expect(Array.from(label.classList)).toContain('visually-hidden');
});

describe('current members list', () => {
it('should show list of eperson members of current active group', () => {
const epersonIdsFound = fixture.debugElement.queryAll(By.css('#ePeopleMembersOfGroup tr td:first-child'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,9 @@ <h4 id="search" class="border-bottom pb-2">
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
<div class="flex-grow-1 me-3">
<div class="mb-3 input-group me-3">
<input type="text" name="query" id="query" formControlName="query"
class="form-control" aria-label="Search input">
<label for="subgroups-list-query" class="visually-hidden">{{messagePrefix + '.search.input' | translate}}</label>
<input type="text" name="query" id="subgroups-list-query" formControlName="query"
class="form-control" [attr.aria-label]="messagePrefix + '.search.input' | translate">
<span class="input-group-append">
<button type="submit" class="search-button btn btn-primary">
<i class="fas fa-search"></i> {{ messagePrefix + '.search.button' | translate }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,19 @@ describe('SubgroupsListComponent', () => {
expect(comp).toBeDefined();
}));

it('should label its search input with an id of its own, not the generic one', () => {
const input: HTMLInputElement = fixture.nativeElement.querySelector('input[name="query"]');
const label: HTMLLabelElement = fixture.nativeElement.querySelector('label[for="' + input.id + '"]');

expect(input.id).toBeTruthy();
// ds-members-list and ds-subgroups-list render side by side on the group edit page,
// so a shared generic id makes one label point at the other component's input
expect(input.id).not.toEqual('query');
expect(label).toBeTruthy();
expect(label.htmlFor).toEqual(input.id);
expect(Array.from(label.classList)).toContain('visually-hidden');
});

describe('current subgroup list', () => {
it('should show list of subgroups of current active group', () => {
const groupIdsFound = fixture.debugElement.queryAll(By.css('#subgroupsOfGroup tr td:first-child'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ <h2 id="search" class="border-bottom pb-2">{{messagePrefix + 'search.head' | tra
<form [formGroup]="searchForm" (ngSubmit)="search(searchForm.value)" class="d-flex justify-content-between">
<div class="flex-grow-1 me-3">
<div class="mb-3 input-group">
<input type="text" name="query" id="query" formControlName="query"
<label for="groups-registry-query" class="visually-hidden">{{messagePrefix + 'search.placeholder' | translate}}</label>
<input type="text" name="query" id="groups-registry-query" formControlName="query"
class="form-control" [attr.aria-label]="messagePrefix + 'search.placeholder' | translate"
[placeholder]="(messagePrefix + 'search.placeholder' | translate)" >
<span class="input-group-append">
Expand Down
3 changes: 2 additions & 1 deletion src/app/search-navbar/search-navbar.component.html
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
<div [title]="'nav.search' | translate" (dsClickOutside)="collapse()">
<div class="d-inline-block position-relative">
<form [formGroup]="searchForm" (ngSubmit)="onSubmit(searchForm.value)" autocomplete="on" class="d-flex">
<input #searchInput [@toggleAnimation]="isExpanded" [attr.aria-label]="('nav.search' | translate)" name="query"
<label for="search-navbar-query" class="visually-hidden">{{ 'nav.search' | translate }}</label>
<input #searchInput id="search-navbar-query" [@toggleAnimation]="isExpanded" [attr.aria-label]="('nav.search' | translate)" name="query"
formControlName="query" type="text" placeholder="{{searchExpanded ? ('nav.search' | translate) : ''}}"
class="d-inline-block bg-transparent position-absolute form-control dropdown-menu-end p1"
[class.display]="searchExpanded ? 'inline-block' : 'none'"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@
[formGroup]="searchForm"
(ngSubmit)="submit(searchForm.value); $event.stopImmediatePropagation();" >
<div>
<select name="scope" id="scope" formControlName="scope" class="form-select" aria-label="Search scope">
<label for="eperson-search-scope" class="visually-hidden">{{labelPrefix + 'search.scope' | translate}}</label>
<select name="scope" id="eperson-search-scope" formControlName="scope" class="form-select"
[attr.aria-label]="labelPrefix + 'search.scope' | translate">
<option value="metadata">{{labelPrefix + 'search.scope.metadata' | translate}}</option>
<option value="email">{{labelPrefix + 'search.scope.email' | translate}}</option>
</select>
</div>
<div class="flex-grow-1 me-3 ms-3">
<div class="mb-3 input-group">
<input type="text" name="query" id="query" formControlName="query"
class="form-control" aria-label="Search input">
<label for="eperson-search-query" class="visually-hidden">{{labelPrefix + 'search.input' | translate}}</label>
<input type="text" name="query" id="eperson-search-query" formControlName="query"
class="form-control" [attr.aria-label]="labelPrefix + 'search.input' | translate">
<button type="submit" class="search-button btn btn-primary">
<i class="fas fa-search"></i> {{ labelPrefix + 'search.button' | translate }}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,36 @@ describe('EpersonSearchBoxComponent test suite', () => {
expect(comp.searchForm.controls.query.value).toBe('');
});

it('should label the scope select and the query input, each resolving to its own control', () => {
fixture.detectChanges();
const element: HTMLElement = fixture.nativeElement;
const labels: HTMLLabelElement[] = Array.from(element.querySelectorAll('label'));

expect(labels.length).toEqual(2);
labels.forEach((label: HTMLLabelElement) => {
expect(label.htmlFor).toBeTruthy();
const control: HTMLElement = element.querySelector('[id="' + label.htmlFor + '"]');
expect(control).toBeTruthy();
expect(control.id).toEqual(label.htmlFor);
// the label must not be rendered visibly (Bootstrap 5 dropped the old screen-reader class)
expect(Array.from(label.classList)).toContain('visually-hidden');
});
});

it('should name both controls from the translated keys rather than hardcoded English', () => {
fixture.detectChanges();
const input: HTMLInputElement = fixture.nativeElement.querySelector('input[name="query"]');
const select: HTMLSelectElement = fixture.nativeElement.querySelector('select[name="scope"]');

// TranslateModule.forRoot() with no catalogue echoes the key back, so the key is the observable value
expect(input.getAttribute('aria-label')).toEqual('admin.access-control.epeople.search.input');
expect(select.getAttribute('aria-label')).toEqual('admin.access-control.epeople.search.scope');
// and the id is no longer the generic one that other components on the same page also used
expect(input.id).toBeTruthy();
expect(input.id).not.toEqual('query');
expect(select.id).not.toEqual('scope');
});

it('should emit new search event', () => {
const data = {
scope: 'metadata',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@
(ngSubmit)="submit(searchForm.value); $event.stopImmediatePropagation();" >
<div class="flex-grow-1 me-3">
<div class="mb-3 input-group">
<input type="text" name="query" id="query" formControlName="query"
class="form-control" aria-label="Search input">
<label for="group-search-query" class="visually-hidden">{{labelPrefix + 'search.input' | translate}}</label>
<input type="text" name="query" id="group-search-query" formControlName="query"
class="form-control" [attr.aria-label]="labelPrefix + 'search.input' | translate">
<button type="submit" class="search-button btn btn-primary">
<i class="fas fa-search"></i> {{ labelPrefix + 'search.button' | translate }}
</button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
{{ 'browse.startsWith.jump' | translate }}
</span>
<div class="col-6 col-md-2">
<label for="year-select" class="visually-hidden">{{ 'browse.startsWith.choose_year.label' | translate }}</label>
<select id="year-select" class="form-select" (change)="setStartsWithYearEvent($event)" [attr.aria-label]="'browse.startsWith.choose_year.label' |translate">
<option [value]="-1" [selected]="!startsWithYear">
{{ 'browse.startsWith.choose_year' | translate }}
Expand All @@ -18,6 +19,7 @@
</select>
</div>
<div class="col-6 col-md-2">
<label for="month-select" class="visually-hidden">{{ 'browse.startsWith.months.none.label' | translate }}</label>
<select id="month-select" class="form-select" (change)="setStartsWithMonthEvent($event)" [attr.aria-label]="'browse.startsWith.months.none.label' |translate">
@for (option of monthOptions; track option) {
<option
Expand All @@ -30,7 +32,8 @@
</div>
<div class="col-12 col-md-6">
<div class="mb-3 input-group pt-1 pt-md-0">
<input class="form-control" placeholder="{{'browse.startsWith.type_date' | translate}}" [attr.aria-label]="'browse.startsWith.type_date.label' |translate" type="text" name="startsWith"
<label for="startsWith-input" class="visually-hidden">{{ 'browse.startsWith.type_date.label' | translate }}</label>
<input id="startsWith-input" class="form-control" placeholder="{{'browse.startsWith.type_date' | translate}}" [attr.aria-label]="'browse.startsWith.type_date.label' |translate" type="text" name="startsWith"
formControlName="startsWith" [value]="getStartsWith() ? getStartsWith() : ''"/>
<button class="btn btn-primary" type="submit" role="button" tabindex="0"><i class="fas fa-book-open"></i> {{ 'browse.startsWith.submit' | translate }}</button>
</div>
Expand Down
Loading