Skip to content

Commit

Permalink
feat(material/form-field): replicate tooltipClass to default MatToolt…
Browse files Browse the repository at this point in the history
…ipDefaultOptions

The `tooltipClass` property has been added to the default configuration options in
`MatTooltipDefaultOptions`. This new property is optional and supports the same syntax
as `ngClass`, just like the component's default attribute.
As with some existing configurations, if a CSS class is defined directly on the tooltip
component, it will automatically override the default class. An example has been added
to the `tooltip-demo` file. Additionally, two tests have been created to ensure the
solution works as expected.

Fixes angular#29355
  • Loading branch information
jullierme committed Jul 21, 2024
1 parent 10da6c6 commit 135bdd3
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/components-examples/material/tooltip/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export {TooltipAutoHideExample} from './tooltip-auto-hide/tooltip-auto-hide-example';
export {TooltipCustomClassExample} from './tooltip-custom-class/tooltip-custom-class-example';
export {TooltipDefaultCustomClassExample} from './tooltip-default-custom-class/tooltip-default-custom-class-example';
export {TooltipDelayExample} from './tooltip-delay/tooltip-delay-example';
export {TooltipDisabledExample} from './tooltip-disabled/tooltip-disabled-example';
export {TooltipManualExample} from './tooltip-manual/tooltip-manual-example';
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.example-tooltip-default-custom-class {
text-transform: uppercase;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<button mat-raised-button
matTooltip="This tooltip uses the global custom class"
aria-label="Button that displays a tooltip with a globally applied custom class">
Button with custom tooltip class
</button>
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {Component, ViewEncapsulation} from '@angular/core';
import {
MAT_TOOLTIP_DEFAULT_OPTIONS,
MAT_TOOLTIP_DEFAULT_OPTIONS_FACTORY,
MatTooltipDefaultOptions,
MatTooltipModule,
} from '@angular/material/tooltip';
import {MatButtonModule} from '@angular/material/button';

/** Custom options the configure the tooltip's default class. */
export const myCustomTooltipDefaults: MatTooltipDefaultOptions = {
...MAT_TOOLTIP_DEFAULT_OPTIONS_FACTORY(),
tooltipClass: 'example-tooltip-default-custom-class',
};

/**
* @title Tooltip with default custom class
*/
@Component({
selector: 'tooltip-default-custom-class-example',
templateUrl: 'tooltip-default-custom-class-example.html',
styleUrl: 'tooltip-default-custom-class-example.css',
providers: [{provide: MAT_TOOLTIP_DEFAULT_OPTIONS, useValue: myCustomTooltipDefaults}],
standalone: true,
// Need to remove view encapsulation so that the custom tooltip style defined in
// `tooltip-default-custom-class-example.css` will not be scoped to this component's view.
encapsulation: ViewEncapsulation.None,
imports: [MatButtonModule, MatTooltipModule],
})
export class TooltipDefaultCustomClassExample {}
3 changes: 3 additions & 0 deletions src/dev-app/tooltip/tooltip-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ <h3>Tooltip auto hide</h3>
<h3>Tooltip custom class</h3>
<tooltip-custom-class-example></tooltip-custom-class-example>

<h3>Tooltip default custom class</h3>
<tooltip-default-custom-class-example></tooltip-default-custom-class-example>

<h3>Tooltip with delay</h3>
<tooltip-delay-example></tooltip-delay-example>

Expand Down
2 changes: 2 additions & 0 deletions src/dev-app/tooltip/tooltip-demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import {
TooltipAutoHideExample,
TooltipCustomClassExample,
TooltipDefaultCustomClassExample,
TooltipDelayExample,
TooltipDisabledExample,
TooltipHarnessExample,
Expand All @@ -27,6 +28,7 @@ import {ChangeDetectionStrategy, Component} from '@angular/core';
imports: [
TooltipAutoHideExample,
TooltipCustomClassExample,
TooltipDefaultCustomClassExample,
TooltipDelayExample,
TooltipDisabledExample,
TooltipManualExample,
Expand Down
81 changes: 81 additions & 0 deletions src/material/tooltip/tooltip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,65 @@ describe('MDC-based MatTooltip', () => {
expect(tooltipDirective._getOverlayPosition().fallback.overlayX).toBe('end');
}));

it('should be able to define a default (global) tooltip class', fakeAsync(() => {
TestBed.resetTestingModule()
.configureTestingModule({
declarations: [TooltipDemoWithoutTooltipClassBinding],
imports: [MatTooltipModule, OverlayModule],
providers: [
{
provide: MAT_TOOLTIP_DEFAULT_OPTIONS,
useValue: {tooltipClass: 'my-default-tooltip-class'},
},
],
})
.compileComponents();

const fixture = TestBed.createComponent(TooltipDemoWithoutTooltipClassBinding);
fixture.detectChanges();
tooltipDirective = fixture.componentInstance.tooltip;
tooltipDirective.show();
fixture.detectChanges();
tick();
const overlayRef = tooltipDirective._overlayRef!;
const tooltipElement = overlayRef.overlayElement.querySelector(
'.mat-mdc-tooltip',
) as HTMLElement;

expect(tooltipDirective.tooltipClass).toBe('my-default-tooltip-class');
expect(tooltipElement.classList).toContain('my-default-tooltip-class');
}));

it('should be able to provide tooltip class over the custom default one', fakeAsync(() => {
TestBed.resetTestingModule()
.configureTestingModule({
declarations: [TooltipDemoWithTooltipClassBinding],
imports: [MatTooltipModule, OverlayModule],
providers: [
{
provide: MAT_TOOLTIP_DEFAULT_OPTIONS,
useValue: {tooltipClass: 'my-default-tooltip-class'},
},
],
})
.compileComponents();

const fixture = TestBed.createComponent(TooltipDemoWithTooltipClassBinding);
fixture.detectChanges();
tooltipDirective = fixture.componentInstance.tooltip;
tooltipDirective.show();
fixture.detectChanges();
tick();
const overlayRef = tooltipDirective._overlayRef!;
const tooltipElement = overlayRef.overlayElement.querySelector(
'.mat-mdc-tooltip',
) as HTMLElement;

expect(tooltipDirective.tooltipClass).not.toBe('my-default-tooltip-class');
expect(tooltipElement.classList).not.toContain('my-default-tooltip-class');
expect(tooltipElement.classList).toContain('fixed-tooltip-class');
}));

it('should position on the bottom-left by default', fakeAsync(() => {
// We don't bind mouse events on mobile devices.
if (platform.IOS || platform.ANDROID) {
Expand Down Expand Up @@ -1660,6 +1719,28 @@ class TooltipDemoWithoutPositionBinding {
@ViewChild('button') button: ElementRef<HTMLButtonElement>;
}

@Component({
selector: 'app',
template: `<button #button [matTooltip]="message">Button</button>`,
})
class TooltipDemoWithoutTooltipClassBinding {
message = initialTooltipMessage;
@ViewChild(MatTooltip) tooltip: MatTooltip;
@ViewChild('button') button: ElementRef<HTMLButtonElement>;
}

@Component({
selector: 'app',
template: `
<button #button matTooltipClass="fixed-tooltip-class" [matTooltip]="message">Button</button>
`,
})
class TooltipDemoWithTooltipClassBinding {
message: any = initialTooltipMessage;
@ViewChild(MatTooltip) tooltip: MatTooltip;
@ViewChild('button') button: ElementRef<HTMLButtonElement>;
}

@Component({
selector: 'app',
styles: `button { width: 500px; height: 500px; }`,
Expand Down
11 changes: 11 additions & 0 deletions src/material/tooltip/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,13 @@ export interface MatTooltipDefaultOptions {

/** Disables the ability for the user to interact with the tooltip element. */
disableTooltipInteractivity?: boolean;

/**
* Default classes to be applied to the tooltip. Supports the same syntax as `ngClass`. These
* default classes will not be applied if `tooltipClass` is defined directly on the tooltip
* element, as it will override the default.
*/
tooltipClass?: string | string[] | Set<string> | {[key: string]: any};
}

/**
Expand Down Expand Up @@ -389,6 +396,10 @@ export class MatTooltip implements OnDestroy, AfterViewInit {
if (_defaultOptions.touchGestures) {
this.touchGestures = _defaultOptions.touchGestures;
}

if (_defaultOptions.tooltipClass) {
this.tooltipClass = _defaultOptions.tooltipClass;
}
}

_dir.change.pipe(takeUntil(this._destroyed)).subscribe(() => {
Expand Down

0 comments on commit 135bdd3

Please sign in to comment.