Skip to content
This repository has been archived by the owner on Dec 18, 2024. It is now read-only.

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 authored and crisbeto committed Dec 3, 2024
1 parent 768fa1b commit 1d6bd9f
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 5 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} 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
* 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: `<div class="deprecated-content"
[matTooltip]="message">
</div>`,
standalone: true,
imports: [MatTooltipModule],
})
export class DeprecatedFieldComponent {
/** Message regarding the deprecation */
message = '';
}
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 { }
47 changes: 46 additions & 1 deletion src/app/shared/doc-viewer/doc-viewer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
});

Expand Down Expand Up @@ -177,6 +207,21 @@ const FAKE_DOCS: {[key: string]: string} = {
'<div material-docs-example="demo-example"></div>',
'http://material.angular.io/whole-snippet-example.html':
'<div material-docs-example="whole-snippet-example" file="whole-snippet-example.ts"></div>',
'http://material.angular.io/deprecated.html':
`<div class="docs-api-class-deprecated-marker"
deprecated-message="deprecated class">Deprecated</div>
<div class="docs-api-constant-deprecated-marker"
deprecated-message="deprecated constant">Deprecated</div>
<div class="docs-api-interface-deprecated-marker"
deprecated-message="deprecated interface">Deprecated</div>
<div class="docs-api-type-alias-deprecated-marker"
deprecated-message="deprecated type alias">Deprecated</div>
<div class="docs-api-deprecated-marker"
deprecated-message="deprecated">Deprecated</div>`,
/* eslint-enable @typescript-eslint/naming-convention */
};

Expand Down
38 changes: 38 additions & 0 deletions src/app/shared/doc-viewer/doc-viewer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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:
// <div class="docs-api-deprecated-marker">Deprecated</div>,
// 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);
});
}
}
15 changes: 13 additions & 2 deletions src/styles/_api.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down

0 comments on commit 1d6bd9f

Please sign in to comment.