Skip to content

Commit

Permalink
fix(material/core): ripple loader not working in shadow DOM (#29015)
Browse files Browse the repository at this point in the history
Fixes the following issues that prevented the button ripples from being loaded in the shadow DOM:
* We weren't resolving the event target properly.
* We were using `click` as a fallback which happens too late for the very first ripple. These changes switch to `mousedown`.

Fixes #29011.

(cherry picked from commit 7553e1c)
  • Loading branch information
crisbeto committed May 9, 2024
1 parent 9bcc612 commit a5ad288
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 15 deletions.
2 changes: 1 addition & 1 deletion src/material/button/button.spec.ts
Expand Up @@ -333,7 +333,7 @@ describe('MDC-based MatButton', () => {
});

// Ensure each of these events triggers the initialization of the button ripple.
for (const event of ['click', 'touchstart', 'mouseenter', 'focus']) {
for (const event of ['mousedown', 'touchstart', 'mouseenter', 'focus']) {
it(`should render the ripple once a button has received a "${event}" event`, () => {
const fab = fixture.debugElement.query(By.css('button[mat-fab]'))!;
let ripple = fab.nativeElement.querySelector('.mat-mdc-button-ripple');
Expand Down
34 changes: 20 additions & 14 deletions src/material/core/private/ripple-loader.ts
Expand Up @@ -16,13 +16,17 @@ import {
inject,
} from '@angular/core';
import {MAT_RIPPLE_GLOBAL_OPTIONS, MatRipple} from '../ripple';
import {Platform} from '@angular/cdk/platform';
import {Platform, _getEventTarget} from '@angular/cdk/platform';

/** The options for the MatRippleLoader's event listeners. */
const eventListenerOptions = {capture: true};

/** The events that should trigger the initialization of the ripple. */
const rippleInteractionEvents = ['focus', 'click', 'mouseenter', 'touchstart'];
/**
* The events that should trigger the initialization of the ripple.
* Note that we use `mousedown`, rather than `click`, for mouse devices because
* we can't rely on `mouseenter` in the shadow DOM and `click` happens too late.
*/
const rippleInteractionEvents = ['focus', 'mousedown', 'mouseenter', 'touchstart'];

/** The attribute attached to a component whose ripple has not yet been initialized. */
const matRippleUninitialized = 'mat-ripple-loader-uninitialized';
Expand Down Expand Up @@ -130,20 +134,22 @@ export class MatRippleLoader implements OnDestroy {
}
}

/** Handles creating and attaching component internals when a component it is initially interacted with. */
/**
* Handles creating and attaching component internals
* when a component is initially interacted with.
*/
private _onInteraction = (event: Event) => {
if (!(event.target instanceof HTMLElement)) {
return;
}
const eventTarget = event.target as HTMLElement;
const eventTarget = _getEventTarget(event);

// TODO(wagnermaciel): Consider batching these events to improve runtime performance.
if (eventTarget instanceof HTMLElement) {
// TODO(wagnermaciel): Consider batching these events to improve runtime performance.
const element = eventTarget.closest(
`[${matRippleUninitialized}="${this._globalRippleOptions?.namespace ?? ''}"]`,
);

const element = eventTarget.closest(
`[${matRippleUninitialized}="${this._globalRippleOptions?.namespace ?? ''}"]`,
);
if (element) {
this._createRipple(element as HTMLElement);
if (element) {
this._createRipple(element as HTMLElement);
}
}
};

Expand Down

0 comments on commit a5ad288

Please sign in to comment.