Skip to content

Commit

Permalink
[ADF-5180] Add keyboard accessibility for filters (#5868)
Browse files Browse the repository at this point in the history
  • Loading branch information
BaptisteMahe committed Jul 15, 2020
1 parent d167259 commit 586562c
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
<mat-checkbox
*ngFor="let option of options"
[checked]="option.checked"
(keydown.enter)="option.checked = !option.checked"
[attr.data-automation-id]="'checkbox-' + (option.name)"
(change)="changeHandler($event, option)"
class="adf-facet-filter">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,24 @@ describe('SearchCheckListComponent', () => {
expect(component.options.items).toEqual(options);
});

it('should handle enter key as click on checkboxes', () => {
component.options = new SearchFilterList<SearchListOption>([
{ name: 'Folder', value: "TYPE:'cm:folder'", checked: false },
{ name: 'Document', value: "TYPE:'cm:content'", checked: false }
]);

component.ngOnInit();
fixture.detectChanges();

const optionElements = fixture.debugElement.queryAll(By.css('mat-checkbox'));

optionElements[0].triggerEventHandler('keydown.enter', {});
expect(component.options.items[0].checked).toBeTruthy();

optionElements[0].triggerEventHandler('keydown.enter', {});
expect(component.options.items[0].checked).toBeFalsy();
});

it('should setup operator from the settings', () => {
component.settings = <any> { operator: 'AND' };
component.ngOnInit();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,36 +3,40 @@
<button mat-icon-button [matMenuTriggerFor]="filter"
id="filter-menu-button"
#menuTrigger="matMenuTrigger"
(click)="onMenuButtonClick($event)"
(click)="$event.stopPropagation()"
(menuOpened)="onMenuOpen()"
(keyup.enter)="$event.stopPropagation()"
class="adf-filter-button"
[matTooltip]="getTooltipTranslation(col?.title)">
<adf-icon value="adf:filter"
[ngClass]="{ 'adf-icon-active': isActive()|| menuTrigger.menuOpen }"
[ngClass]="{ 'adf-icon-active': isActive() || menuTrigger.menuOpen }"
matBadge="filter"
matBadgeColor="warn"
[matBadgeHidden]="!isActive()"></adf-icon>
</button>

<mat-menu #filter="matMenu" class="adf-filter-menu">
<div (click)="onMenuClick($event)" class="adf-filter-container">
<div class="adf-filter-title">{{ category?.name | translate }}</div>
<adf-search-widget-container
(keydown.enter)="onApply()"
[id]="category?.id"
[selector]="category?.component?.selector"
[settings]="category?.component?.settings">
</adf-search-widget-container>
<mat-menu #filter="matMenu" class="adf-filter-menu" (closed)="onClosed()">
<div #filterContainer (keydown.tab)="$event.stopPropagation();">
<div (click)="$event.stopPropagation()" class="adf-filter-container">
<div class="adf-filter-title">{{ category?.name | translate }}</div>
<adf-search-widget-container
(keydown.enter)="onEnterPressed()"
[id]="category?.id"
[selector]="category?.component?.selector"
[settings]="category?.component?.settings">
</adf-search-widget-container>
</div>
<mat-dialog-actions class="adf-filter-actions">
<button mat-button id="clear-filter-button"
(click)="onClearButtonClick($event)">{{ 'SEARCH.SEARCH_HEADER.CLEAR' | translate | uppercase }}
</button>
<button mat-button color="primary"
id="apply-filter-button"
class="adf-filter-apply-button"
(click)="onApply()">{{ 'SEARCH.SEARCH_HEADER.APPLY' | translate | uppercase }}
</button>
</mat-dialog-actions>
</div>
<mat-dialog-actions class="adf-filter-actions">
<button mat-button id="clear-filter-button"
(click)="onClearButtonClick($event)">{{ 'SEARCH.SEARCH_HEADER.CLEAR' | translate | uppercase }}
</button>
<button mat-button color="primary"
id="apply-filter-button"
class="adf-filter-apply-button" (click)="onApply()">
{{ 'SEARCH.SEARCH_HEADER.APPLY' | translate | uppercase }}
</button>
</mat-dialog-actions>
</mat-menu>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { fakeNodePaging } from '../../../mock';
import { SEARCH_QUERY_SERVICE_TOKEN } from '../../search-query-service.token';
import { By } from '@angular/platform-browser';
import { SimpleChange } from '@angular/core';
import { MatMenuTrigger } from '@angular/material/menu';

const mockCategory: any = {
'id': 'queryName',
Expand Down Expand Up @@ -209,4 +210,47 @@ describe('SearchHeaderComponent', () => {
fixture.detectChanges();
await fixture.whenStable();
});

describe('Accessibility', () => {

it('should set up a focus trap on the filter when the menu is opened', async () => {
expect(component.focusTrap).toBeUndefined();

const menuButton: HTMLButtonElement = fixture.nativeElement.querySelector('#filter-menu-button');
menuButton.click();
fixture.detectChanges();

expect(component.focusTrap).toBeDefined();
expect(component.focusTrap._element).toBe(component.filterContainer.nativeElement);
});

it('should focus the input element when the menu is opened', async () => {
const menuButton: HTMLButtonElement = fixture.nativeElement.querySelector('#filter-menu-button');
menuButton.click();

fixture.detectChanges();
await fixture.whenStable();

const inputElement = fixture.debugElement.query(By.css('.mat-input-element'));
expect(document.activeElement).toBe(inputElement.nativeElement);

});

it('should focus the menu trigger when the menu is closed', async () => {
const menuButton: HTMLButtonElement = fixture.nativeElement.querySelector('#filter-menu-button');
menuButton.click();

fixture.detectChanges();
await fixture.whenStable();

const matMenuTrigger = fixture.debugElement.query(By.directive(MatMenuTrigger)).injector.get(MatMenuTrigger);
matMenuTrigger.closeMenu();

fixture.detectChanges();
await fixture.whenStable();

const matMenuButton = fixture.debugElement.query(By.css('#filter-menu-button'));
expect(document.activeElement).toBe(matMenuButton.nativeElement);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ import {
ViewEncapsulation,
ViewChild,
Inject,
OnDestroy
OnDestroy,
ElementRef
} from '@angular/core';
import { ConfigurableFocusTrapFactory, ConfigurableFocusTrap } from '@angular/cdk/a11y';
import { DataColumn, TranslationService } from '@alfresco/adf-core';
import { SearchWidgetContainerComponent } from '../search-widget-container/search-widget-container.component';
import { SearchHeaderQueryBuilderService } from '../../search-header-query-builder.service';
Expand Down Expand Up @@ -72,13 +74,18 @@ export class SearchHeaderComponent implements OnInit, OnChanges, OnDestroy {
@ViewChild(SearchWidgetContainerComponent)
widgetContainer: SearchWidgetContainerComponent;

@ViewChild('filterContainer')
filterContainer: ElementRef;

category: SearchCategory;
isFilterServiceActive: boolean;
focusTrap: ConfigurableFocusTrap;

private onDestroy$ = new Subject<boolean>();

constructor(@Inject(SEARCH_QUERY_SERVICE_TOKEN) private searchHeaderQueryBuilder: SearchHeaderQueryBuilderService,
private translationService: TranslationService) {
private translationService: TranslationService,
private focusTrapFactory: ConfigurableFocusTrapFactory) {
this.isFilterServiceActive = this.searchHeaderQueryBuilder.isFilterServiceActive();
}

Expand Down Expand Up @@ -120,12 +127,10 @@ export class SearchHeaderComponent implements OnInit, OnChanges, OnDestroy {
this.onDestroy$.complete();
}

onMenuButtonClick(event: Event) {
event.stopPropagation();
}

onMenuClick(event: Event) {
event.stopPropagation();
onEnterPressed() {
if (this.widgetContainer.selector !== 'check-list') {
this.onApply();
}
}

onApply() {
Expand Down Expand Up @@ -165,4 +170,16 @@ export class SearchHeaderComponent implements OnInit, OnChanges, OnDestroy {
isActive(): boolean {
return this.widgetContainer && this.widgetContainer.componentRef && this.widgetContainer.componentRef.instance.isActive;
}

onMenuOpen() {
if (this.filterContainer && !this.focusTrap) {
this.focusTrap = this.focusTrapFactory.create(this.filterContainer.nativeElement);
this.focusTrap.focusInitialElement();
}
}

onClosed() {
this.focusTrap.destroy();
this.focusTrap = null;
}
}

0 comments on commit 586562c

Please sign in to comment.