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..78cafe38
--- /dev/null
+++ b/src/app/shared/doc-viewer/deprecated-tooltip.ts
@@ -0,0 +1,36 @@
+import {Component} 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
+ * deprecation and other information regarding deprecation
+ * 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
+ * `breaking-change` and `deprecated`.
+ */
+@Component({
+ selector: 'deprecated-field',
+ template: `
+
`,
+ standalone: true,
+ imports: [MatTooltipModule],
+})
+export class DeprecatedFieldComponent {
+ /** Message regarding the deprecation */
+ message = '';
+}
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.spec.ts b/src/app/shared/doc-viewer/doc-viewer.spec.ts
index c8b0c290..6878aca9 100644
--- a/src/app/shared/doc-viewer/doc-viewer.spec.ts
+++ b/src/app/shared/doc-viewer/doc-viewer.spec.ts
@@ -6,7 +6,7 @@ import {DocsAppTestingModule} from '../../testing/testing-module';
import {DocViewer} from './doc-viewer';
import {DocViewerModule} from './doc-viewer-module';
import {ExampleViewer} from '../example-viewer/example-viewer';
-
+import {MatTooltip} from '@angular/material/tooltip';
describe('DocViewer', () => {
let http: HttpTestingController;
@@ -149,6 +149,36 @@ describe('DocViewer', () => {
expect(console.error).toHaveBeenCalledTimes(1);
});
+ it('should show tooltip for deprecated symbol', () => {
+ const fixture = TestBed.createComponent(DocViewerTestComponent);
+ fixture.componentInstance.documentUrl = `http://material.angular.io/deprecated.html`;
+ fixture.detectChanges();
+
+ const url = fixture.componentInstance.documentUrl;
+ http.expectOne(url).flush(FAKE_DOCS[url]);
+
+ const docViewer = fixture.debugElement.query(By.directive(DocViewer));
+
+ expect(docViewer).not.toBeNull();
+
+ // we have five deprecated symbols: class, constant, type alias, interface
+ // and properties.
+ expect(docViewer.children.length).toBe(5);
+
+ // it should have "Deprecated" as its inner text
+ const deprecatedSymbol = docViewer.children.shift()!;
+ expect(deprecatedSymbol.nativeElement.innerText).toBe('Deprecated');
+
+ // should contain the tooltip component
+ const tooltipElement = deprecatedSymbol.children.shift()!;
+ expect(tooltipElement.nativeElement).toBeTruthy();
+
+ // should show tooltip on hovering the element
+ tooltipElement.nativeNode.dispatchEvent(new MouseEvent('hover'));
+ fixture.detectChanges();
+ expect(deprecatedSymbol.query(By.directive(MatTooltip))).toBeTruthy();
+ });
+
// TODO(mmalerba): Add test that example-viewer is instantiated.
});
@@ -177,6 +207,21 @@ const FAKE_DOCS: {[key: string]: string} = {
'',
'http://material.angular.io/whole-snippet-example.html':
'',
+ 'http://material.angular.io/deprecated.html':
+ `Deprecated
+
+ Deprecated
+
+ Deprecated
+
+ Deprecated
+
+ Deprecated
`,
/* eslint-enable @typescript-eslint/naming-convention */
};
diff --git a/src/app/shared/doc-viewer/doc-viewer.ts b/src/app/shared/doc-viewer/doc-viewer.ts
index a4e23d64..a4f63327 100644
--- a/src/app/shared/doc-viewer/doc-viewer.ts
+++ b/src/app/shared/doc-viewer/doc-viewer.ts
@@ -27,6 +27,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 {
@@ -144,6 +145,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.
@@ -189,4 +193,38 @@ export class DocViewer implements OnDestroy {
this._clearLiveExamples();
this._documentFetchSubscription?.unsubscribe();
}
+
+ _createTooltipsForDeprecated() {
+ // all of the deprecated symbols end with `deprecated-marker`
+ // class name on their element.
+ // for example:
+ // Deprecated
,
+ // these can vary for each deprecated symbols such for class, interface,
+ // type alias, constants or properties:
+ // .docs-api-class-interface-marker, docs-api-type-alias-deprecated-marker
+ // .docs-api-constant-deprecated-marker, .some-more
+ // so instead of manually writing each deprecated class, we just query
+ // elements that ends 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 6d59e384..a74bbf65 100644
--- a/src/styles/_api.scss
+++ b/src/styles/_api.scss
@@ -131,8 +131,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;
}