Skip to content

Commit 5008db0

Browse files
committed
fix(material/tooltip): match panel to trigger in test harness
The tooltip harness was written the assumption that only tooltip can be shown on the page at a time, however in tests that might not be the case. These changes add a couple of attributes so a trigger can be matched to its specific panel. Fixes #26773.
1 parent 1eb83e5 commit 5008db0

File tree

7 files changed

+26
-19
lines changed

7 files changed

+26
-19
lines changed

src/material/legacy-tooltip/testing/tooltip-harness.ts

-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import {_MatTooltipHarnessBase, TooltipHarnessFilters} from '@angular/material/t
1515
* @breaking-change 17.0.0
1616
*/
1717
export class MatLegacyTooltipHarness extends _MatTooltipHarnessBase {
18-
protected _optionalPanel = this.documentRootLocatorFactory().locatorForOptional('.mat-tooltip');
1918
protected _hiddenClass = 'mat-tooltip-hide';
2019
protected _showAnimationName = 'mat-tooltip-show';
2120
protected _hideAnimationName = 'mat-tooltip-hide';

src/material/legacy-tooltip/tooltip.ts

+3
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ import {
4949
exportAs: 'matTooltip',
5050
host: {
5151
'class': 'mat-tooltip-trigger',
52+
// Used by harnesses to match the trigger to its tooltip.
53+
'[attr.data-mat-tooltip]': '_panelId',
5254
},
5355
})
5456
export class MatLegacyTooltip extends _MatTooltipBase<LegacyTooltipComponent> {
@@ -102,6 +104,7 @@ export class MatLegacyTooltip extends _MatTooltipBase<LegacyTooltipComponent> {
102104
// won't be rendered if the animations are disabled or there is no web animations polyfill.
103105
'[style.zoom]': 'isVisible() ? 1 : null',
104106
'(mouseleave)': '_handleMouseLeave($event)',
107+
'[attr.id]': '_id',
105108
'aria-hidden': 'true',
106109
},
107110
})

src/material/tooltip/testing/tooltip-harness.ts

+11-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
*/
88

99
import {
10-
AsyncFactoryFn,
1110
ComponentHarness,
1211
ComponentHarnessConstructor,
1312
HarnessPredicate,
@@ -16,7 +15,6 @@ import {
1615
import {TooltipHarnessFilters} from './tooltip-harness-filters';
1716

1817
export abstract class _MatTooltipHarnessBase extends ComponentHarness {
19-
protected abstract _optionalPanel: AsyncFactoryFn<TestElement | null>;
2018
protected abstract _hiddenClass: string;
2119
protected abstract _showAnimationName: string;
2220
protected abstract _hideAnimationName: string;
@@ -30,7 +28,7 @@ export abstract class _MatTooltipHarnessBase extends ComponentHarness {
3028
// element has ripples.
3129
await host.dispatchEvent('touchstart', {changedTouches: []});
3230
await host.hover();
33-
const panel = await this._optionalPanel();
31+
const panel = await this._getPanel();
3432
await panel?.dispatchEvent('animationend', {animationName: this._showAnimationName});
3533
}
3634

@@ -42,27 +40,32 @@ export abstract class _MatTooltipHarnessBase extends ComponentHarness {
4240
// the tooltip binds different events depending on the device.
4341
await host.dispatchEvent('touchend');
4442
await host.mouseAway();
45-
const panel = await this._optionalPanel();
43+
const panel = await this._getPanel();
4644
await panel?.dispatchEvent('animationend', {animationName: this._hideAnimationName});
4745
}
4846

4947
/** Gets whether the tooltip is open. */
5048
async isOpen(): Promise<boolean> {
51-
const panel = await this._optionalPanel();
49+
const panel = await this._getPanel();
5250
return !!panel && !(await panel.hasClass(this._hiddenClass));
5351
}
5452

5553
/** Gets a promise for the tooltip panel's text. */
5654
async getTooltipText(): Promise<string> {
57-
const panel = await this._optionalPanel();
55+
const panel = await this._getPanel();
5856
return panel ? panel.text() : '';
5957
}
58+
59+
/** Gets the tooltip panel associated with the trigger. */
60+
private async _getPanel(): Promise<TestElement | null> {
61+
const host = await this.host();
62+
const locatorFactory = this.documentRootLocatorFactory();
63+
return locatorFactory.locatorForOptional(`#${await host.getAttribute('data-mat-tooltip')}`)();
64+
}
6065
}
6166

6267
/** Harness for interacting with a standard mat-tooltip in tests. */
6368
export class MatTooltipHarness extends _MatTooltipHarnessBase {
64-
protected _optionalPanel =
65-
this.documentRootLocatorFactory().locatorForOptional('.mat-mdc-tooltip');
6669
static hostSelector = '.mat-mdc-tooltip-trigger';
6770
protected _hiddenClass = 'mat-mdc-tooltip-hide';
6871
protected _showAnimationName = 'mat-mdc-tooltip-show';

src/material/tooltip/tooltip.ts

+9
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,7 @@ const MIN_VIEWPORT_TOOLTIP_THRESHOLD = 8;
161161
const UNBOUNDED_ANCHOR_GAP = 8;
162162
const MIN_HEIGHT = 24;
163163
const MAX_WIDTH = 200;
164+
let uniqueId = 0;
164165

165166
@Directive()
166167
export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
@@ -181,6 +182,7 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
181182
protected _viewportMargin = 8;
182183
private _currentPosition: TooltipPosition;
183184
protected readonly _cssClassPrefix: string = 'mat';
185+
readonly _panelId = `mat-tooltip-panel-${uniqueId++}`;
184186

185187
/** Allows the user to define the position of the tooltip relative to the parent element */
186188
@Input('matTooltipPosition')
@@ -426,6 +428,7 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
426428
const instance = (this._tooltipInstance = overlayRef.attach(this._portal).instance);
427429
instance._triggerElement = this._elementRef.nativeElement;
428430
instance._mouseLeaveHideDelay = this._hideDelay;
431+
instance._id = this._panelId;
429432
instance
430433
.afterHidden()
431434
.pipe(takeUntil(this._destroyed))
@@ -852,6 +855,8 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase>
852855
exportAs: 'matTooltip',
853856
host: {
854857
'class': 'mat-mdc-tooltip-trigger',
858+
// Used by harnesses to match the trigger to its tooltip.
859+
'[attr.data-mat-tooltip]': '_panelId',
855860
},
856861
})
857862
export class MatTooltip extends _MatTooltipBase<TooltipComponent> {
@@ -927,6 +932,9 @@ export abstract class _TooltipComponentBase implements OnDestroy {
927932
/** Amount of milliseconds to delay the closing sequence. */
928933
_mouseLeaveHideDelay: number;
929934

935+
/** Unique ID for the panel. Assigned by the trigger. */
936+
_id: string | undefined;
937+
930938
/** Whether animations are currently disabled. */
931939
private _animationsDisabled: boolean;
932940

@@ -1111,6 +1119,7 @@ export abstract class _TooltipComponentBase implements OnDestroy {
11111119
// won't be rendered if the animations are disabled or there is no web animations polyfill.
11121120
'[style.zoom]': 'isVisible() ? 1 : null',
11131121
'(mouseleave)': '_handleMouseLeave($event)',
1122+
'[attr.id]': '_id',
11141123
'aria-hidden': 'true',
11151124
},
11161125
})

tools/public_api_guard/material/legacy-tooltip-testing.md

-4
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,9 @@
44
55
```ts
66

7-
import { AsyncFactoryFn } from '@angular/cdk/testing';
87
import { HarnessPredicate } from '@angular/cdk/testing';
98
import { TooltipHarnessFilters as LegacyTooltipHarnessFilters } from '@angular/material/tooltip/testing';
109
import { _MatTooltipHarnessBase } from '@angular/material/tooltip/testing';
11-
import { TestElement } from '@angular/cdk/testing';
1210

1311
export { LegacyTooltipHarnessFilters }
1412

@@ -21,8 +19,6 @@ export class MatLegacyTooltipHarness extends _MatTooltipHarnessBase {
2119
// (undocumented)
2220
static hostSelector: string;
2321
// (undocumented)
24-
protected _optionalPanel: AsyncFactoryFn<TestElement | null>;
25-
// (undocumented)
2622
protected _showAnimationName: string;
2723
static with(options?: LegacyTooltipHarnessFilters): HarnessPredicate<MatLegacyTooltipHarness>;
2824
}

tools/public_api_guard/material/tooltip-testing.md

-6
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,10 @@
44
55
```ts
66

7-
import { AsyncFactoryFn } from '@angular/cdk/testing';
87
import { BaseHarnessFilters } from '@angular/cdk/testing';
98
import { ComponentHarness } from '@angular/cdk/testing';
109
import { ComponentHarnessConstructor } from '@angular/cdk/testing';
1110
import { HarnessPredicate } from '@angular/cdk/testing';
12-
import { TestElement } from '@angular/cdk/testing';
1311

1412
// @public
1513
export class MatTooltipHarness extends _MatTooltipHarnessBase {
@@ -20,8 +18,6 @@ export class MatTooltipHarness extends _MatTooltipHarnessBase {
2018
// (undocumented)
2119
static hostSelector: string;
2220
// (undocumented)
23-
protected _optionalPanel: AsyncFactoryFn<TestElement | null>;
24-
// (undocumented)
2521
protected _showAnimationName: string;
2622
static with<T extends MatTooltipHarness>(this: ComponentHarnessConstructor<T>, options?: TooltipHarnessFilters): HarnessPredicate<T>;
2723
}
@@ -35,8 +31,6 @@ export abstract class _MatTooltipHarnessBase extends ComponentHarness {
3531
// (undocumented)
3632
protected abstract _hideAnimationName: string;
3733
isOpen(): Promise<boolean>;
38-
// (undocumented)
39-
protected abstract _optionalPanel: AsyncFactoryFn<TestElement | null>;
4034
show(): Promise<void>;
4135
// (undocumented)
4236
protected abstract _showAnimationName: string;

tools/public_api_guard/material/tooltip.md

+3
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,8 @@ export abstract class _MatTooltipBase<T extends _TooltipComponentBase> implement
105105
ngOnDestroy(): void;
106106
// (undocumented)
107107
_overlayRef: OverlayRef | null;
108+
// (undocumented)
109+
readonly _panelId: string;
108110
get position(): TooltipPosition;
109111
set position(value: TooltipPosition);
110112
// (undocumented)
@@ -195,6 +197,7 @@ export abstract class _TooltipComponentBase implements OnDestroy {
195197
_handleMouseLeave({ relatedTarget }: MouseEvent): void;
196198
hide(delay: number): void;
197199
protected abstract readonly _hideAnimation: string;
200+
_id: string | undefined;
198201
isVisible(): boolean;
199202
_markForCheck(): void;
200203
message: string;

0 commit comments

Comments
 (0)