From 122e2530a8d8530db1e75baa3c6528f1dcea4144 Mon Sep 17 00:00:00 2001 From: naaajii Date: Mon, 14 Oct 2024 05:13:36 +0500 Subject: [PATCH] feat: show deprecated related information in `material/tooltip` 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 --- .../shared/doc-viewer/deprecated-tooltip.ts | 36 +++++++++++++++++++ .../shared/doc-viewer/doc-viewer-module.ts | 6 ++-- src/app/shared/doc-viewer/doc-viewer.ts | 30 ++++++++++++++++ src/styles/_api.scss | 15 ++++++-- 4 files changed, 83 insertions(+), 4 deletions(-) create mode 100644 src/app/shared/doc-viewer/deprecated-tooltip.ts diff --git a/src/app/shared/doc-viewer/deprecated-tooltip.ts b/src/app/shared/doc-viewer/deprecated-tooltip.ts new file mode 100644 index 00000000..d98ae075 --- /dev/null +++ b/src/app/shared/doc-viewer/deprecated-tooltip.ts @@ -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: + * + *
+ * Deprecated + *
+ * + * 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: `
+
`, + standalone: true, + imports: [MatTooltipModule], +}) +export class DeprecatedFieldComponent { + /** Message regarding the deprecation */ + @Input() message!: string; +} diff --git a/src/app/shared/doc-viewer/doc-viewer-module.ts b/src/app/shared/doc-viewer/doc-viewer-module.ts index 9c3f51c0..36f5a9ad 100644 --- a/src/app/shared/doc-viewer/doc-viewer-module.ts +++ b/src/app/shared/doc-viewer/doc-viewer-module.ts @@ -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. @@ -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 { } diff --git a/src/app/shared/doc-viewer/doc-viewer.ts b/src/app/shared/doc-viewer/doc-viewer.ts index 8690fad8..0e456182 100644 --- a/src/app/shared/doc-viewer/doc-viewer.ts +++ b/src/app/shared/doc-viewer/doc-viewer.ts @@ -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 { @@ -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. @@ -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); + }); + } } diff --git a/src/styles/_api.scss b/src/styles/_api.scss index 0fa8b7e7..ffdafb9e 100644 --- a/src/styles/_api.scss +++ b/src/styles/_api.scss @@ -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; }