Merge pull request #1315 from tdonohue/header_keyboard_fix

Header Accessibility Fix: Allow enter key or spacebar to expand or contract menu based on current active status
This commit is contained in:
Tim Donohue
2021-10-04 10:59:34 -05:00
committed by GitHub
3 changed files with 90 additions and 6 deletions

View File

@@ -212,13 +212,17 @@ Once you have tested the Pull Request, please add a comment and/or approval to t
### Unit Tests ### Unit Tests
Unit tests use Karma. You can find the configuration file at the same level of this README file:`./karma.conf.js` If you are going to use a remote test environment you need to edit the `./karma.conf.js`. Follow the instructions you will find inside it. To executing tests whenever any file changes you can modify the 'autoWatch' option to 'true' and 'singleRun' option to 'false'. A coverage report is also available at: http://localhost:9876/ after you run: `yarn run coverage`. Unit tests use the [Jasmine test framework](https://jasmine.github.io/), and are run via [Karma](https://karma-runner.github.io/).
You can find the Karma configuration file at the same level of this README file:`./karma.conf.js` If you are going to use a remote test environment you need to edit the `./karma.conf.js`. Follow the instructions you will find inside it. To executing tests whenever any file changes you can modify the 'autoWatch' option to 'true' and 'singleRun' option to 'false'. A coverage report is also available at: http://localhost:9876/ after you run: `yarn run coverage`.
The default browser is Google Chrome. The default browser is Google Chrome.
Place your tests in the same location of the application source code files that they test. Place your tests in the same location of the application source code files that they test, e.g. ending with `*.component.spec.ts`
and run: `yarn run test` and run: `yarn test`
If you run into odd test errors, see the Angular guide to debugging tests: https://angular.io/guide/test-debugging
### E2E Tests ### E2E Tests
@@ -258,6 +262,10 @@ _Hint: Creating e2e tests is easiest in an IDE (like Visual Studio), as it can h
More Information: [docs.cypress.io](https://docs.cypress.io/) has great guides & documentation helping you learn more about writing/debugging e2e tests in Cypress. More Information: [docs.cypress.io](https://docs.cypress.io/) has great guides & documentation helping you learn more about writing/debugging e2e tests in Cypress.
### Learning how to build tests
See our [DSpace Code Testing Guide](https://wiki.lyrasis.org/display/DSPACE/Code+Testing+Guide) for more hints/tips.
Documentation Documentation
-------------- --------------

View File

@@ -1,5 +1,8 @@
<div class="nav-item dropdown expandable-navbar-section" <div class="nav-item dropdown expandable-navbar-section"
(keyup.enter)="activateSection($event)" *ngVar="(active | async) as isActive"
(keyup.enter)="isActive ? deactivateSection($event) : activateSection($event)"
(keyup.space)="isActive ? deactivateSection($event) : activateSection($event)"
(keydown.space)="$event.preventDefault()"
(mouseenter)="activateSection($event)" (mouseenter)="activateSection($event)"
(mouseleave)="deactivateSection($event)"> (mouseleave)="deactivateSection($event)">
<a href="#" class="nav-link dropdown-toggle" routerLinkActive="active" <a href="#" class="nav-link dropdown-toggle" routerLinkActive="active"

View File

@@ -9,6 +9,7 @@ import { HostWindowService } from '../../shared/host-window.service';
import { MenuService } from '../../shared/menu/menu.service'; import { MenuService } from '../../shared/menu/menu.service';
import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub'; import { HostWindowServiceStub } from '../../shared/testing/host-window-service.stub';
import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { VarDirective } from '../../shared/utils/var.directive';
describe('ExpandableNavbarSectionComponent', () => { describe('ExpandableNavbarSectionComponent', () => {
let component: ExpandableNavbarSectionComponent; let component: ExpandableNavbarSectionComponent;
@@ -19,7 +20,7 @@ describe('ExpandableNavbarSectionComponent', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NoopAnimationsModule], imports: [NoopAnimationsModule],
declarations: [ExpandableNavbarSectionComponent, TestComponent], declarations: [ExpandableNavbarSectionComponent, TestComponent, VarDirective],
providers: [ providers: [
{ provide: 'sectionDataProvider', useValue: {} }, { provide: 'sectionDataProvider', useValue: {} },
{ provide: MenuService, useValue: menuService }, { provide: MenuService, useValue: menuService },
@@ -76,6 +77,78 @@ describe('ExpandableNavbarSectionComponent', () => {
}); });
}); });
describe('when Enter key is pressed on section header (while inactive)', () => {
beforeEach(() => {
spyOn(menuService, 'activateSection');
// Make sure section is 'inactive'. Requires calling ngOnInit() to update component 'active' property.
spyOn(menuService, 'isSectionActive').and.returnValue(observableOf(false));
component.ngOnInit();
fixture.detectChanges();
const sidebarToggler = fixture.debugElement.query(By.css('div.nav-item.dropdown'));
// dispatch the (keyup.enter) action used in our component HTML
sidebarToggler.nativeElement.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }));
});
it('should call activateSection on the menuService', () => {
expect(menuService.activateSection).toHaveBeenCalled();
});
});
describe('when Enter key is pressed on section header (while active)', () => {
beforeEach(() => {
spyOn(menuService, 'deactivateSection');
// Make sure section is 'active'. Requires calling ngOnInit() to update component 'active' property.
spyOn(menuService, 'isSectionActive').and.returnValue(observableOf(true));
component.ngOnInit();
fixture.detectChanges();
const sidebarToggler = fixture.debugElement.query(By.css('div.nav-item.dropdown'));
// dispatch the (keyup.enter) action used in our component HTML
sidebarToggler.nativeElement.dispatchEvent(new KeyboardEvent('keyup', { key: 'Enter' }));
});
it('should call deactivateSection on the menuService', () => {
expect(menuService.deactivateSection).toHaveBeenCalled();
});
});
describe('when spacebar is pressed on section header (while inactive)', () => {
beforeEach(() => {
spyOn(menuService, 'activateSection');
// Make sure section is 'inactive'. Requires calling ngOnInit() to update component 'active' property.
spyOn(menuService, 'isSectionActive').and.returnValue(observableOf(false));
component.ngOnInit();
fixture.detectChanges();
const sidebarToggler = fixture.debugElement.query(By.css('div.nav-item.dropdown'));
// dispatch the (keyup.space) action used in our component HTML
sidebarToggler.nativeElement.dispatchEvent(new KeyboardEvent('keyup', { key: ' ' }));
});
it('should call activateSection on the menuService', () => {
expect(menuService.activateSection).toHaveBeenCalled();
});
});
describe('when spacebar is pressed on section header (while active)', () => {
beforeEach(() => {
spyOn(menuService, 'deactivateSection');
// Make sure section is 'active'. Requires calling ngOnInit() to update component 'active' property.
spyOn(menuService, 'isSectionActive').and.returnValue(observableOf(true));
component.ngOnInit();
fixture.detectChanges();
const sidebarToggler = fixture.debugElement.query(By.css('div.nav-item.dropdown'));
// dispatch the (keyup.space) action used in our component HTML
sidebarToggler.nativeElement.dispatchEvent(new KeyboardEvent('keyup', { key: ' ' }));
});
it('should call deactivateSection on the menuService', () => {
expect(menuService.deactivateSection).toHaveBeenCalled();
});
});
describe('when a click occurs on the section header', () => { describe('when a click occurs on the section header', () => {
beforeEach(() => { beforeEach(() => {
spyOn(menuService, 'toggleActiveSection'); spyOn(menuService, 'toggleActiveSection');
@@ -96,7 +169,7 @@ describe('ExpandableNavbarSectionComponent', () => {
beforeEach(waitForAsync(() => { beforeEach(waitForAsync(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
imports: [NoopAnimationsModule], imports: [NoopAnimationsModule],
declarations: [ExpandableNavbarSectionComponent, TestComponent], declarations: [ExpandableNavbarSectionComponent, TestComponent, VarDirective],
providers: [ providers: [
{ provide: 'sectionDataProvider', useValue: {} }, { provide: 'sectionDataProvider', useValue: {} },
{ provide: MenuService, useValue: menuService }, { provide: MenuService, useValue: menuService },