From b35f8b3be6795e7f8b411bed151ae99a99b28f76 Mon Sep 17 00:00:00 2001 From: crisbeto Date: Sun, 7 Apr 2019 19:42:39 +0200 Subject: [PATCH] fix(expansion-panel): don't handle events with modifier keys in accordion Fixes the accordion reacting to and preventing the default action of the home and end keys, even if they have modifiers, which could conflict with the user's shortcuts. --- src/material/expansion/accordion.spec.ts | 48 ++++++++++++++++++++++-- src/material/expansion/accordion.ts | 14 ++++--- 2 files changed, 54 insertions(+), 8 deletions(-) diff --git a/src/material/expansion/accordion.spec.ts b/src/material/expansion/accordion.spec.ts index ef384133ab76..4f0e7433ff2f 100644 --- a/src/material/expansion/accordion.spec.ts +++ b/src/material/expansion/accordion.spec.ts @@ -8,7 +8,7 @@ import { MatExpansionPanel, MatExpansionPanelHeader, } from './index'; -import {dispatchKeyboardEvent} from '@angular/cdk/testing'; +import {dispatchKeyboardEvent, createKeyboardEvent, dispatchEvent} from '@angular/cdk/testing'; import {DOWN_ARROW, UP_ARROW, HOME, END} from '@angular/cdk/keycodes'; import {FocusMonitor} from '@angular/cdk/a11y'; @@ -201,10 +201,31 @@ describe('MatAccordion', () => { const headers = fixture.componentInstance.headers.toArray(); headers.forEach(header => spyOn(header, 'focus')); - dispatchKeyboardEvent(headerElements[headerElements.length - 1].nativeElement, 'keydown', HOME); + const event = dispatchKeyboardEvent( + headerElements[headerElements.length - 1].nativeElement, 'keydown', HOME); fixture.detectChanges(); expect(headers[0].focus).toHaveBeenCalledTimes(1); + expect(event.defaultPrevented).toBe(true); + }); + + it('should not handle the home key when it is pressed with a modifier', () => { + const fixture = TestBed.createComponent(SetOfItems); + fixture.detectChanges(); + + const headerElements = fixture.debugElement.queryAll(By.css('mat-expansion-panel-header')); + const headers = fixture.componentInstance.headers.toArray(); + + headers.forEach(header => spyOn(header, 'focus')); + const eventTarget = headerElements[headerElements.length - 1].nativeElement; + const event = createKeyboardEvent('keydown', HOME, eventTarget); + Object.defineProperty(event, 'altKey', {get: () => true}); + + dispatchEvent(eventTarget, event); + fixture.detectChanges(); + + expect(headers[0].focus).not.toHaveBeenCalled(); + expect(event.defaultPrevented).toBe(false); }); it('should focus the last header when pressing the end key', () => { @@ -215,10 +236,31 @@ describe('MatAccordion', () => { const headers = fixture.componentInstance.headers.toArray(); headers.forEach(header => spyOn(header, 'focus')); - dispatchKeyboardEvent(headerElements[0].nativeElement, 'keydown', END); + const event = dispatchKeyboardEvent(headerElements[0].nativeElement, 'keydown', END); fixture.detectChanges(); expect(headers[headers.length - 1].focus).toHaveBeenCalledTimes(1); + expect(event.defaultPrevented).toBe(true); + }); + + it('should not handle the end key when it is pressed with a modifier', () => { + const fixture = TestBed.createComponent(SetOfItems); + fixture.detectChanges(); + + const headerElements = fixture.debugElement.queryAll(By.css('mat-expansion-panel-header')); + const headers = fixture.componentInstance.headers.toArray(); + + headers.forEach(header => spyOn(header, 'focus')); + + const eventTarget = headerElements[0].nativeElement; + const event = createKeyboardEvent('keydown', END, eventTarget); + Object.defineProperty(event, 'altKey', {get: () => true}); + + dispatchEvent(eventTarget, event); + fixture.detectChanges(); + + expect(headers[headers.length - 1].focus).not.toHaveBeenCalled(); + expect(event.defaultPrevented).toBe(false); }); }); diff --git a/src/material/expansion/accordion.ts b/src/material/expansion/accordion.ts index 8db83ca5b972..0a9cb3c1d1f7 100644 --- a/src/material/expansion/accordion.ts +++ b/src/material/expansion/accordion.ts @@ -10,7 +10,7 @@ import {Directive, Input, ContentChildren, QueryList, AfterContentInit} from '@a import {coerceBooleanProperty} from '@angular/cdk/coercion'; import {CdkAccordion} from '@angular/cdk/accordion'; import {FocusKeyManager} from '@angular/cdk/a11y'; -import {HOME, END} from '@angular/cdk/keycodes'; +import {HOME, END, hasModifierKey} from '@angular/cdk/keycodes'; import {MAT_ACCORDION, MatAccordionBase, MatAccordionDisplayMode} from './accordion-base'; import {MatExpansionPanelHeader} from './expansion-panel-header'; @@ -61,11 +61,15 @@ export class MatAccordion extends CdkAccordion implements MatAccordionBase, Afte const manager = this._keyManager; if (keyCode === HOME) { - manager.setFirstItemActive(); - event.preventDefault(); + if (!hasModifierKey(event)) { + manager.setFirstItemActive(); + event.preventDefault(); + } } else if (keyCode === END) { - manager.setLastItemActive(); - event.preventDefault(); + if (!hasModifierKey(event)) { + manager.setLastItemActive(); + event.preventDefault(); + } } else { this._keyManager.onKeydown(event); }