build(docs-infra): expose deprecated status on items more clearly (#25750)
PR Close #25750
This commit is contained in:
parent
cea2e0477c
commit
026b60cd70
|
@ -775,7 +775,7 @@ describe('AppComponent', () => {
|
|||
const searchService: MockSearchService = TestBed.get(SearchService);
|
||||
|
||||
const results = [
|
||||
{ path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' }
|
||||
{ path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '', deprecated: false }
|
||||
];
|
||||
|
||||
searchService.searchResults.next({ query: 'something', results: results });
|
||||
|
|
|
@ -22,11 +22,14 @@
|
|||
|
||||
<article class="api-list-container l-content-small docs-content">
|
||||
<div *ngFor="let section of filteredSections | async" >
|
||||
<h2 *ngIf="section.items"><a [href]="section.path">{{section.title}}</a></h2>
|
||||
<h2 *ngIf="section.items"><a [href]="section.path" [class.deprecated-api-item]="section.deprecated">{{section.title}}</a></h2>
|
||||
<ul class="api-list" *ngIf="section.items?.length">
|
||||
<ng-container *ngFor="let item of section.items">
|
||||
<li class="api-item">
|
||||
<a [href]="item.path"><span class="symbol {{item.docType}}"></span> {{item.title}}</a>
|
||||
<a [href]="item.path" [class.deprecated-api-item]="item.stability === 'deprecated'">
|
||||
<span class="symbol {{item.docType}}"></span>
|
||||
{{item.title}}
|
||||
</a>
|
||||
</li>
|
||||
</ng-container>
|
||||
</ul>
|
||||
|
|
|
@ -226,6 +226,7 @@ const apiSections: ApiSection[] = [
|
|||
"name": "common",
|
||||
"title": "common",
|
||||
"path": "api/common",
|
||||
"deprecated": false,
|
||||
"items": [
|
||||
{
|
||||
"name": "class_1",
|
||||
|
@ -233,7 +234,7 @@ const apiSections: ApiSection[] = [
|
|||
"path": "api/common/class_1",
|
||||
"docType": "class",
|
||||
"stability": "experimental",
|
||||
"securityRisk": false
|
||||
"securityRisk": false,
|
||||
},
|
||||
{
|
||||
"name": "class_2",
|
||||
|
@ -241,7 +242,7 @@ const apiSections: ApiSection[] = [
|
|||
"path": "api/common/class_2",
|
||||
"docType": "class",
|
||||
"stability": "stable",
|
||||
"securityRisk": false
|
||||
"securityRisk": false,
|
||||
},
|
||||
{
|
||||
"name": "directive_1",
|
||||
|
@ -249,7 +250,7 @@ const apiSections: ApiSection[] = [
|
|||
"path": "api/common/directive_1",
|
||||
"docType": "directive",
|
||||
"stability": "stable",
|
||||
"securityRisk": true
|
||||
"securityRisk": true,
|
||||
},
|
||||
{
|
||||
"name": "pipe_1",
|
||||
|
@ -257,7 +258,7 @@ const apiSections: ApiSection[] = [
|
|||
"path": "api/common/pipe_1",
|
||||
"docType": "pipe",
|
||||
"stability": "stable",
|
||||
"securityRisk": true
|
||||
"securityRisk": true,
|
||||
},
|
||||
]
|
||||
},
|
||||
|
@ -265,6 +266,7 @@ const apiSections: ApiSection[] = [
|
|||
"name": "core",
|
||||
"title": "core",
|
||||
"path": "api/core",
|
||||
"deprecated": false,
|
||||
"items": [
|
||||
{
|
||||
"name": "class_3",
|
||||
|
@ -272,7 +274,7 @@ const apiSections: ApiSection[] = [
|
|||
"path": "api/core/class_3",
|
||||
"docType": "class",
|
||||
"stability": "experimental",
|
||||
"securityRisk": false
|
||||
"securityRisk": false,
|
||||
},
|
||||
{
|
||||
"name": "function_1",
|
||||
|
@ -280,7 +282,7 @@ const apiSections: ApiSection[] = [
|
|||
"path": "api/core/function 1",
|
||||
"docType": "function",
|
||||
"stability": "deprecated",
|
||||
"securityRisk": true
|
||||
"securityRisk": true,
|
||||
},
|
||||
{
|
||||
"name": "const_1",
|
||||
|
@ -288,7 +290,7 @@ const apiSections: ApiSection[] = [
|
|||
"path": "api/core/const_1",
|
||||
"docType": "const",
|
||||
"stability": "stable",
|
||||
"securityRisk": false
|
||||
"securityRisk": false,
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -50,7 +50,10 @@ describe('ApiService', () => {
|
|||
describe('#sections', () => {
|
||||
|
||||
it('first subscriber should fetch sections', done => {
|
||||
const data = [{name: 'a', title: 'A', path: '', items: []}, {name: 'b', title: 'B', path: '', items: []}];
|
||||
const data = [
|
||||
{name: 'a', title: 'A', path: '', items: [], deprecated: false},
|
||||
{name: 'b', title: 'B', path: '', items: [], deprecated: false},
|
||||
];
|
||||
|
||||
service.sections.subscribe(sections => {
|
||||
expect(sections).toEqual(data);
|
||||
|
@ -61,7 +64,10 @@ describe('ApiService', () => {
|
|||
});
|
||||
|
||||
it('second subscriber should get previous sections and NOT trigger refetch', done => {
|
||||
const data = [{name: 'a', title: 'A', path: '', items: []}, {name: 'b', title: 'B', path: '', items: []}];
|
||||
const data = [
|
||||
{name: 'a', title: 'A', path: '', items: [], deprecated: false},
|
||||
{name: 'b', title: 'B', path: '', items: [], deprecated: false},
|
||||
];
|
||||
let subscriptions = 0;
|
||||
|
||||
service.sections.subscribe(sections => {
|
||||
|
@ -91,7 +97,10 @@ describe('ApiService', () => {
|
|||
|
||||
let call = 0;
|
||||
|
||||
let data = [{name: 'a', title: 'A', path: '', items: []}, {name: 'b', title: 'B', path: '', items: []}];
|
||||
let data = [
|
||||
{name: 'a', title: 'A', path: '', items: [], deprecated: false},
|
||||
{name: 'b', title: 'B', path: '', items: [], deprecated: false},
|
||||
];
|
||||
|
||||
service.sections.subscribe(sections => {
|
||||
// called twice during this test
|
||||
|
@ -103,7 +112,7 @@ describe('ApiService', () => {
|
|||
httpMock.expectOne({}).flush(data);
|
||||
|
||||
// refresh/refetch
|
||||
data = [{name: 'c', title: 'C', path: '', items: []}];
|
||||
data = [{name: 'c', title: 'C', path: '', items: [], deprecated: false}];
|
||||
service.fetchSections();
|
||||
httpMock.expectOne({}).flush(data);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ export interface ApiSection {
|
|||
path: string;
|
||||
name: string;
|
||||
title: string;
|
||||
deprecated: boolean;
|
||||
items: ApiItem[]|null;
|
||||
}
|
||||
|
||||
|
@ -47,7 +48,12 @@ export class ApiService implements OnDestroy {
|
|||
this._sections.subscribe(sections => this.logger.log('ApiService got API sections') );
|
||||
}
|
||||
|
||||
return this._sections;
|
||||
return this._sections.pipe(tap(sections => {
|
||||
sections.forEach(section => {
|
||||
section.deprecated = !!section.items &&
|
||||
section.items.every(item => item.stability === 'deprecated');
|
||||
});
|
||||
}));
|
||||
};
|
||||
|
||||
constructor(private http: HttpClient, private logger: Logger) { }
|
||||
|
|
|
@ -9,6 +9,7 @@ export interface SearchResult {
|
|||
type: string;
|
||||
titleWords: string;
|
||||
keywords: string;
|
||||
deprecated: boolean;
|
||||
}
|
||||
|
||||
export interface SearchArea {
|
||||
|
|
|
@ -9,14 +9,16 @@
|
|||
<ul class="priority-pages" >
|
||||
<li class="search-page" *ngFor="let page of area.priorityPages">
|
||||
<a class="search-result-item" href="{{ page.path }}" (click)="onResultSelected(page, $event)">
|
||||
<span class="symbol {{page.type}}" *ngIf="area.name === 'api'"></span>{{ page.title }}
|
||||
<span class="symbol {{page.type}}" *ngIf="area.name === 'api'"></span>
|
||||
<span [class.deprecated-api-item]="page.deprecated">{{ page.title }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li class="search-page" *ngFor="let page of area.pages">
|
||||
<a class="search-result-item" href="{{ page.path }}" (click)="onResultSelected(page, $event)">
|
||||
<span class="symbol {{page.type}}" *ngIf="area.name === 'api'"></span>{{ page.title }}
|
||||
<span class="symbol {{page.type}}" *ngIf="area.name === 'api'"></span>
|
||||
<span [class.deprecated-api-item]="page.deprecated">{{ page.title }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
|
|
@ -5,41 +5,59 @@ import { SearchResult } from 'app/search/interfaces';
|
|||
import { SearchResultsComponent } from './search-results.component';
|
||||
|
||||
describe('SearchResultsComponent', () => {
|
||||
|
||||
let component: SearchResultsComponent;
|
||||
let fixture: ComponentFixture<SearchResultsComponent>;
|
||||
let guideA: SearchResult;
|
||||
let apiD: SearchResult;
|
||||
let guideB: SearchResult;
|
||||
let guideAC: SearchResult;
|
||||
let apiC: SearchResult;
|
||||
let guideN: SearchResult;
|
||||
let guideM: SearchResult;
|
||||
let guideL: SearchResult;
|
||||
let guideK: SearchResult;
|
||||
let guideJ: SearchResult;
|
||||
let guideI: SearchResult;
|
||||
let guideH: SearchResult;
|
||||
let guideG: SearchResult;
|
||||
let guideF: SearchResult;
|
||||
let guideE: SearchResult;
|
||||
let standardResults: SearchResult[];
|
||||
|
||||
/** Get all text from component element */
|
||||
/** Get all text from component element. */
|
||||
function getText() { return fixture.debugElement.nativeElement.textContent; }
|
||||
|
||||
/** Get a full set of test results. "Take" what you need */
|
||||
function getTestResults(take?: number) {
|
||||
const results: SearchResult[] = [
|
||||
{ path: 'guide/a', title: 'Guide A' },
|
||||
{ path: 'api/d', title: 'API D' },
|
||||
{ path: 'guide/b', title: 'Guide B' },
|
||||
{ path: 'guide/a/c', title: 'Guide A - C' },
|
||||
{ path: 'api/c', title: 'API C' }
|
||||
]
|
||||
// fill it out to exceed 10 guide pages
|
||||
.concat('nmlkjihgfe'.split('').map(l => {
|
||||
return { path: 'guide/' + l, title: 'Guide ' + l};
|
||||
}))
|
||||
// add these empty fields to satisfy interface
|
||||
.map(r => ({...{ keywords: '', titleWords: '', type: '' }, ...r }));
|
||||
|
||||
return take === undefined ? results : results.slice(0, take);
|
||||
}
|
||||
|
||||
function compareTitle(l: SearchResult, r: SearchResult) {
|
||||
return l.title!.toUpperCase() > r.title!.toUpperCase() ? 1 : -1;
|
||||
}
|
||||
|
||||
/** Pass the given search results to the component and trigger change detection. */
|
||||
function setSearchResults(query: string, results: SearchResult[]) {
|
||||
component.searchResults = {query, results};
|
||||
component.ngOnChanges({});
|
||||
fixture.detectChanges();
|
||||
}
|
||||
|
||||
/** Get a full set of test results. "Take" what you need */
|
||||
beforeEach(() => {
|
||||
apiD = { path: 'api/d', title: 'API D', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
apiC = { path: 'api/c', title: 'API C', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideA = { path: 'guide/a', title: 'Guide A', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideB = { path: 'guide/b', title: 'Guide B', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideAC = { path: 'guide/a/c', title: 'Guide A - C', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideE = { path: 'guide/e', title: 'Guide e', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideF = { path: 'guide/f', title: 'Guide f', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideG = { path: 'guide/g', title: 'Guide g', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideH = { path: 'guide/h', title: 'Guide h', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideI = { path: 'guide/i', title: 'Guide i', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideJ = { path: 'guide/j', title: 'Guide j', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideK = { path: 'guide/k', title: 'Guide k', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideL = { path: 'guide/l', title: 'Guide l', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideM = { path: 'guide/m', title: 'Guide m', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
guideN = { path: 'guide/n', title: 'Guide n', deprecated: false, keywords: '', titleWords: '', type: '' };
|
||||
|
||||
standardResults = [
|
||||
guideA, apiD, guideB, guideAC, apiC, guideN, guideM, guideL, guideK, guideJ, guideI, guideH, guideG, guideF, guideE,
|
||||
];
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ SearchResultsComponent ]
|
||||
|
@ -53,48 +71,42 @@ describe('SearchResultsComponent', () => {
|
|||
});
|
||||
|
||||
it('should map the search results into groups based on their containing folder', () => {
|
||||
setSearchResults('', getTestResults(3));
|
||||
setSearchResults('', [guideA, apiD, guideB]);
|
||||
expect(component.searchAreas).toEqual([
|
||||
{ name: 'api', priorityPages: [
|
||||
{ path: 'api/d', title: 'API D', type: '', keywords: '', titleWords: '' }
|
||||
], pages: [] },
|
||||
{ name: 'guide', priorityPages: [
|
||||
{ path: 'guide/a', title: 'Guide A', type: '', keywords: '', titleWords: '' },
|
||||
{ path: 'guide/b', title: 'Guide B', type: '', keywords: '', titleWords: '' },
|
||||
], pages: [] }
|
||||
{ name: 'api', priorityPages: [apiD], pages: [] },
|
||||
{ name: 'guide', priorityPages: [guideA, guideB], pages: [] }
|
||||
]);
|
||||
});
|
||||
|
||||
it('should special case results that are top level folders', () => {
|
||||
setSearchResults('', [
|
||||
{ path: 'tutorial', title: 'Tutorial index', type: '', keywords: '', titleWords: '' },
|
||||
{ path: 'tutorial/toh-pt1', title: 'Tutorial - part 1', type: '', keywords: '', titleWords: '' },
|
||||
{ path: 'tutorial', title: 'Tutorial index', type: '', keywords: '', titleWords: '', deprecated: false },
|
||||
{ path: 'tutorial/toh-pt1', title: 'Tutorial - part 1', type: '', keywords: '', titleWords: '', deprecated: false },
|
||||
]);
|
||||
expect(component.searchAreas).toEqual([
|
||||
{ name: 'tutorial', priorityPages: [
|
||||
{ path: 'tutorial', title: 'Tutorial index', type: '', keywords: '', titleWords: '' },
|
||||
{ path: 'tutorial/toh-pt1', title: 'Tutorial - part 1', type: '', keywords: '', titleWords: '' },
|
||||
{ path: 'tutorial', title: 'Tutorial index', type: '', keywords: '', titleWords: '', deprecated: false },
|
||||
{ path: 'tutorial/toh-pt1', title: 'Tutorial - part 1', type: '', keywords: '', titleWords: '', deprecated: false },
|
||||
], pages: [] }
|
||||
]);
|
||||
});
|
||||
|
||||
it('should put first 5 results for each area into priorityPages', () => {
|
||||
const results = getTestResults();
|
||||
setSearchResults('', results);
|
||||
expect(component.searchAreas[0].priorityPages).toEqual(results.filter(p => p.path.startsWith('api')).slice(0, 5));
|
||||
expect(component.searchAreas[1].priorityPages).toEqual(results.filter(p => p.path.startsWith('guide')).slice(0, 5));
|
||||
it('should put, at most, the first 5 results for each area into priorityPages, not sorted', () => {
|
||||
setSearchResults('', standardResults);
|
||||
expect(component.searchAreas[0].priorityPages).toEqual([apiD, apiC]);
|
||||
expect(component.searchAreas[1].priorityPages).toEqual([guideA, guideB, guideAC, guideN, guideM]);
|
||||
});
|
||||
|
||||
it('should put the nonPriorityPages into the pages array, sorted by title', () => {
|
||||
const results = getTestResults();
|
||||
setSearchResults('', results);
|
||||
setSearchResults('', standardResults);
|
||||
expect(component.searchAreas[0].pages).toEqual([]);
|
||||
expect(component.searchAreas[1].pages).toEqual(results.filter(p => p.path.startsWith('guide')).slice(5).sort(compareTitle));
|
||||
expect(component.searchAreas[1].pages).toEqual([
|
||||
guideE, guideF, guideG, guideH, guideI, guideJ, guideK, guideL
|
||||
]);
|
||||
});
|
||||
|
||||
it('should put a total count in the header of each area of search results', () => {
|
||||
const results = getTestResults();
|
||||
setSearchResults('', results);
|
||||
setSearchResults('', standardResults);
|
||||
fixture.detectChanges();
|
||||
const headers = fixture.debugElement.queryAll(By.css('h3'));
|
||||
expect(headers.length).toEqual(2);
|
||||
|
@ -104,26 +116,55 @@ describe('SearchResultsComponent', () => {
|
|||
|
||||
it('should put search results with no containing folder into the default area (other)', () => {
|
||||
const results = [
|
||||
{ path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' }
|
||||
{ path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '', deprecated: false }
|
||||
];
|
||||
|
||||
setSearchResults('', results);
|
||||
expect(component.searchAreas).toEqual([
|
||||
{ name: 'other', priorityPages: [
|
||||
{ path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' }
|
||||
{ path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '', deprecated: false }
|
||||
], pages: [] }
|
||||
]);
|
||||
});
|
||||
|
||||
it('should omit search results with no title', () => {
|
||||
const results = [
|
||||
{ path: 'news', title: '', type: 'marketing', keywords: '', titleWords: '' }
|
||||
{ path: 'news', title: '', type: 'marketing', keywords: '', titleWords: '', deprecated: false }
|
||||
];
|
||||
|
||||
setSearchResults('something', results);
|
||||
expect(component.searchAreas).toEqual([]);
|
||||
});
|
||||
|
||||
describe('when there are deprecated items', () => {
|
||||
beforeEach(() => {
|
||||
apiD.deprecated = true;
|
||||
guideAC.deprecated = true;
|
||||
guideJ.deprecated = true;
|
||||
guideE.deprecated = true;
|
||||
setSearchResults('something', standardResults);
|
||||
});
|
||||
it('should include deprecated items in priority pages unless there are fewer than 5 non-deprecated priority pages', () => {
|
||||
// Priority pages do not include deprecated items:
|
||||
expect(component.searchAreas[1].priorityPages).not.toContain(guideAC);
|
||||
expect(component.searchAreas[1].priorityPages).not.toContain(guideJ);
|
||||
// Except where there are too few priority pages:
|
||||
expect(component.searchAreas[0].priorityPages).toContain(apiD);
|
||||
});
|
||||
|
||||
it('should move the non-priority deprecated pages to the bottom of the pages list, unsorted', () => {
|
||||
// Bottom pages are the deprecated ones (in original order)
|
||||
expect(component.searchAreas[1].pages.slice(-3)).toEqual([guideAC, guideJ, guideE]);
|
||||
});
|
||||
|
||||
it('should sort the non-deprecated, non-priority pages by title', () => {
|
||||
// The rest of the pages are non-deprecated, sorted by title
|
||||
expect(component.searchAreas[1].pages.slice(0, -3)).toEqual([
|
||||
guideF, guideG, guideH, guideI, guideK,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it('should display "Searching ..." while waiting for search results', () => {
|
||||
fixture.detectChanges();
|
||||
expect(getText()).toContain('Searching ...');
|
||||
|
@ -138,7 +179,7 @@ describe('SearchResultsComponent', () => {
|
|||
component.resultSelected.subscribe((result: SearchResult) => selected = result);
|
||||
|
||||
selected = null;
|
||||
searchResult = { path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' };
|
||||
searchResult = { path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '', deprecated: false };
|
||||
setSearchResults('something', [searchResult]);
|
||||
|
||||
fixture.detectChanges();
|
||||
|
|
|
@ -53,12 +53,12 @@ export class SearchResultsComponent implements OnChanges {
|
|||
});
|
||||
const keys = Object.keys(searchAreaMap).sort((l, r) => l > r ? 1 : -1);
|
||||
return keys.map(name => {
|
||||
let pages: SearchResult[] = searchAreaMap[name];
|
||||
|
||||
// Extract the top 5 most relevant results as priorityPages
|
||||
const priorityPages = pages.splice(0, 5);
|
||||
pages = pages.sort(compareResults);
|
||||
return { name, pages, priorityPages };
|
||||
const {priorityPages, pages, deprecated} = splitPages(searchAreaMap[name]);
|
||||
return {
|
||||
name,
|
||||
priorityPages,
|
||||
pages: pages.concat(deprecated),
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,30 @@ export class SearchResultsComponent implements OnChanges {
|
|||
}
|
||||
}
|
||||
|
||||
function splitPages(allPages: SearchResult[]) {
|
||||
const priorityPages: SearchResult[] = [];
|
||||
const pages: SearchResult[] = [];
|
||||
const deprecated: SearchResult[] = [];
|
||||
allPages.forEach(page => {
|
||||
if (page.deprecated) {
|
||||
deprecated.push(page);
|
||||
} else if (priorityPages.length < 5) {
|
||||
priorityPages.push(page);
|
||||
} else {
|
||||
pages.push(page);
|
||||
}
|
||||
});
|
||||
while (priorityPages.length < 5 && pages.length) {
|
||||
priorityPages.push(pages.shift()!);
|
||||
}
|
||||
while (priorityPages.length < 5 && deprecated.length) {
|
||||
priorityPages.push(deprecated.shift()!);
|
||||
}
|
||||
pages.sort(compareResults);
|
||||
|
||||
return { priorityPages, pages, deprecated };
|
||||
}
|
||||
|
||||
function compareResults(l: SearchResult, r: SearchResult) {
|
||||
return l.title.toUpperCase() > r.title.toUpperCase() ? 1 : -1;
|
||||
}
|
||||
|
|
|
@ -198,6 +198,10 @@ aio-api-list {
|
|||
background: $blue-grey-50;
|
||||
color: $blue-500;
|
||||
}
|
||||
|
||||
&.deprecated-api-item {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,3 +114,7 @@
|
|||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.deprecated-api-item {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
|
|
@ -49,11 +49,7 @@ label.raised, .api-header label {
|
|||
&.api-status-label {
|
||||
background-color: $mediumgray;
|
||||
|
||||
&.security {
|
||||
background-color: $brightred;
|
||||
}
|
||||
|
||||
&.impure-pipe {
|
||||
&.deprecated, &.security, &.impure-pipe {
|
||||
background-color: $brightred;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ $mediumgray: #6e6e6e;
|
|||
$darkgray: #333;
|
||||
$black: #0A1014;
|
||||
$orange: #FF9800;
|
||||
$darkorange: #940;
|
||||
$anti-pattern: $brightred;
|
||||
|
||||
// API & CODE COLORS
|
||||
|
@ -95,7 +96,7 @@ $api-symbols: (
|
|||
),
|
||||
ngmodule: (
|
||||
content: 'M',
|
||||
background: $darkred
|
||||
background: $darkorange
|
||||
),
|
||||
package: (
|
||||
content: 'Pk',
|
||||
|
|
|
@ -175,7 +175,7 @@ describe('site App', function() {
|
|||
page.navigateTo('http/router');
|
||||
const results = page.getSearchResults();
|
||||
|
||||
expect(results).toContain('Http');
|
||||
expect(results).toContain('HttpRequest');
|
||||
expect(results).toContain('Router');
|
||||
});
|
||||
});
|
||||
|
|
|
@ -2,8 +2,8 @@ const { dirname } = require('canonical-path');
|
|||
|
||||
module.exports = function processPackages() {
|
||||
return {
|
||||
$runAfter: ['extractDecoratedClassesProcessor'],
|
||||
$runBefore: ['computing-ids'],
|
||||
$runAfter: ['extractDecoratedClassesProcessor', 'computeStability'],
|
||||
$runBefore: ['computing-ids', 'generateKeywordsProcessor'],
|
||||
$process(docs) {
|
||||
const packageContentFiles = {};
|
||||
const packageMap = {};
|
||||
|
@ -34,6 +34,9 @@ module.exports = function processPackages() {
|
|||
doc.directives = doc.exports.filter(doc => doc.docType === 'directive');
|
||||
doc.pipes = doc.exports.filter(doc => doc.docType === 'pipe');
|
||||
doc.types = doc.exports.filter(doc => doc.docType === 'type-alias' || doc.docType === 'const');
|
||||
if (doc.exports.every(doc => !!doc.deprecated)) {
|
||||
doc.deprecated = 'all exports of this entry point are deprecated.';
|
||||
}
|
||||
}
|
||||
|
||||
// Copy over docs from the PACKAGE.md file that is used to document packages
|
||||
|
@ -58,6 +61,12 @@ module.exports = function processPackages() {
|
|||
}
|
||||
});
|
||||
|
||||
// Update package deprecation status (compared to entry point status)
|
||||
Object.keys(packageMap).forEach(key => {
|
||||
const pkg = packageMap[key];
|
||||
pkg.primary.packageDeprecated = pkg.primary.deprecated !== undefined && pkg.secondary.every(entryPoint => entryPoint.deprecated !== undefined);
|
||||
});
|
||||
|
||||
return docs;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -9,8 +9,8 @@ describe('processPackages processor', () => {
|
|||
const injector = dgeni.configureInjector();
|
||||
const processor = injector.get('processPackages');
|
||||
expect(processor.$process).toBeDefined();
|
||||
expect(processor.$runAfter).toEqual(['extractDecoratedClassesProcessor']);
|
||||
expect(processor.$runBefore).toEqual(['computing-ids']);
|
||||
expect(processor.$runAfter).toEqual(['extractDecoratedClassesProcessor', 'computeStability']);
|
||||
expect(processor.$runBefore).toEqual(['computing-ids', 'generateKeywordsProcessor']);
|
||||
});
|
||||
|
||||
it('should filter out any `package-content` docs from the collection', () => {
|
||||
|
@ -177,4 +177,110 @@ describe('processPackages processor', () => {
|
|||
{ docType: 'const', id: 'const-2' },
|
||||
]);
|
||||
});
|
||||
|
||||
it('should compute the deprecated status of each entry point', () => {
|
||||
const docs = [
|
||||
{
|
||||
fileInfo: { filePath: 'some/package-1/index' },
|
||||
docType: 'module',
|
||||
id: 'package-1',
|
||||
exports: [
|
||||
{ docType: 'class', id: 'class-1', deprecated: true },
|
||||
]
|
||||
},
|
||||
{
|
||||
fileInfo: { filePath: 'some/package-1/sub-1index' },
|
||||
docType: 'module',
|
||||
id: 'package-1/sub-1',
|
||||
exports: [
|
||||
{ docType: 'class', id: 'class-2', deprecated: true },
|
||||
]
|
||||
},
|
||||
{
|
||||
fileInfo: { filePath: 'some/package-2/index' },
|
||||
docType: 'module',
|
||||
id: 'package-2',
|
||||
exports: [
|
||||
{ docType: 'class', id: 'class-3' },
|
||||
{ docType: 'class', id: 'class-4', deprecated: true },
|
||||
]
|
||||
},
|
||||
{
|
||||
fileInfo: { filePath: 'some/package-3/index' },
|
||||
docType: 'module',
|
||||
id: 'package-3',
|
||||
exports: [
|
||||
{ docType: 'class', id: 'class-5' },
|
||||
{ docType: 'class', id: 'class-6' },
|
||||
]
|
||||
},
|
||||
];
|
||||
const processor = processorFactory();
|
||||
const newDocs = processor.$process(docs);
|
||||
|
||||
expect(newDocs[0].deprecated).toBeTruthy();
|
||||
expect(newDocs[1].deprecated).toBeTruthy();
|
||||
expect(newDocs[2].deprecated).toBeUndefined();
|
||||
expect(newDocs[3].deprecated).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should compute the deprecated status of packages', () => {
|
||||
const docs = [
|
||||
{
|
||||
fileInfo: { filePath: 'some/package-1/index' },
|
||||
docType: 'module',
|
||||
id: 'package-1',
|
||||
exports: [
|
||||
{ docType: 'class', id: 'class-1', deprecated: true },
|
||||
]
|
||||
},
|
||||
{
|
||||
fileInfo: { filePath: 'some/package-1/sub-1index' },
|
||||
docType: 'module',
|
||||
id: 'package-1/sub-1',
|
||||
exports: [
|
||||
{ docType: 'class', id: 'class-2', deprecated: true },
|
||||
]
|
||||
},
|
||||
{
|
||||
fileInfo: { filePath: 'some/package-2/index' },
|
||||
docType: 'module',
|
||||
id: 'package-2',
|
||||
exports: [
|
||||
{ docType: 'class', id: 'class-3', deprecated: true },
|
||||
]
|
||||
},
|
||||
{
|
||||
fileInfo: { filePath: 'some/package-2/sub-1index' },
|
||||
docType: 'module',
|
||||
id: 'package-2/sub-1',
|
||||
exports: [
|
||||
{ docType: 'class', id: 'class-4', deprecated: false },
|
||||
]
|
||||
},
|
||||
{
|
||||
fileInfo: { filePath: 'some/package-3/index' },
|
||||
docType: 'module',
|
||||
id: 'package-3',
|
||||
exports: [
|
||||
{ docType: 'class', id: 'class-5', deprecated: false },
|
||||
]
|
||||
},
|
||||
{
|
||||
fileInfo: { filePath: 'some/package-3/sub-1index' },
|
||||
docType: 'module',
|
||||
id: 'package-3/sub-1',
|
||||
exports: [
|
||||
{ docType: 'class', id: 'class-6', deprecated: true },
|
||||
]
|
||||
},
|
||||
];
|
||||
const processor = processorFactory();
|
||||
const newDocs = processor.$process(docs);
|
||||
expect(newDocs[0].packageDeprecated).toBe(true);
|
||||
expect(newDocs[1].packageDeprecated).toBeUndefined();
|
||||
expect(newDocs[2].packageDeprecated).toBe(false);
|
||||
expect(newDocs[3].packageDeprecated).toBeUndefined();
|
||||
expect(newDocs[4].packageDeprecated).toBe(false);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -140,7 +140,8 @@ module.exports = function generateKeywordsProcessor(log, readFilesProcessor) {
|
|||
return Object.assign({
|
||||
path: page.path,
|
||||
title: page.searchTitle,
|
||||
type: page.docType
|
||||
type: page.docType,
|
||||
deprecated: !!page.deprecated,
|
||||
}, page.searchTerms);
|
||||
});
|
||||
|
||||
|
|
|
@ -166,7 +166,8 @@ describe('generateKeywords processor', () => {
|
|||
'titleWords':'someclass',
|
||||
'headingWords':'heading some someclass',
|
||||
'keywords':'api class documentation for is someclass the',
|
||||
'members':''
|
||||
'members':'',
|
||||
'deprecated': false,
|
||||
}]
|
||||
);
|
||||
});
|
||||
|
|
|
@ -16,12 +16,15 @@
|
|||
</script>
|
||||
{% for crumb in doc.breadCrumbs %}{% if not loop.last %} {$ breadcrumbDelimiter() $} {% if crumb.path %}<a href="{$ crumb.path $}">{$ crumb.text $}</a>{% else %}{$ crumb.text $}{% endif %}{% endif %}{% endfor %}
|
||||
</div>
|
||||
{% block header %}
|
||||
<header class="api-header">
|
||||
<h1>{$ doc.name $}</h1>
|
||||
<label class="api-type-label {$ doc.docType $}">{$ doc.docType $}</label>
|
||||
{% if doc.deprecated !== undefined %}<label class="api-status-label deprecated">deprecated</label>{% endif %}
|
||||
{% if doc.security !== undefined %}<label class="api-status-label security">security</label>{% endif %}
|
||||
{% if doc.pipeOptions.pure === 'false' %}<label class="api-status-label impure-pipe">impure</label>{% endif %}
|
||||
</header>
|
||||
{% endblock %}
|
||||
<aio-toc class="embedded"></aio-toc>
|
||||
|
||||
<div class="api-body">
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
{% if doc.deprecated %}
|
||||
{% if doc.deprecated !== undefined %}
|
||||
<section class="deprecated">
|
||||
<h2>Deprecation notes</h2>
|
||||
{$ doc.deprecated | marked $}
|
||||
{$ ('**Deprecated:** ' + doc.deprecated) | marked $}
|
||||
</section>
|
||||
{% endif %}
|
|
@ -39,10 +39,11 @@
|
|||
<tr>
|
||||
<td>
|
||||
<a href="{$ item.path $}">
|
||||
<code-example language="ts" hideCopy="true" linenums="false" class="no-box">{$ item.name | escape $}</code-example>
|
||||
<code-example language="ts" hideCopy="true" linenums="false" class="no-box{% if item.deprecated !== undefined %} deprecated-api-item{% endif %}">{$ item.name | escape $}</code-example>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{% if item.deprecated !== undefined %}{$ ('**Deprecated:** ' + item.deprecated) | marked $}{% endif %}
|
||||
{$ item.shortDescription | marked $}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
|
@ -9,8 +9,13 @@
|
|||
{% for item in filteredItems %}
|
||||
<tr>
|
||||
<td><code class="code-anchor">
|
||||
<a href="{$ overridePath or item.path $}">{$ item.name $}</a></code></td>
|
||||
<td>{% if item.shortDescription %}{$ item.shortDescription | marked $}{% endif %}</td>
|
||||
<a href="{$ overridePath or item.path $}"
|
||||
{%- if item.deprecated != undefined %} class="deprecated-api-item"{% endif %}>{$ item.name $}</a></code>
|
||||
</td>
|
||||
<td>
|
||||
{% if item.deprecated !== undefined %}{$ ('**Deprecated:** ' + item.deprecated) | marked $}{% endif %}
|
||||
{% if item.shortDescription %}{$ item.shortDescription | marked $}{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
@ -18,8 +23,17 @@
|
|||
{% endif %}
|
||||
{% endmacro %}
|
||||
|
||||
{% block header %}
|
||||
<header class="api-header">
|
||||
<h1>{$ doc.name $}</h1>
|
||||
<label class="api-type-label {$ doc.docType $}">{$ doc.docType $}</label>
|
||||
{% if doc.packageDeprecated or (not doc.isPrimaryPackage and doc.deprecated !== undefined) %}<label class="api-status-label deprecated">deprecated</label>{% endif %}
|
||||
{% if doc.security !== undefined %}<label class="api-status-label security">security</label>{% endif %}
|
||||
{% if doc.pipeOptions.pure === 'false' %}<label class="api-status-label impure-pipe">impure</label>{% endif %}
|
||||
</header>
|
||||
{% endblock %}
|
||||
|
||||
{% block body -%}
|
||||
{% include "includes/deprecation.html" %}
|
||||
{$ doc.shortDescription | marked $}
|
||||
{% if doc.description %}{$ doc.description | marked $}{% endif %}
|
||||
|
||||
|
@ -32,6 +46,7 @@
|
|||
{% endif %}
|
||||
|
||||
<h2>{% if doc.isPrimaryPackage %}Primary entry{% else %}Entry{% endif %} point exports</h2>
|
||||
{% include "includes/deprecation.html" %}
|
||||
{$ listItems(doc.ngmodules, 'NgModules') $}
|
||||
{$ listItems(doc.classes, 'Classes') $}
|
||||
{$ listItems(doc.decorators, 'Decorators') $}
|
||||
|
|
Loading…
Reference in New Issue