diff --git a/src/material-experimental/mdc-chips/chip-remove.spec.ts b/src/material-experimental/mdc-chips/chip-remove.spec.ts index 6ad850b681da..5e6a68b1fe71 100644 --- a/src/material-experimental/mdc-chips/chip-remove.spec.ts +++ b/src/material-experimental/mdc-chips/chip-remove.spec.ts @@ -1,7 +1,13 @@ -import {createFakeEvent} from '@angular/cdk/testing/private'; +import { + createFakeEvent, + dispatchKeyboardEvent, + createKeyboardEvent, + dispatchEvent, +} from '@angular/cdk/testing/private'; import {Component, DebugElement} from '@angular/core'; import {By} from '@angular/platform-browser'; import {async, ComponentFixture, TestBed} from '@angular/core/testing'; +import {SPACE, ENTER} from '@angular/cdk/keycodes'; import {MatChip, MatChipsModule} from './index'; describe('MDC-based Chip Remove', () => { @@ -86,6 +92,58 @@ describe('MDC-based Chip Remove', () => { expect(buttonElement.hasAttribute('aria-hidden')).toBe(false); }); + it('should prevent the default SPACE action', () => { + const buttonElement = chipNativeElement.querySelector('button')!; + + testChip.removable = true; + fixture.detectChanges(); + + const event = dispatchKeyboardEvent(buttonElement, 'keydown', SPACE); + fixture.detectChanges(); + + expect(event.defaultPrevented).toBe(true); + }); + + it('should not prevent the default SPACE action when a modifier key is pressed', () => { + const buttonElement = chipNativeElement.querySelector('button')!; + + testChip.removable = true; + fixture.detectChanges(); + + const event = createKeyboardEvent('keydown', SPACE); + Object.defineProperty(event, 'shiftKey', {get: () => true}); + dispatchEvent(buttonElement, event); + fixture.detectChanges(); + + expect(event.defaultPrevented).toBe(false); + }); + + it('should prevent the default ENTER action', () => { + const buttonElement = chipNativeElement.querySelector('button')!; + + testChip.removable = true; + fixture.detectChanges(); + + const event = dispatchKeyboardEvent(buttonElement, 'keydown', ENTER); + fixture.detectChanges(); + + expect(event.defaultPrevented).toBe(true); + }); + + it('should not prevent the default ENTER action when a modifier key is pressed', () => { + const buttonElement = chipNativeElement.querySelector('button')!; + + testChip.removable = true; + fixture.detectChanges(); + + const event = createKeyboardEvent('keydown', ENTER); + Object.defineProperty(event, 'shiftKey', {get: () => true}); + dispatchEvent(buttonElement, event); + fixture.detectChanges(); + + expect(event.defaultPrevented).toBe(false); + }); + }); }); diff --git a/src/material-experimental/mdc-chips/chip.ts b/src/material-experimental/mdc-chips/chip.ts index 2866120ad652..cd5e39dc24aa 100644 --- a/src/material-experimental/mdc-chips/chip.ts +++ b/src/material-experimental/mdc-chips/chip.ts @@ -46,6 +46,7 @@ import { } from '@angular/material/core'; import {MDCChipAdapter, MDCChipFoundation} from '@material/chips'; import {numbers} from '@material/ripple'; +import {SPACE, ENTER, hasModifierKey} from '@angular/cdk/keycodes'; import {Subject} from 'rxjs'; import {takeUntil} from 'rxjs/operators'; import {MatChipAvatar, MatChipTrailingIcon, MatChipRemove} from './chip-icons'; @@ -351,11 +352,23 @@ export class MatChip extends _MatChipMixinBase implements AfterContentInit, Afte // event, even ones it doesn't handle, so we want to avoid passing it keyboard events // for which we have a custom handler. Note that we assert the type of the event using // the `type`, because `instanceof KeyboardEvent` can throw during server-side rendering. - if (this.disabled || (event.type.startsWith('key') && - this.HANDLED_KEYS.indexOf((event as KeyboardEvent).keyCode) !== -1)) { + const isKeyboardEvent = event.type.startsWith('key'); + + if (this.disabled || (isKeyboardEvent && + this.HANDLED_KEYS.indexOf((event as KeyboardEvent).keyCode) !== -1)) { return; } + this._chipFoundation.handleTrailingIconInteraction(event); + + if (isKeyboardEvent && !hasModifierKey(event as KeyboardEvent)) { + const keyCode = (event as KeyboardEvent).keyCode; + + // Prevent default space and enter presses so we don't scroll the page or submit forms. + if (keyCode === SPACE || keyCode === ENTER) { + event.preventDefault(); + } + } }); }