diff --git a/src/material-experimental/mdc-tooltip/testing/tooltip-harness.ts b/src/material-experimental/mdc-tooltip/testing/tooltip-harness.ts index 62596cba1967..2761b4ffdf5a 100644 --- a/src/material-experimental/mdc-tooltip/testing/tooltip-harness.ts +++ b/src/material-experimental/mdc-tooltip/testing/tooltip-harness.ts @@ -14,6 +14,9 @@ export class MatTooltipHarness extends _MatTooltipHarnessBase { protected _optionalPanel = this.documentRootLocatorFactory().locatorForOptional('.mat-mdc-tooltip'); static hostSelector = '.mat-mdc-tooltip-trigger'; + protected _hiddenClass = 'mat-mdc-tooltip-hide'; + protected _showAnimationName = 'mat-mdc-tooltip-show'; + protected _hideAnimationName = 'mat-mdc-tooltip-hide'; /** * Gets a `HarnessPredicate` that can be used to search diff --git a/src/material-experimental/mdc-tooltip/tooltip.html b/src/material-experimental/mdc-tooltip/tooltip.html index 6eff2c6af2c9..28b374a88f8a 100644 --- a/src/material-experimental/mdc-tooltip/tooltip.html +++ b/src/material-experimental/mdc-tooltip/tooltip.html @@ -1,9 +1,8 @@
+ (animationend)="_handleAnimationEnd($event)" + [class.mdc-tooltip--multiline]="_isMultiline">
{{message}}
diff --git a/src/material-experimental/mdc-tooltip/tooltip.scss b/src/material-experimental/mdc-tooltip/tooltip.scss index 07c0a9af2dba..5901072294cb 100644 --- a/src/material-experimental/mdc-tooltip/tooltip.scss +++ b/src/material-experimental/mdc-tooltip/tooltip.scss @@ -6,6 +6,7 @@ .mat-mdc-tooltip { // We don't use MDC's positioning so this has to be relative. position: relative; + transform: scale(0); // Increases the area of the tooltip so the user's pointer can go from the trigger directly to it. &::before { @@ -18,8 +19,47 @@ z-index: -1; position: absolute; } + + &._mat-animation-noopable { + animation: none; + transform: scale(1); + } } .mat-mdc-tooltip-panel-non-interactive { pointer-events: none; } + +// TODO(crisbeto): we may be able to use MDC directly for these animations. + +@keyframes mat-mdc-tooltip-show { + 0% { + opacity: 0; + transform: scale(0.8); + } + + 100% { + opacity: 1; + transform: scale(1); + } +} + +@keyframes mat-mdc-tooltip-hide { + 0% { + opacity: 1; + transform: scale(1); + } + + 100% { + opacity: 0; + transform: scale(0.8); + } +} + +.mat-mdc-tooltip-show { + animation: mat-mdc-tooltip-show 150ms cubic-bezier(0, 0, 0.2, 1) forwards; +} + +.mat-mdc-tooltip-hide { + animation: mat-mdc-tooltip-hide 75ms cubic-bezier(0.4, 0, 1, 1) forwards; +} diff --git a/src/material-experimental/mdc-tooltip/tooltip.spec.ts b/src/material-experimental/mdc-tooltip/tooltip.spec.ts index 020a0f7674c0..a07f148c8e50 100644 --- a/src/material-experimental/mdc-tooltip/tooltip.spec.ts +++ b/src/material-experimental/mdc-tooltip/tooltip.spec.ts @@ -1,4 +1,3 @@ -import {AnimationEvent} from '@angular/animations'; import {FocusMonitor} from '@angular/cdk/a11y'; import {Direction, Directionality} from '@angular/cdk/bidi'; import {ESCAPE} from '@angular/cdk/keycodes'; @@ -26,14 +25,12 @@ import { ComponentFixture, fakeAsync, flush, - flushMicrotasks, inject, TestBed, tick, waitForAsync, } from '@angular/core/testing'; import {By} from '@angular/platform-browser'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {Subject} from 'rxjs'; import { MAT_TOOLTIP_DEFAULT_OPTIONS, @@ -43,6 +40,7 @@ import { TooltipPosition, TooltipTouchGestures, } from './index'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; const initialTooltipMessage = 'initial tooltip message'; @@ -55,7 +53,7 @@ describe('MDC-based MatTooltip', () => { beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ - imports: [MatTooltipModule, OverlayModule, NoopAnimationsModule], + imports: [MatTooltipModule, OverlayModule], declarations: [ BasicTooltipDemo, ScrollableTooltipDemo, @@ -111,19 +109,19 @@ describe('MDC-based MatTooltip', () => { fixture.detectChanges(); - // wait till animation has finished - tick(500); + // Wait until animation has finished + finishCurrentTooltipAnimation(overlayContainerElement, true); - // Make sure tooltip is shown to the user and animation has finished + // Make sure tooltip is shown to the user and animation has finished. const tooltipElement = overlayContainerElement.querySelector( '.mat-mdc-tooltip', ) as HTMLElement; expect(tooltipElement instanceof HTMLElement).toBe(true); - expect(tooltipElement.style.transform).toBe('scale(1)'); + expect(tooltipElement.classList).toContain('mat-mdc-tooltip-show'); expect(overlayContainerElement.textContent).toContain(initialTooltipMessage); - // After hide called, a timeout delay is created that will to hide the tooltip. + // After hide is called, a timeout delay is created that will to hide the tooltip. const tooltipDelay = 1000; tooltipDirective.hide(tooltipDelay); expect(tooltipDirective._isTooltipVisible()).toBe(true); @@ -134,7 +132,7 @@ describe('MDC-based MatTooltip', () => { expect(tooltipDirective._isTooltipVisible()).toBe(false); // On animation complete, should expect that the tooltip has been detached. - flushMicrotasks(); + finishCurrentTooltipAnimation(overlayContainerElement, false); assertTooltipInstance(tooltipDirective, false); })); @@ -143,17 +141,17 @@ describe('MDC-based MatTooltip', () => { tick(0); expect(tooltipDirective._isTooltipVisible()).toBe(true); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); tooltipDirective._overlayRef!.detach(); tick(0); fixture.detectChanges(); expect(tooltipDirective._isTooltipVisible()).toBe(false); - flushMicrotasks(); assertTooltipInstance(tooltipDirective, false); tooltipDirective.show(); tick(0); + finishCurrentTooltipAnimation(overlayContainerElement, true); expect(tooltipDirective._isTooltipVisible()).toBe(true); })); @@ -175,7 +173,7 @@ describe('MDC-based MatTooltip', () => { it('should be able to override the default show and hide delays', fakeAsync(() => { TestBed.resetTestingModule() .configureTestingModule({ - imports: [MatTooltipModule, OverlayModule, NoopAnimationsModule], + imports: [MatTooltipModule, OverlayModule], declarations: [BasicTooltipDemo], providers: [ { @@ -212,7 +210,7 @@ describe('MDC-based MatTooltip', () => { it('should be able to override the default position', fakeAsync(() => { TestBed.resetTestingModule() .configureTestingModule({ - imports: [MatTooltipModule, OverlayModule, NoopAnimationsModule], + imports: [MatTooltipModule, OverlayModule], declarations: [TooltipDemoWithoutPositionBinding], providers: [ { @@ -421,16 +419,7 @@ describe('MDC-based MatTooltip', () => { tooltipDirective.hide(0); fixture.detectChanges(); tick(); - - // At this point the animation should be able to complete itself and trigger the - // _animationDone function, but for unknown reasons in the test infrastructure, - // this does not occur. Manually call the hook so the animation subscriptions get invoked. - tooltipDirective._tooltipInstance!._animationDone({ - fromState: 'visible', - toState: 'hidden', - totalTime: 150, - phaseName: 'done', - } as AnimationEvent); + finishCurrentTooltipAnimation(overlayContainerElement, false); expect(() => { tooltipDirective.position = 'right'; @@ -444,7 +433,7 @@ describe('MDC-based MatTooltip', () => { tooltipDirective.show(); tick(0); // Tick for the show delay (default is 0) - expect(tooltipDirective._tooltipInstance!._visibility).toBe('visible'); + expect(tooltipDirective._tooltipInstance!.isVisible()).toBe(true); fixture.detectChanges(); expect(overlayContainerElement.textContent).toContain(initialTooltipMessage); @@ -530,33 +519,21 @@ describe('MDC-based MatTooltip', () => { it('should not try to dispose the tooltip when destroyed and done hiding', fakeAsync(() => { tooltipDirective.show(); fixture.detectChanges(); - tick(150); + finishCurrentTooltipAnimation(overlayContainerElement, true); const tooltipDelay = 1000; tooltipDirective.hide(); tick(tooltipDelay); // Change the tooltip state to hidden and trigger animation start + finishCurrentTooltipAnimation(overlayContainerElement, false); - // Store the tooltip instance, which will be set to null after the button is hidden. - const tooltipInstance = tooltipDirective._tooltipInstance!; fixture.componentInstance.showButton = false; fixture.detectChanges(); - - // At this point the animation should be able to complete itself and trigger the - // _animationDone function, but for unknown reasons in the test infrastructure, - // this does not occur. Manually call this and verify that doing so does not - // throw an error. - tooltipInstance._animationDone({ - fromState: 'visible', - toState: 'hidden', - totalTime: 150, - phaseName: 'done', - } as AnimationEvent); })); it('should complete the afterHidden stream when tooltip is destroyed', fakeAsync(() => { tooltipDirective.show(); fixture.detectChanges(); - tick(150); + finishCurrentTooltipAnimation(overlayContainerElement, true); const spy = jasmine.createSpy('complete spy'); const subscription = tooltipDirective @@ -566,7 +543,7 @@ describe('MDC-based MatTooltip', () => { tooltipDirective.hide(0); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, false); expect(spy).toHaveBeenCalled(); subscription.unsubscribe(); @@ -642,7 +619,7 @@ describe('MDC-based MatTooltip', () => { tooltipDirective.show(); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); let tooltipWrapper = overlayContainerElement.querySelector( '.cdk-overlay-connected-position-bounding-box', @@ -654,13 +631,13 @@ describe('MDC-based MatTooltip', () => { tooltipDirective.hide(0); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, false); dir.value = 'ltr'; tooltipDirective.show(); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); tooltipWrapper = overlayContainerElement.querySelector( '.cdk-overlay-connected-position-bounding-box', @@ -681,7 +658,7 @@ describe('MDC-based MatTooltip', () => { tooltipDirective.show(); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); expect(tooltipDirective._isTooltipVisible()).toBe(true); expect(overlayContainerElement.textContent).toContain(initialTooltipMessage); @@ -689,7 +666,7 @@ describe('MDC-based MatTooltip', () => { document.body.click(); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, false); fixture.detectChanges(); expect(tooltipDirective._isTooltipVisible()).toBe(false); @@ -700,7 +677,7 @@ describe('MDC-based MatTooltip', () => { tooltipDirective.show(); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); expect(tooltipDirective._isTooltipVisible()).toBe(true); expect(overlayContainerElement.textContent).toContain(initialTooltipMessage); @@ -708,7 +685,7 @@ describe('MDC-based MatTooltip', () => { dispatchFakeEvent(document.body, 'auxclick'); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, false); fixture.detectChanges(); expect(tooltipDirective._isTooltipVisible()).toBe(false); @@ -723,6 +700,7 @@ describe('MDC-based MatTooltip', () => { document.body.click(); fixture.detectChanges(); tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); expect(overlayContainerElement.textContent).toContain(initialTooltipMessage); })); @@ -741,6 +719,7 @@ describe('MDC-based MatTooltip', () => { fixture.detectChanges(); tick(500); fixture.detectChanges(); + finishCurrentTooltipAnimation(overlayContainerElement, false); expect(tooltipDirective._isTooltipVisible()).toBe(false); expect(overlayContainerElement.textContent).toBe(''); @@ -822,7 +801,7 @@ describe('MDC-based MatTooltip', () => { tick(0); expect(tooltipDirective._isTooltipVisible()).toBe(true); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); const overlayRef = tooltipDirective._overlayRef!; @@ -832,7 +811,7 @@ describe('MDC-based MatTooltip', () => { tick(0); expect(tooltipDirective._isTooltipVisible()).toBe(true); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); expect(overlayRef.detach).not.toHaveBeenCalled(); })); @@ -1176,14 +1155,14 @@ describe('MDC-based MatTooltip', () => { fixture.detectChanges(); // wait until animation has finished - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); // Make sure tooltip is shown to the user and animation has finished const tooltipElement = overlayContainerElement.querySelector( '.mat-mdc-tooltip', ) as HTMLElement; expect(tooltipElement instanceof HTMLElement).toBe(true); - expect(tooltipElement.style.transform).toBe('scale(1)'); + expect(tooltipElement.classList).toContain('mat-mdc-tooltip-show'); // After hide called, a timeout delay is created that will to hide the tooltip. const tooltipDelay = 1000; @@ -1196,7 +1175,7 @@ describe('MDC-based MatTooltip', () => { expect(tooltipDirective._isTooltipVisible()).toBe(false); // On animation complete, should expect that the tooltip has been detached. - flushMicrotasks(); + finishCurrentTooltipAnimation(overlayContainerElement, false); assertTooltipInstance(tooltipDirective, false); })); @@ -1233,9 +1212,9 @@ describe('MDC-based MatTooltip', () => { assertTooltipInstance(fixture.componentInstance.tooltip, false); - tick(250); // Finish the delay. + tick(500); // Finish the delay. fixture.detectChanges(); - tick(500); // Finish the animation. + finishCurrentTooltipAnimation(overlayContainerElement, true); // Finish the animation. assertTooltipInstance(fixture.componentInstance.tooltip, true); })); @@ -1274,7 +1253,7 @@ describe('MDC-based MatTooltip', () => { fixture.detectChanges(); tick(500); // Finish the open delay. fixture.detectChanges(); - tick(500); // Finish the animation. + finishCurrentTooltipAnimation(overlayContainerElement, true); // Finish the animation. assertTooltipInstance(fixture.componentInstance.tooltip, true); dispatchFakeEvent(button, 'touchend'); @@ -1284,7 +1263,7 @@ describe('MDC-based MatTooltip', () => { tick(500); // Finish the delay. fixture.detectChanges(); - tick(500); // Finish the exit animation. + finishCurrentTooltipAnimation(overlayContainerElement, false); // Finish the exit animation. assertTooltipInstance(fixture.componentInstance.tooltip, false); })); @@ -1298,7 +1277,7 @@ describe('MDC-based MatTooltip', () => { fixture.detectChanges(); tick(500); // Finish the open delay. fixture.detectChanges(); - tick(500); // Finish the animation. + finishCurrentTooltipAnimation(overlayContainerElement, true); // Finish the animation. assertTooltipInstance(fixture.componentInstance.tooltip, true); dispatchFakeEvent(button, 'touchcancel'); @@ -1308,7 +1287,7 @@ describe('MDC-based MatTooltip', () => { tick(500); // Finish the delay. fixture.detectChanges(); - tick(500); // Finish the exit animation. + finishCurrentTooltipAnimation(overlayContainerElement, false); // Finish the exit animation. assertTooltipInstance(fixture.componentInstance.tooltip, false); })); @@ -1431,7 +1410,7 @@ describe('MDC-based MatTooltip', () => { fixture.detectChanges(); tick(500); // Finish the open delay. fixture.detectChanges(); - tick(500); // Finish the animation. + finishCurrentTooltipAnimation(overlayContainerElement, true); assertTooltipInstance(fixture.componentInstance.tooltip, true); // Simulate the pointer at the bottom/right of the page. @@ -1445,7 +1424,7 @@ describe('MDC-based MatTooltip', () => { fixture.detectChanges(); tick(1500); // Finish the delay. fixture.detectChanges(); - tick(500); // Finish the exit animation. + finishCurrentTooltipAnimation(overlayContainerElement, false); assertTooltipInstance(fixture.componentInstance.tooltip, false); })); @@ -1464,7 +1443,7 @@ describe('MDC-based MatTooltip', () => { fixture.detectChanges(); tick(500); // Finish the open delay. fixture.detectChanges(); - tick(500); // Finish the animation. + finishCurrentTooltipAnimation(overlayContainerElement, true); assertTooltipInstance(fixture.componentInstance.tooltip, true); // Simulate the pointer over the trigger. @@ -1479,7 +1458,7 @@ describe('MDC-based MatTooltip', () => { fixture.detectChanges(); tick(1500); // Finish the delay. fixture.detectChanges(); - tick(500); // Finish the exit animation. + finishCurrentTooltipAnimation(overlayContainerElement, false); assertTooltipInstance(fixture.componentInstance.tooltip, true); })); @@ -1621,3 +1600,12 @@ function assertTooltipInstance(tooltip: MatTooltip, shouldExist: boolean): void // happens due to the `_tooltipInstance` having a circular structure. expect(!!tooltip._tooltipInstance).toBe(shouldExist); } + +function finishCurrentTooltipAnimation(overlayContainer: HTMLElement, isVisible: boolean) { + const tooltip = overlayContainer.querySelector('.mat-mdc-tooltip')!; + const event = createFakeEvent('animationend'); + Object.defineProperty(event, 'animationName', { + get: () => `mat-mdc-tooltip-${isVisible ? 'show' : 'hide'}`, + }); + dispatchEvent(tooltip, event); +} diff --git a/src/material-experimental/mdc-tooltip/tooltip.ts b/src/material-experimental/mdc-tooltip/tooltip.ts index cc0a49ba4d42..c08addbf7f2b 100644 --- a/src/material-experimental/mdc-tooltip/tooltip.ts +++ b/src/material-experimental/mdc-tooltip/tooltip.ts @@ -15,11 +15,13 @@ import { Inject, NgZone, Optional, + ViewChild, ViewContainerRef, ViewEncapsulation, } from '@angular/core'; import {DOCUMENT} from '@angular/common'; import {Platform} from '@angular/cdk/platform'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; import {AriaDescriber, FocusMonitor} from '@angular/cdk/a11y'; import {Directionality} from '@angular/cdk/bidi'; import {ConnectedPosition, Overlay, ScrollDispatcher} from '@angular/cdk/overlay'; @@ -31,7 +33,6 @@ import { _TooltipComponentBase, } from '@angular/material/tooltip'; import {numbers} from '@material/tooltip'; -import {matTooltipAnimations} from './tooltip-animations'; /** * CSS class that will be attached to the overlay panel. @@ -116,11 +117,10 @@ export class MatTooltip extends _MatTooltipBase { styleUrls: ['tooltip.css'], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, - animations: [matTooltipAnimations.tooltipState], host: { // Forces the element to have a layout in IE and Edge. This fixes issues where the element // won't be rendered if the animations are disabled or there is no web animations polyfill. - '[style.zoom]': '_visibility === "visible" ? 1 : null', + '[style.zoom]': 'isVisible() ? 1 : null', '(mouseleave)': '_handleMouseLeave($event)', 'aria-hidden': 'true', }, @@ -129,12 +129,27 @@ export class TooltipComponent extends _TooltipComponentBase { /* Whether the tooltip text overflows to multiple lines */ _isMultiline = false; - constructor(changeDetectorRef: ChangeDetectorRef, private _elementRef: ElementRef) { - super(changeDetectorRef); + /** Reference to the internal tooltip element. */ + @ViewChild('tooltip', { + // Use a static query here since we interact directly with + // the DOM which can happen before `ngAfterViewInit`. + static: true, + }) + _tooltip: ElementRef; + _showAnimation = 'mat-mdc-tooltip-show'; + _hideAnimation = 'mat-mdc-tooltip-hide'; + + constructor( + changeDetectorRef: ChangeDetectorRef, + private _elementRef: ElementRef, + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string, + ) { + super(changeDetectorRef, animationMode); } protected override _onShow(): void { this._isMultiline = this._isTooltipMultiline(); + this._markForCheck(); } /** Whether the tooltip text has overflown to the next line */ diff --git a/src/material/tooltip/BUILD.bazel b/src/material/tooltip/BUILD.bazel index deb3e9d9fe9b..656cf3342018 100644 --- a/src/material/tooltip/BUILD.bazel +++ b/src/material/tooltip/BUILD.bazel @@ -29,7 +29,6 @@ ng_module( "//src/cdk/portal", "//src/cdk/scrolling", "//src/material/core", - "@npm//@angular/animations", "@npm//@angular/common", "@npm//@angular/core", "@npm//rxjs", @@ -65,7 +64,6 @@ ng_test_library( "//src/cdk/overlay", "//src/cdk/platform", "//src/cdk/testing/private", - "@npm//@angular/animations", "@npm//@angular/platform-browser", "@npm//rxjs", ], diff --git a/src/material/tooltip/testing/tooltip-harness.ts b/src/material/tooltip/testing/tooltip-harness.ts index 6940760d8752..bd1ce13c833f 100644 --- a/src/material/tooltip/testing/tooltip-harness.ts +++ b/src/material/tooltip/testing/tooltip-harness.ts @@ -16,6 +16,9 @@ import {TooltipHarnessFilters} from './tooltip-harness-filters'; export abstract class _MatTooltipHarnessBase extends ComponentHarness { protected abstract _optionalPanel: AsyncFactoryFn; + protected abstract _hiddenClass: string; + protected abstract _showAnimationName: string; + protected abstract _hideAnimationName: string; /** Shows the tooltip. */ async show(): Promise { @@ -24,9 +27,10 @@ export abstract class _MatTooltipHarnessBase extends ComponentHarness { // We need to dispatch both `touchstart` and a hover event, because the tooltip binds // different events depending on the device. The `changedTouches` is there in case the // element has ripples. - // @breaking-change 12.0.0 Remove null assertion from `dispatchEvent`. - await host.dispatchEvent?.('touchstart', {changedTouches: []}); + await host.dispatchEvent('touchstart', {changedTouches: []}); await host.hover(); + const panel = await this._optionalPanel(); + await panel?.dispatchEvent('animationend', {animationName: this._showAnimationName}); } /** Hides the tooltip. */ @@ -35,15 +39,16 @@ export abstract class _MatTooltipHarnessBase extends ComponentHarness { // We need to dispatch both `touchstart` and a hover event, because // the tooltip binds different events depending on the device. - // @breaking-change 12.0.0 Remove null assertion from `dispatchEvent`. - await host.dispatchEvent?.('touchend'); + await host.dispatchEvent('touchend'); await host.mouseAway(); - await this.forceStabilize(); // Needed in order to flush the `hide` animation. + const panel = await this._optionalPanel(); + await panel?.dispatchEvent('animationend', {animationName: this._hideAnimationName}); } /** Gets whether the tooltip is open. */ async isOpen(): Promise { - return !!(await this._optionalPanel()); + const panel = await this._optionalPanel(); + return !!panel && !(await panel.hasClass(this._hiddenClass)); } /** Gets a promise for the tooltip panel's text. */ @@ -56,6 +61,9 @@ export abstract class _MatTooltipHarnessBase extends ComponentHarness { /** Harness for interacting with a standard mat-tooltip in tests. */ export class MatTooltipHarness extends _MatTooltipHarnessBase { protected _optionalPanel = this.documentRootLocatorFactory().locatorForOptional('.mat-tooltip'); + protected _hiddenClass = 'mat-tooltip-hide'; + protected _showAnimationName = 'mat-tooltip-show'; + protected _hideAnimationName = 'mat-tooltip-hide'; static hostSelector = '.mat-tooltip-trigger'; /** diff --git a/src/material/tooltip/tooltip.html b/src/material/tooltip/tooltip.html index feaaaf53352f..cf6bd74a0592 100644 --- a/src/material/tooltip/tooltip.html +++ b/src/material/tooltip/tooltip.html @@ -1,6 +1,5 @@ -
{{message}}
+ [class.mat-tooltip-handset]="(_isHandset | async)?.matches">{{message}} diff --git a/src/material/tooltip/tooltip.scss b/src/material/tooltip/tooltip.scss index 1d4c9eeb4b64..1fadd1358a97 100644 --- a/src/material/tooltip/tooltip.scss +++ b/src/material/tooltip/tooltip.scss @@ -16,6 +16,12 @@ $handset-margin: 24px; padding-right: $horizontal-padding; overflow: hidden; text-overflow: ellipsis; + transform: scale(0); + + &._mat-animation-noopable { + animation: none; + transform: scale(1); + } @include a11y.high-contrast(active, off) { outline: solid 1px; @@ -31,3 +37,40 @@ $handset-margin: 24px; .mat-tooltip-panel-non-interactive { pointer-events: none; } + +@keyframes mat-tooltip-show { + 0% { + opacity: 0; + transform: scale(0); + } + + 50% { + opacity: 0.5; + transform: scale(0.99); + } + + 100% { + opacity: 1; + transform: scale(1); + } +} + +@keyframes mat-tooltip-hide { + 0% { + opacity: 1; + transform: scale(1); + } + + 100% { + opacity: 0; + transform: scale(1); + } +} + +.mat-tooltip-show { + animation: mat-tooltip-show 200ms cubic-bezier(0, 0, 0.2, 1) forwards; +} + +.mat-tooltip-hide { + animation: mat-tooltip-hide 100ms cubic-bezier(0, 0, 0.2, 1) forwards; +} diff --git a/src/material/tooltip/tooltip.spec.ts b/src/material/tooltip/tooltip.spec.ts index 759f642475a3..ec96e22c6c16 100644 --- a/src/material/tooltip/tooltip.spec.ts +++ b/src/material/tooltip/tooltip.spec.ts @@ -1,4 +1,3 @@ -import {AnimationEvent} from '@angular/animations'; import {FocusMonitor} from '@angular/cdk/a11y'; import {Direction, Directionality} from '@angular/cdk/bidi'; import {ESCAPE} from '@angular/cdk/keycodes'; @@ -26,14 +25,12 @@ import { ComponentFixture, fakeAsync, flush, - flushMicrotasks, inject, TestBed, tick, waitForAsync, } from '@angular/core/testing'; import {By} from '@angular/platform-browser'; -import {NoopAnimationsModule} from '@angular/platform-browser/animations'; import {Subject} from 'rxjs'; import { MAT_TOOLTIP_DEFAULT_OPTIONS, @@ -43,6 +40,7 @@ import { TooltipPosition, TooltipTouchGestures, } from './index'; +import {NoopAnimationsModule} from '@angular/platform-browser/animations'; const initialTooltipMessage = 'initial tooltip message'; @@ -55,7 +53,7 @@ describe('MatTooltip', () => { beforeEach( waitForAsync(() => { TestBed.configureTestingModule({ - imports: [MatTooltipModule, OverlayModule, NoopAnimationsModule], + imports: [MatTooltipModule, OverlayModule], declarations: [ BasicTooltipDemo, ScrollableTooltipDemo, @@ -111,13 +109,13 @@ describe('MatTooltip', () => { fixture.detectChanges(); - // Wait until the animation has finished. - tick(500); + // Wait until animation has finished + finishCurrentTooltipAnimation(overlayContainerElement, true); // Make sure tooltip is shown to the user and animation has finished. const tooltipElement = overlayContainerElement.querySelector('.mat-tooltip') as HTMLElement; expect(tooltipElement instanceof HTMLElement).toBe(true); - expect(tooltipElement.style.transform).toBe('scale(1)'); + expect(tooltipElement.classList).toContain('mat-tooltip-show'); expect(overlayContainerElement.textContent).toContain(initialTooltipMessage); @@ -132,7 +130,7 @@ describe('MatTooltip', () => { expect(tooltipDirective._isTooltipVisible()).toBe(false); // On animation complete, should expect that the tooltip has been detached. - flushMicrotasks(); + finishCurrentTooltipAnimation(overlayContainerElement, false); assertTooltipInstance(tooltipDirective, false); })); @@ -141,17 +139,17 @@ describe('MatTooltip', () => { tick(0); expect(tooltipDirective._isTooltipVisible()).toBe(true); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); tooltipDirective._overlayRef!.detach(); tick(0); fixture.detectChanges(); expect(tooltipDirective._isTooltipVisible()).toBe(false); - flushMicrotasks(); assertTooltipInstance(tooltipDirective, false); tooltipDirective.show(); tick(0); + finishCurrentTooltipAnimation(overlayContainerElement, true); expect(tooltipDirective._isTooltipVisible()).toBe(true); })); @@ -173,7 +171,7 @@ describe('MatTooltip', () => { it('should be able to override the default show and hide delays', fakeAsync(() => { TestBed.resetTestingModule() .configureTestingModule({ - imports: [MatTooltipModule, OverlayModule, NoopAnimationsModule], + imports: [MatTooltipModule, OverlayModule], declarations: [BasicTooltipDemo], providers: [ { @@ -210,7 +208,7 @@ describe('MatTooltip', () => { it('should be able to override the default position', fakeAsync(() => { TestBed.resetTestingModule() .configureTestingModule({ - imports: [MatTooltipModule, OverlayModule, NoopAnimationsModule], + imports: [MatTooltipModule, OverlayModule], declarations: [TooltipDemoWithoutPositionBinding], providers: [ { @@ -419,16 +417,7 @@ describe('MatTooltip', () => { tooltipDirective.hide(0); fixture.detectChanges(); tick(); - - // At this point the animation should be able to complete itself and trigger the - // _animationDone function, but for unknown reasons in the test infrastructure, - // this does not occur. Manually call the hook so the animation subscriptions get invoked. - tooltipDirective._tooltipInstance!._animationDone({ - fromState: 'visible', - toState: 'hidden', - totalTime: 150, - phaseName: 'done', - } as AnimationEvent); + finishCurrentTooltipAnimation(overlayContainerElement, false); expect(() => { tooltipDirective.position = 'right'; @@ -442,7 +431,7 @@ describe('MatTooltip', () => { tooltipDirective.show(); tick(0); // Tick for the show delay (default is 0) - expect(tooltipDirective._tooltipInstance!._visibility).toBe('visible'); + expect(tooltipDirective._tooltipInstance!.isVisible()).toBe(true); fixture.detectChanges(); expect(overlayContainerElement.textContent).toContain(initialTooltipMessage); @@ -526,33 +515,21 @@ describe('MatTooltip', () => { it('should not try to dispose the tooltip when destroyed and done hiding', fakeAsync(() => { tooltipDirective.show(); fixture.detectChanges(); - tick(150); + finishCurrentTooltipAnimation(overlayContainerElement, true); const tooltipDelay = 1000; tooltipDirective.hide(); tick(tooltipDelay); // Change the tooltip state to hidden and trigger animation start + finishCurrentTooltipAnimation(overlayContainerElement, false); - // Store the tooltip instance, which will be set to null after the button is hidden. - const tooltipInstance = tooltipDirective._tooltipInstance!; fixture.componentInstance.showButton = false; fixture.detectChanges(); - - // At this point the animation should be able to complete itself and trigger the - // _animationDone function, but for unknown reasons in the test infrastructure, - // this does not occur. Manually call this and verify that doing so does not - // throw an error. - tooltipInstance._animationDone({ - fromState: 'visible', - toState: 'hidden', - totalTime: 150, - phaseName: 'done', - } as AnimationEvent); })); it('should complete the afterHidden stream when tooltip is destroyed', fakeAsync(() => { tooltipDirective.show(); fixture.detectChanges(); - tick(150); + finishCurrentTooltipAnimation(overlayContainerElement, true); const spy = jasmine.createSpy('complete spy'); const subscription = tooltipDirective @@ -562,7 +539,7 @@ describe('MatTooltip', () => { tooltipDirective.hide(0); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, false); expect(spy).toHaveBeenCalled(); subscription.unsubscribe(); @@ -638,7 +615,7 @@ describe('MatTooltip', () => { tooltipDirective.show(); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); let tooltipWrapper = overlayContainerElement.querySelector( '.cdk-overlay-connected-position-bounding-box', @@ -650,13 +627,13 @@ describe('MatTooltip', () => { tooltipDirective.hide(0); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, false); dir.value = 'ltr'; tooltipDirective.show(); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); tooltipWrapper = overlayContainerElement.querySelector( '.cdk-overlay-connected-position-bounding-box', @@ -677,7 +654,7 @@ describe('MatTooltip', () => { tooltipDirective.show(); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); expect(tooltipDirective._isTooltipVisible()).toBe(true); expect(overlayContainerElement.textContent).toContain(initialTooltipMessage); @@ -685,7 +662,7 @@ describe('MatTooltip', () => { document.body.click(); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, false); fixture.detectChanges(); expect(tooltipDirective._isTooltipVisible()).toBe(false); @@ -696,7 +673,7 @@ describe('MatTooltip', () => { tooltipDirective.show(); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); expect(tooltipDirective._isTooltipVisible()).toBe(true); expect(overlayContainerElement.textContent).toContain(initialTooltipMessage); @@ -704,7 +681,7 @@ describe('MatTooltip', () => { dispatchFakeEvent(document.body, 'auxclick'); tick(0); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, false); fixture.detectChanges(); expect(tooltipDirective._isTooltipVisible()).toBe(false); @@ -715,10 +692,10 @@ describe('MatTooltip', () => { tooltipDirective.show(); tick(0); fixture.detectChanges(); - document.body.click(); fixture.detectChanges(); tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); expect(overlayContainerElement.textContent).toContain(initialTooltipMessage); })); @@ -737,6 +714,7 @@ describe('MatTooltip', () => { fixture.detectChanges(); tick(500); fixture.detectChanges(); + finishCurrentTooltipAnimation(overlayContainerElement, false); expect(tooltipDirective._isTooltipVisible()).toBe(false); expect(overlayContainerElement.textContent).toBe(''); @@ -818,7 +796,7 @@ describe('MatTooltip', () => { tick(0); expect(tooltipDirective._isTooltipVisible()).toBe(true); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); const overlayRef = tooltipDirective._overlayRef!; @@ -828,7 +806,7 @@ describe('MatTooltip', () => { tick(0); expect(tooltipDirective._isTooltipVisible()).toBe(true); fixture.detectChanges(); - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); expect(overlayRef.detach).not.toHaveBeenCalled(); })); @@ -1147,12 +1125,12 @@ describe('MatTooltip', () => { fixture.detectChanges(); // wait until animation has finished - tick(500); + finishCurrentTooltipAnimation(overlayContainerElement, true); // Make sure tooltip is shown to the user and animation has finished const tooltipElement = overlayContainerElement.querySelector('.mat-tooltip') as HTMLElement; expect(tooltipElement instanceof HTMLElement).toBe(true); - expect(tooltipElement.style.transform).toBe('scale(1)'); + expect(tooltipElement.classList).toContain('mat-tooltip-show'); // After hide called, a timeout delay is created that will to hide the tooltip. const tooltipDelay = 1000; @@ -1165,7 +1143,7 @@ describe('MatTooltip', () => { expect(tooltipDirective._isTooltipVisible()).toBe(false); // On animation complete, should expect that the tooltip has been detached. - flushMicrotasks(); + finishCurrentTooltipAnimation(overlayContainerElement, false); assertTooltipInstance(tooltipDirective, false); })); @@ -1200,9 +1178,9 @@ describe('MatTooltip', () => { assertTooltipInstance(fixture.componentInstance.tooltip, false); - tick(250); // Finish the delay. + tick(500); // Finish the delay. fixture.detectChanges(); - tick(500); // Finish the animation. + finishCurrentTooltipAnimation(overlayContainerElement, true); // Finish the animation. assertTooltipInstance(fixture.componentInstance.tooltip, true); })); @@ -1241,7 +1219,7 @@ describe('MatTooltip', () => { fixture.detectChanges(); tick(500); // Finish the open delay. fixture.detectChanges(); - tick(500); // Finish the animation. + finishCurrentTooltipAnimation(overlayContainerElement, true); // Finish the animation. assertTooltipInstance(fixture.componentInstance.tooltip, true); dispatchFakeEvent(button, 'touchend'); @@ -1251,7 +1229,7 @@ describe('MatTooltip', () => { tick(500); // Finish the delay. fixture.detectChanges(); - tick(500); // Finish the exit animation. + finishCurrentTooltipAnimation(overlayContainerElement, false); // Finish the exit animation. assertTooltipInstance(fixture.componentInstance.tooltip, false); })); @@ -1265,7 +1243,7 @@ describe('MatTooltip', () => { fixture.detectChanges(); tick(500); // Finish the open delay. fixture.detectChanges(); - tick(500); // Finish the animation. + finishCurrentTooltipAnimation(overlayContainerElement, true); // Finish the animation. assertTooltipInstance(fixture.componentInstance.tooltip, true); dispatchFakeEvent(button, 'touchcancel'); @@ -1275,7 +1253,7 @@ describe('MatTooltip', () => { tick(500); // Finish the delay. fixture.detectChanges(); - tick(500); // Finish the exit animation. + finishCurrentTooltipAnimation(overlayContainerElement, false); // Finish the exit animation. assertTooltipInstance(fixture.componentInstance.tooltip, false); })); @@ -1398,7 +1376,7 @@ describe('MatTooltip', () => { fixture.detectChanges(); tick(500); // Finish the open delay. fixture.detectChanges(); - tick(500); // Finish the animation. + finishCurrentTooltipAnimation(overlayContainerElement, true); assertTooltipInstance(fixture.componentInstance.tooltip, true); // Simulate the pointer at the bottom/right of the page. @@ -1412,7 +1390,7 @@ describe('MatTooltip', () => { fixture.detectChanges(); tick(1500); // Finish the delay. fixture.detectChanges(); - tick(500); // Finish the exit animation. + finishCurrentTooltipAnimation(overlayContainerElement, false); assertTooltipInstance(fixture.componentInstance.tooltip, false); })); @@ -1431,7 +1409,7 @@ describe('MatTooltip', () => { fixture.detectChanges(); tick(500); // Finish the open delay. fixture.detectChanges(); - tick(500); // Finish the animation. + finishCurrentTooltipAnimation(overlayContainerElement, true); assertTooltipInstance(fixture.componentInstance.tooltip, true); // Simulate the pointer over the trigger. @@ -1446,7 +1424,7 @@ describe('MatTooltip', () => { fixture.detectChanges(); tick(1500); // Finish the delay. fixture.detectChanges(); - tick(500); // Finish the exit animation. + finishCurrentTooltipAnimation(overlayContainerElement, false); assertTooltipInstance(fixture.componentInstance.tooltip, true); })); @@ -1588,3 +1566,12 @@ function assertTooltipInstance(tooltip: MatTooltip, shouldExist: boolean): void // happens due to the `_tooltipInstance` having a circular structure. expect(!!tooltip._tooltipInstance).toBe(shouldExist); } + +function finishCurrentTooltipAnimation(overlayContainer: HTMLElement, isVisible: boolean) { + const tooltip = overlayContainer.querySelector('.mat-tooltip')!; + const event = createFakeEvent('animationend'); + Object.defineProperty(event, 'animationName', { + get: () => `mat-tooltip-${isVisible ? 'show' : 'hide'}`, + }); + dispatchEvent(tooltip, event); +} diff --git a/src/material/tooltip/tooltip.ts b/src/material/tooltip/tooltip.ts index 4299911474c7..77c9a665acb4 100644 --- a/src/material/tooltip/tooltip.ts +++ b/src/material/tooltip/tooltip.ts @@ -5,7 +5,6 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {AnimationEvent} from '@angular/animations'; import {AriaDescriber, FocusMonitor} from '@angular/cdk/a11y'; import {Directionality} from '@angular/cdk/bidi'; import { @@ -46,13 +45,13 @@ import { ViewContainerRef, ViewEncapsulation, AfterViewInit, + ViewChild, } from '@angular/core'; import {DOCUMENT} from '@angular/common'; +import {ANIMATION_MODULE_TYPE} from '@angular/platform-browser/animations'; import {Observable, Subject} from 'rxjs'; import {take, takeUntil} from 'rxjs/operators'; -import {matTooltipAnimations} from './tooltip-animations'; - /** Possible positions for a tooltip. */ export type TooltipPosition = 'left' | 'right' | 'above' | 'below' | 'before' | 'after'; @@ -860,13 +859,33 @@ export abstract class _TooltipComponentBase implements OnDestroy { /** Amount of milliseconds to delay the closing sequence. */ _mouseLeaveHideDelay: number; + /** Whether animations are currently disabled. */ + private _animationsDisabled: boolean; + + /** Reference to the internal tooltip element. */ + abstract _tooltip: ElementRef; + /** Whether interactions on the page should close the tooltip */ - private _closeOnInteraction: boolean = false; + private _closeOnInteraction = false; + + /** Whether the tooltip is currently visible. */ + private _isVisible = false; /** Subject for notifying that the tooltip has been hidden from the view */ private readonly _onHide: Subject = new Subject(); - constructor(private _changeDetectorRef: ChangeDetectorRef) {} + /** Name of the show animation and the class that toggles it. */ + protected abstract readonly _showAnimation: string; + + /** Name of the hide animation and the class that toggles it. */ + protected abstract readonly _hideAnimation: string; + + constructor( + private _changeDetectorRef: ChangeDetectorRef, + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string, + ) { + this._animationsDisabled = animationMode === 'NoopAnimations'; + } /** * Shows the tooltip with an animation originating from the provided origin @@ -876,16 +895,9 @@ export abstract class _TooltipComponentBase implements OnDestroy { // Cancel the delayed hide if it is scheduled clearTimeout(this._hideTimeoutId); - // Body interactions should cancel the tooltip if there is a delay in showing. - this._closeOnInteraction = true; this._showTimeoutId = setTimeout(() => { - this._visibility = 'visible'; + this._toggleVisibility(true); this._showTimeoutId = undefined; - this._onShow(); - - // Mark for check so if any parent component has set the - // ChangeDetectionStrategy to OnPush it will be checked anyways - this._markForCheck(); }, delay); } @@ -898,12 +910,8 @@ export abstract class _TooltipComponentBase implements OnDestroy { clearTimeout(this._showTimeoutId); this._hideTimeoutId = setTimeout(() => { - this._visibility = 'hidden'; + this._toggleVisibility(false); this._hideTimeoutId = undefined; - - // Mark for check so if any parent component has set the - // ChangeDetectionStrategy to OnPush it will be checked anyways - this._markForCheck(); }, delay); } @@ -914,7 +922,7 @@ export abstract class _TooltipComponentBase implements OnDestroy { /** Whether the tooltip is being displayed. */ isVisible(): boolean { - return this._visibility === 'visible'; + return this._isVisible; } ngOnDestroy() { @@ -924,22 +932,6 @@ export abstract class _TooltipComponentBase implements OnDestroy { this._triggerElement = null!; } - _animationStart() { - this._closeOnInteraction = false; - } - - _animationDone(event: AnimationEvent): void { - const toState = event.toState as TooltipVisibility; - - if (toState === 'hidden' && !this.isVisible()) { - this._onHide.next(); - } - - if (toState === 'visible' || toState === 'hidden') { - this._closeOnInteraction = true; - } - } - /** * Interactions on the HTML body should close the tooltip immediately as defined in the * material design spec. @@ -972,6 +964,58 @@ export abstract class _TooltipComponentBase implements OnDestroy { * in the mdc-tooltip, not here. */ protected _onShow(): void {} + + /** Event listener dispatched when an animation on the tooltip finishes. */ + _handleAnimationEnd({animationName}: AnimationEvent) { + if (animationName === this._showAnimation || animationName === this._hideAnimation) { + this._finalizeAnimation(animationName === this._showAnimation); + } + } + + /** Handles the cleanup after an animation has finished. */ + private _finalizeAnimation(toVisible: boolean) { + if (toVisible) { + this._closeOnInteraction = true; + } else if (!this.isVisible()) { + this._onHide.next(); + } + } + + /** Toggles the visibility of the tooltip element. */ + private _toggleVisibility(isVisible: boolean) { + // We set the classes directly here ourselves so that toggling the tooltip state + // isn't bound by change detection. This allows us to hide it even if the + // view ref has been detached from the CD tree. + const tooltip = this._tooltip.nativeElement; + const showClass = this._showAnimation; + const hideClass = this._hideAnimation; + tooltip.classList.remove(isVisible ? hideClass : showClass); + tooltip.classList.add(isVisible ? showClass : hideClass); + this._isVisible = isVisible; + + // It's common for internal apps to disable animations using `* { animation: none !important }` + // which can break the opening sequence. Try to detect such cases and work around them. + if (isVisible && !this._animationsDisabled && typeof getComputedStyle === 'function') { + const styles = getComputedStyle(tooltip); + + // Use `getPropertyValue` to avoid issues with property renaming. + if ( + styles.getPropertyValue('animation-duration') === '0s' || + styles.getPropertyValue('animation-name') === 'none' + ) { + this._animationsDisabled = true; + } + } + + if (isVisible) { + this._onShow(); + } + + if (this._animationsDisabled) { + tooltip.classList.add('_mat-animation-noopable'); + this._finalizeAnimation(isVisible); + } + } } /** @@ -984,11 +1028,10 @@ export abstract class _TooltipComponentBase implements OnDestroy { styleUrls: ['tooltip.css'], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, - animations: [matTooltipAnimations.tooltipState], host: { // Forces the element to have a layout in IE and Edge. This fixes issues where the element // won't be rendered if the animations are disabled or there is no web animations polyfill. - '[style.zoom]': '_visibility === "visible" ? 1 : null', + '[style.zoom]': 'isVisible() ? 1 : null', '(mouseleave)': '_handleMouseLeave($event)', 'aria-hidden': 'true', }, @@ -996,11 +1039,21 @@ export abstract class _TooltipComponentBase implements OnDestroy { export class TooltipComponent extends _TooltipComponentBase { /** Stream that emits whether the user has a handset-sized display. */ _isHandset: Observable = this._breakpointObserver.observe(Breakpoints.Handset); + _showAnimation = 'mat-tooltip-show'; + _hideAnimation = 'mat-tooltip-hide'; + + @ViewChild('tooltip', { + // Use a static query here since we interact directly with + // the DOM which can happen before `ngAfterViewInit`. + static: true, + }) + _tooltip: ElementRef; constructor( changeDetectorRef: ChangeDetectorRef, private _breakpointObserver: BreakpointObserver, + @Optional() @Inject(ANIMATION_MODULE_TYPE) animationMode?: string, ) { - super(changeDetectorRef); + super(changeDetectorRef, animationMode); } } diff --git a/tools/public_api_guard/material/tooltip-testing.md b/tools/public_api_guard/material/tooltip-testing.md index 805d4f1efa9b..f40f0eefdca8 100644 --- a/tools/public_api_guard/material/tooltip-testing.md +++ b/tools/public_api_guard/material/tooltip-testing.md @@ -12,21 +12,33 @@ import { TestElement } from '@angular/cdk/testing'; // @public export class MatTooltipHarness extends _MatTooltipHarnessBase { + // (undocumented) + protected _hiddenClass: string; + // (undocumented) + protected _hideAnimationName: string; // (undocumented) static hostSelector: string; // (undocumented) protected _optionalPanel: AsyncFactoryFn; + // (undocumented) + protected _showAnimationName: string; static with(options?: TooltipHarnessFilters): HarnessPredicate; } // @public (undocumented) export abstract class _MatTooltipHarnessBase extends ComponentHarness { getTooltipText(): Promise; + // (undocumented) + protected abstract _hiddenClass: string; hide(): Promise; + // (undocumented) + protected abstract _hideAnimationName: string; isOpen(): Promise; // (undocumented) protected abstract _optionalPanel: AsyncFactoryFn; show(): Promise; + // (undocumented) + protected abstract _showAnimationName: string; } // @public diff --git a/tools/public_api_guard/material/tooltip.md b/tools/public_api_guard/material/tooltip.md index 9b9dbcabe573..82bf91dddb88 100644 --- a/tools/public_api_guard/material/tooltip.md +++ b/tools/public_api_guard/material/tooltip.md @@ -5,7 +5,6 @@ ```ts import { AfterViewInit } from '@angular/core'; -import { AnimationEvent as AnimationEvent_2 } from '@angular/animations'; import { AnimationTriggerMetadata } from '@angular/animations'; import { AriaDescriber } from '@angular/cdk/a11y'; import { BooleanInput } from '@angular/cdk/coercion'; @@ -157,26 +156,30 @@ export const TOOLTIP_PANEL_CLASS = "mat-tooltip-panel"; // @public export class TooltipComponent extends _TooltipComponentBase { - constructor(changeDetectorRef: ChangeDetectorRef, _breakpointObserver: BreakpointObserver); + constructor(changeDetectorRef: ChangeDetectorRef, _breakpointObserver: BreakpointObserver, animationMode?: string); + // (undocumented) + _hideAnimation: string; _isHandset: Observable; // (undocumented) + _showAnimation: string; + // (undocumented) + _tooltip: ElementRef; + // (undocumented) static ɵcmp: i0.ɵɵComponentDeclaration; // (undocumented) - static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵfac: i0.ɵɵFactoryDeclaration; } // @public (undocumented) export abstract class _TooltipComponentBase implements OnDestroy { - constructor(_changeDetectorRef: ChangeDetectorRef); + constructor(_changeDetectorRef: ChangeDetectorRef, animationMode?: string); afterHidden(): Observable; - // (undocumented) - _animationDone(event: AnimationEvent_2): void; - // (undocumented) - _animationStart(): void; + _handleAnimationEnd({ animationName }: AnimationEvent): void; _handleBodyInteraction(): void; // (undocumented) _handleMouseLeave({ relatedTarget }: MouseEvent): void; hide(delay: number): void; + protected abstract readonly _hideAnimation: string; _hideTimeoutId: number | undefined; isVisible(): boolean; _markForCheck(): void; @@ -186,7 +189,9 @@ export abstract class _TooltipComponentBase implements OnDestroy { ngOnDestroy(): void; protected _onShow(): void; show(delay: number): void; + protected abstract readonly _showAnimation: string; _showTimeoutId: number | undefined; + abstract _tooltip: ElementRef; tooltipClass: string | string[] | Set | { [key: string]: any; }; @@ -195,7 +200,7 @@ export abstract class _TooltipComponentBase implements OnDestroy { // (undocumented) static ɵdir: i0.ɵɵDirectiveDeclaration<_TooltipComponentBase, never, never, {}, {}, never>; // (undocumented) - static ɵfac: i0.ɵɵFactoryDeclaration<_TooltipComponentBase, never>; + static ɵfac: i0.ɵɵFactoryDeclaration<_TooltipComponentBase, [null, { optional: true; }]>; } // @public