Skip to content

Commit

Permalink
feat: show deprecated related information in material/tooltip
Browse files Browse the repository at this point in the history
previously we weren't showing any other information other than `breaking-change` for deprecated fields, this commit adds a component that protrays information regarding deprecation in tooltips rather than `title` attribute

closes angular/components#29839
  • Loading branch information
naaajii committed Oct 21, 2024
1 parent 6a6db1c commit 122e253
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 4 deletions.
36 changes: 36 additions & 0 deletions src/app/shared/doc-viewer/deprecated-tooltip.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import {Component, Input} from '@angular/core';
import {MatTooltipModule} from '@angular/material/tooltip';

/**
* This component is responsible for showing the
* deprecated fields throughout API from material repo,
*
* When deprecated docs content is generated like:
*
* <div class="docs-api-class-deprecated-marker"
* title="Will be removed in v21.0.0 or later">
* Deprecated
* </div>
*
* It uses `title` attribute to show information regarding
* depreciation and other information regarding depreciation
* isnt shown either.
*
* We are gonna use this component to show deprecation
* information using the `material/tooltip`, the information
* would contain when the field is being deprecated and what
* are the alternatives to it which both are extracted from
* `deprecated` and `breaking-change`.
*/
@Component({
selector: 'deprecated-field',
template: `<div class="deprecated-content"
[matTooltip]="message">
</div>`,
standalone: true,
imports: [MatTooltipModule],
})
export class DeprecatedFieldComponent {
/** Message regarding the deprecation */
@Input() message!: string;
}
6 changes: 4 additions & 2 deletions src/app/shared/doc-viewer/doc-viewer-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {PortalModule} from '@angular/cdk/portal';
import {NgModule} from '@angular/core';
import {HeaderLink} from './header-link';
import {CodeSnippet} from '../example-viewer/code-snippet';
import {DeprecatedFieldComponent} from './deprecated-tooltip';


// ExampleViewer is included in the DocViewerModule because they have a circular dependency.
Expand All @@ -23,8 +24,9 @@ import {CodeSnippet} from '../example-viewer/code-snippet';
DocViewer,
ExampleViewer,
HeaderLink,
CodeSnippet
CodeSnippet,
DeprecatedFieldComponent
],
exports: [DocViewer, ExampleViewer, HeaderLink]
exports: [DocViewer, ExampleViewer, HeaderLink, DeprecatedFieldComponent]
})
export class DocViewerModule { }
30 changes: 30 additions & 0 deletions src/app/shared/doc-viewer/doc-viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {Observable, Subscription} from 'rxjs';
import {shareReplay, take, tap} from 'rxjs/operators';
import {ExampleViewer} from '../example-viewer/example-viewer';
import {HeaderLink} from './header-link';
import {DeprecatedFieldComponent} from './deprecated-tooltip';

@Injectable({providedIn: 'root'})
class DocFetcher {
Expand Down Expand Up @@ -121,6 +122,9 @@ export class DocViewer implements OnDestroy {
this._loadComponents('material-docs-example', ExampleViewer);
this._loadComponents('header-link', HeaderLink);

// Create tooltips for the deprecated fields
this._createTooltipsForDeprecated();

// Resolving and creating components dynamically in Angular happens synchronously, but since
// we want to emit the output if the components are actually rendered completely, we wait
// until the Angular zone becomes stable.
Expand Down Expand Up @@ -166,4 +170,30 @@ export class DocViewer implements OnDestroy {
this._clearLiveExamples();
this._documentFetchSubscription?.unsubscribe();
}

_createTooltipsForDeprecated() {
// all of the deprecated markers end with `deprecated-marker`
// in their class name
const deprecatedElements =
this._elementRef.nativeElement.querySelectorAll(`[class$=deprecated-marker]`);

[...deprecatedElements].forEach((element: Element) => {
// the deprecation message, it will include alternative to deprecated item
// and breaking change if there is one included.
const deprecationTitle = element.getAttribute('deprecated-message');

const elementPortalOutlet = new DomPortalOutlet(
element, this._componentFactoryResolver, this._appRef, this._injector);

const tooltipPortal = new ComponentPortal(DeprecatedFieldComponent, this._viewContainerRef);
const tooltipOutlet = elementPortalOutlet.attach(tooltipPortal);


if (deprecationTitle) {
tooltipOutlet.instance.message = deprecationTitle;
}

this._portalHosts.push(elementPortalOutlet);
});
}
}
15 changes: 13 additions & 2 deletions src/styles/_api.scss
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,19 @@
.docs-api-interface-deprecated-marker {
display: inline-block;
font-weight: bold;

&[title] {
position: relative;

// We want to set width and height according to our parent
// deprecated marker element because the component that presents
// the tooltip for depcreated message is empty by default and
// empty element can not be able to show up therefore the tooltip
// wont show either. This makes sure that our tooltip component
// is aligned with deprecated marker in position and size.
& .deprecated-content {
position: absolute;
width: 100%;
height: 100%;
top: 0;
border-bottom: 1px dotted grey;
cursor: help;
}
Expand Down

0 comments on commit 122e253

Please sign in to comment.