Skip to content

Commit d57f85d

Browse files
committed
fix(ripple): don't launch ripple for fake mouse events
When using a screen reader the ripples can get fake mouse events when pressing space or enter. With the following changes we skip launching the ripples in these cases, in order to align the experience with the non-screen-reader.
1 parent 444fb38 commit d57f85d

File tree

3 files changed

+23
-2
lines changed

3 files changed

+23
-2
lines changed

src/cdk/testing/event-objects.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ export function createMouseEvent(type: string, x = 0, y = 0) {
2626
0, /* button */
2727
null /* relatedTarget */);
2828

29+
// `initMouseEvent` doesn't allow us to pass the `buttons` and
30+
// defaults it to 0 which looks like a fake event.
31+
Object.defineProperty(event, 'buttons', {get: () => 1});
32+
2933
return event;
3034
}
3135

src/lib/core/ripple/ripple-renderer.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,13 @@ export class RippleRenderer {
246246

247247
/** Function being called whenever the trigger is being pressed using mouse. */
248248
private onMousedown = (event: MouseEvent) => {
249+
// Screen readers will fire fake mouse events for space/enter. Skip launching a
250+
// ripple in this case for consistency with the non-screen-reader experience.
251+
const isFakeMousedown = event.buttons === 0;
249252
const isSyntheticEvent = this._lastTouchStartEvent &&
250253
Date.now() < this._lastTouchStartEvent + ignoreMouseEventsTimeout;
251254

252-
if (!this._target.rippleDisabled && !isSyntheticEvent) {
255+
if (!this._target.rippleDisabled && !isFakeMousedown && !isSyntheticEvent) {
253256
this._isPointerDown = true;
254257
this.fadeInRipple(event.clientX, event.clientY, this._target.rippleConfig);
255258
}

src/lib/core/ripple/ripple.spec.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
import {TestBed, ComponentFixture, fakeAsync, tick, inject} from '@angular/core/testing';
22
import {Component, ViewChild} from '@angular/core';
33
import {Platform} from '@angular/cdk/platform';
4-
import {dispatchMouseEvent, dispatchTouchEvent} from '@angular/cdk/testing';
4+
import {
5+
dispatchMouseEvent,
6+
dispatchTouchEvent,
7+
createMouseEvent,
8+
dispatchEvent,
9+
} from '@angular/cdk/testing';
510
import {defaultRippleAnimationConfig, RippleAnimationConfig} from './ripple-renderer';
611
import {
712
MatRipple, MatRippleModule, MAT_RIPPLE_GLOBAL_OPTIONS, RippleState, RippleGlobalOptions
@@ -135,6 +140,15 @@ describe('MatRipple', () => {
135140
expect(rippleTarget.querySelectorAll('.mat-ripple-element').length).toBe(0);
136141
}));
137142

143+
it('should ignore fake mouse events from screen readers', () => fakeAsync(() => {
144+
const event = createMouseEvent('mousedown');
145+
Object.defineProperty(event, 'buttons', {get: () => 0});
146+
147+
dispatchEvent(rippleTarget, event);
148+
tick(enterDuration);
149+
expect(rippleTarget.querySelector('.mat-ripple-element')).toBeFalsy();
150+
}));
151+
138152
it('removes ripple after timeout', fakeAsync(() => {
139153
dispatchMouseEvent(rippleTarget, 'mousedown');
140154
dispatchMouseEvent(rippleTarget, 'mouseup');

0 commit comments

Comments
 (0)