Skip to content

Commit ba1f847

Browse files
committed
fix(material-experimental/mdc-chips): prevent default space and enter
We support pressing space or enter on the remove icon of an MDC-based chip, but we don't `preventDefault` which means that we can end up submitting a form or scrolling the page down.
1 parent 09dc459 commit ba1f847

File tree

2 files changed

+74
-3
lines changed

2 files changed

+74
-3
lines changed

src/material-experimental/mdc-chips/chip-remove.spec.ts

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
1-
import {createFakeEvent} from '@angular/cdk/testing/private';
1+
import {
2+
createFakeEvent,
3+
dispatchKeyboardEvent,
4+
createKeyboardEvent,
5+
dispatchEvent,
6+
} from '@angular/cdk/testing/private';
27
import {Component, DebugElement} from '@angular/core';
38
import {By} from '@angular/platform-browser';
49
import {async, ComponentFixture, TestBed} from '@angular/core/testing';
10+
import {SPACE, ENTER} from '@angular/cdk/keycodes';
511
import {MatChip, MatChipsModule} from './index';
612

713
describe('MDC-based Chip Remove', () => {
@@ -86,6 +92,58 @@ describe('MDC-based Chip Remove', () => {
8692
expect(buttonElement.hasAttribute('aria-hidden')).toBe(false);
8793
});
8894

95+
it('should prevent the default SPACE action', () => {
96+
const buttonElement = chipNativeElement.querySelector('button')!;
97+
98+
testChip.removable = true;
99+
fixture.detectChanges();
100+
101+
const event = dispatchKeyboardEvent(buttonElement, 'keydown', SPACE);
102+
fixture.detectChanges();
103+
104+
expect(event.defaultPrevented).toBe(true);
105+
});
106+
107+
it('should not prevent the default SPACE action when a modifier key is pressed', () => {
108+
const buttonElement = chipNativeElement.querySelector('button')!;
109+
110+
testChip.removable = true;
111+
fixture.detectChanges();
112+
113+
const event = createKeyboardEvent('keydown', SPACE);
114+
Object.defineProperty(event, 'shiftKey', {get: () => true});
115+
dispatchEvent(buttonElement, event);
116+
fixture.detectChanges();
117+
118+
expect(event.defaultPrevented).toBe(false);
119+
});
120+
121+
it('should prevent the default ENTER action', () => {
122+
const buttonElement = chipNativeElement.querySelector('button')!;
123+
124+
testChip.removable = true;
125+
fixture.detectChanges();
126+
127+
const event = dispatchKeyboardEvent(buttonElement, 'keydown', ENTER);
128+
fixture.detectChanges();
129+
130+
expect(event.defaultPrevented).toBe(true);
131+
});
132+
133+
it('should not prevent the default ENTER action when a modifier key is pressed', () => {
134+
const buttonElement = chipNativeElement.querySelector('button')!;
135+
136+
testChip.removable = true;
137+
fixture.detectChanges();
138+
139+
const event = createKeyboardEvent('keydown', ENTER);
140+
Object.defineProperty(event, 'shiftKey', {get: () => true});
141+
dispatchEvent(buttonElement, event);
142+
fixture.detectChanges();
143+
144+
expect(event.defaultPrevented).toBe(false);
145+
});
146+
89147
});
90148
});
91149

src/material-experimental/mdc-chips/chip.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import {
4646
} from '@angular/material/core';
4747
import {MDCChipAdapter, MDCChipFoundation} from '@material/chips';
4848
import {numbers} from '@material/ripple';
49+
import {SPACE, ENTER, hasModifierKey} from '@angular/cdk/keycodes';
4950
import {Subject} from 'rxjs';
5051
import {takeUntil} from 'rxjs/operators';
5152
import {MatChipAvatar, MatChipTrailingIcon, MatChipRemove} from './chip-icons';
@@ -351,11 +352,23 @@ export class MatChip extends _MatChipMixinBase implements AfterContentInit, Afte
351352
// event, even ones it doesn't handle, so we want to avoid passing it keyboard events
352353
// for which we have a custom handler. Note that we assert the type of the event using
353354
// the `type`, because `instanceof KeyboardEvent` can throw during server-side rendering.
354-
if (this.disabled || (event.type.startsWith('key') &&
355-
this.HANDLED_KEYS.indexOf((event as KeyboardEvent).keyCode) !== -1)) {
355+
const isKeyboardEvent = event.type.startsWith('key');
356+
357+
if (this.disabled || (isKeyboardEvent &&
358+
this.HANDLED_KEYS.indexOf((event as KeyboardEvent).keyCode) !== -1)) {
356359
return;
357360
}
361+
358362
this._chipFoundation.handleTrailingIconInteraction(event);
363+
364+
if (isKeyboardEvent && !hasModifierKey(event as KeyboardEvent)) {
365+
const keyCode = (event as KeyboardEvent).keyCode;
366+
367+
// Prevent default space and enter presses so we don't scroll the page or submit forms.
368+
if (keyCode === SPACE || keyCode === ENTER) {
369+
event.preventDefault();
370+
}
371+
}
359372
});
360373
}
361374

0 commit comments

Comments
 (0)