From 026b60cd70d5afcb16e5b28fb79b970bf3cebae9 Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 17 Sep 2018 17:37:18 +0100 Subject: [PATCH] build(docs-infra): expose deprecated status on items more clearly (#25750) PR Close #25750 --- aio/src/app/app.component.spec.ts | 2 +- .../api/api-list.component.html | 7 +- .../api/api-list.component.spec.ts | 16 +- .../custom-elements/api/api.service.spec.ts | 17 ++- .../app/custom-elements/api/api.service.ts | 8 +- aio/src/app/search/interfaces.ts | 1 + .../search-results.component.html | 6 +- .../search-results.component.spec.ts | 141 +++++++++++------- .../search-results.component.ts | 36 ++++- aio/src/styles/2-modules/_api-list.scss | 4 + aio/src/styles/2-modules/_api-pages.scss | 4 + aio/src/styles/2-modules/_label.scss | 6 +- aio/src/styles/_constants.scss | 3 +- aio/tests/e2e/app.e2e-spec.ts | 2 +- .../processors/processPackages.js | 13 +- .../processors/processPackages.spec.js | 110 +++++++++++++- .../processors/generateKeywords.js | 3 +- .../processors/generateKeywords.spec.js | 3 +- .../templates/api/base.template.html | 3 + .../templates/api/includes/deprecation.html | 5 +- .../templates/api/ngmodule.template.html | 3 +- .../templates/api/package.template.html | 21 ++- 22 files changed, 321 insertions(+), 93 deletions(-) diff --git a/aio/src/app/app.component.spec.ts b/aio/src/app/app.component.spec.ts index 5b646af0a5..b27951a1ff 100644 --- a/aio/src/app/app.component.spec.ts +++ b/aio/src/app/app.component.spec.ts @@ -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 }); diff --git a/aio/src/app/custom-elements/api/api-list.component.html b/aio/src/app/custom-elements/api/api-list.component.html index d998a500ca..2ae0f12420 100644 --- a/aio/src/app/custom-elements/api/api-list.component.html +++ b/aio/src/app/custom-elements/api/api-list.component.html @@ -22,11 +22,14 @@
-

{{section.title}}

+

{{section.title}}

diff --git a/aio/src/app/custom-elements/api/api-list.component.spec.ts b/aio/src/app/custom-elements/api/api-list.component.spec.ts index 12cbf13056..0c7a18a667 100644 --- a/aio/src/app/custom-elements/api/api-list.component.spec.ts +++ b/aio/src/app/custom-elements/api/api-list.component.spec.ts @@ -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, } ] } diff --git a/aio/src/app/custom-elements/api/api.service.spec.ts b/aio/src/app/custom-elements/api/api.service.spec.ts index 253acd57de..a815e0d8a0 100644 --- a/aio/src/app/custom-elements/api/api.service.spec.ts +++ b/aio/src/app/custom-elements/api/api.service.spec.ts @@ -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); diff --git a/aio/src/app/custom-elements/api/api.service.ts b/aio/src/app/custom-elements/api/api.service.ts index f90599c8af..fdfb54cfcf 100644 --- a/aio/src/app/custom-elements/api/api.service.ts +++ b/aio/src/app/custom-elements/api/api.service.ts @@ -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) { } diff --git a/aio/src/app/search/interfaces.ts b/aio/src/app/search/interfaces.ts index c0c18d5c98..691a17bb7f 100644 --- a/aio/src/app/search/interfaces.ts +++ b/aio/src/app/search/interfaces.ts @@ -9,6 +9,7 @@ export interface SearchResult { type: string; titleWords: string; keywords: string; + deprecated: boolean; } export interface SearchArea { diff --git a/aio/src/app/shared/search-results/search-results.component.html b/aio/src/app/shared/search-results/search-results.component.html index fedce7b829..84de689807 100644 --- a/aio/src/app/shared/search-results/search-results.component.html +++ b/aio/src/app/shared/search-results/search-results.component.html @@ -9,14 +9,16 @@ diff --git a/aio/src/app/shared/search-results/search-results.component.spec.ts b/aio/src/app/shared/search-results/search-results.component.spec.ts index 602763b9d7..edd720c10c 100644 --- a/aio/src/app/shared/search-results/search-results.component.spec.ts +++ b/aio/src/app/shared/search-results/search-results.component.spec.ts @@ -5,41 +5,59 @@ import { SearchResult } from 'app/search/interfaces'; import { SearchResultsComponent } from './search-results.component'; describe('SearchResultsComponent', () => { + let component: SearchResultsComponent; let fixture: ComponentFixture; + 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(); diff --git a/aio/src/app/shared/search-results/search-results.component.ts b/aio/src/app/shared/search-results/search-results.component.ts index 9423fa9556..6b401431b2 100644 --- a/aio/src/app/shared/search-results/search-results.component.ts +++ b/aio/src/app/shared/search-results/search-results.component.ts @@ -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; } diff --git a/aio/src/styles/2-modules/_api-list.scss b/aio/src/styles/2-modules/_api-list.scss index d3428e1545..f9e241f945 100644 --- a/aio/src/styles/2-modules/_api-list.scss +++ b/aio/src/styles/2-modules/_api-list.scss @@ -198,6 +198,10 @@ aio-api-list { background: $blue-grey-50; color: $blue-500; } + + &.deprecated-api-item { + text-decoration: line-through; + } } } } diff --git a/aio/src/styles/2-modules/_api-pages.scss b/aio/src/styles/2-modules/_api-pages.scss index 8443bf5bca..81e39b50a4 100644 --- a/aio/src/styles/2-modules/_api-pages.scss +++ b/aio/src/styles/2-modules/_api-pages.scss @@ -114,3 +114,7 @@ padding: 0; } } + +.deprecated-api-item { + text-decoration: line-through; +} diff --git a/aio/src/styles/2-modules/_label.scss b/aio/src/styles/2-modules/_label.scss index 4dfdacb7a7..17b2756af8 100644 --- a/aio/src/styles/2-modules/_label.scss +++ b/aio/src/styles/2-modules/_label.scss @@ -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; } } diff --git a/aio/src/styles/_constants.scss b/aio/src/styles/_constants.scss index c7d30e4b70..df2d4ea994 100755 --- a/aio/src/styles/_constants.scss +++ b/aio/src/styles/_constants.scss @@ -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', diff --git a/aio/tests/e2e/app.e2e-spec.ts b/aio/tests/e2e/app.e2e-spec.ts index 783c3f1a7a..40f2682482 100644 --- a/aio/tests/e2e/app.e2e-spec.ts +++ b/aio/tests/e2e/app.e2e-spec.ts @@ -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'); }); }); diff --git a/aio/tools/transforms/angular-api-package/processors/processPackages.js b/aio/tools/transforms/angular-api-package/processors/processPackages.js index e4124155f7..6e19cd5da9 100644 --- a/aio/tools/transforms/angular-api-package/processors/processPackages.js +++ b/aio/tools/transforms/angular-api-package/processors/processPackages.js @@ -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; } }; diff --git a/aio/tools/transforms/angular-api-package/processors/processPackages.spec.js b/aio/tools/transforms/angular-api-package/processors/processPackages.spec.js index 3692ab1b4c..ad6327654b 100644 --- a/aio/tools/transforms/angular-api-package/processors/processPackages.spec.js +++ b/aio/tools/transforms/angular-api-package/processors/processPackages.spec.js @@ -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); + }); }); diff --git a/aio/tools/transforms/angular-base-package/processors/generateKeywords.js b/aio/tools/transforms/angular-base-package/processors/generateKeywords.js index 4e9aefa503..38236978c2 100644 --- a/aio/tools/transforms/angular-base-package/processors/generateKeywords.js +++ b/aio/tools/transforms/angular-base-package/processors/generateKeywords.js @@ -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); }); diff --git a/aio/tools/transforms/angular-base-package/processors/generateKeywords.spec.js b/aio/tools/transforms/angular-base-package/processors/generateKeywords.spec.js index 1bd96155ce..2bf490da9a 100644 --- a/aio/tools/transforms/angular-base-package/processors/generateKeywords.spec.js +++ b/aio/tools/transforms/angular-base-package/processors/generateKeywords.spec.js @@ -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, }] ); }); diff --git a/aio/tools/transforms/templates/api/base.template.html b/aio/tools/transforms/templates/api/base.template.html index 47b6f3b67e..5c37d1e765 100644 --- a/aio/tools/transforms/templates/api/base.template.html +++ b/aio/tools/transforms/templates/api/base.template.html @@ -16,12 +16,15 @@ {% for crumb in doc.breadCrumbs %}{% if not loop.last %} {$ breadcrumbDelimiter() $} {% if crumb.path %}{$ crumb.text $}{% else %}{$ crumb.text $}{% endif %}{% endif %}{% endfor %}
+ {% block header %}

{$ doc.name $}

+ {% if doc.deprecated !== undefined %}{% endif %} {% if doc.security !== undefined %}{% endif %} {% if doc.pipeOptions.pure === 'false' %}{% endif %}
+ {% endblock %}
diff --git a/aio/tools/transforms/templates/api/includes/deprecation.html b/aio/tools/transforms/templates/api/includes/deprecation.html index 8286ab4148..2cf5b963f2 100644 --- a/aio/tools/transforms/templates/api/includes/deprecation.html +++ b/aio/tools/transforms/templates/api/includes/deprecation.html @@ -1,6 +1,5 @@ -{% if doc.deprecated %} +{% if doc.deprecated !== undefined %}
-

Deprecation notes

- {$ doc.deprecated | marked $} + {$ ('**Deprecated:** ' + doc.deprecated) | marked $}
{% endif %} \ No newline at end of file diff --git a/aio/tools/transforms/templates/api/ngmodule.template.html b/aio/tools/transforms/templates/api/ngmodule.template.html index 32e1d74d12..b87e2526c4 100644 --- a/aio/tools/transforms/templates/api/ngmodule.template.html +++ b/aio/tools/transforms/templates/api/ngmodule.template.html @@ -39,10 +39,11 @@ - {$ item.name | escape $} + {$ item.name | escape $} + {% if item.deprecated !== undefined %}{$ ('**Deprecated:** ' + item.deprecated) | marked $}{% endif %} {$ item.shortDescription | marked $} diff --git a/aio/tools/transforms/templates/api/package.template.html b/aio/tools/transforms/templates/api/package.template.html index 035415d27b..73525bfeea 100644 --- a/aio/tools/transforms/templates/api/package.template.html +++ b/aio/tools/transforms/templates/api/package.template.html @@ -9,8 +9,13 @@ {% for item in filteredItems %} - {$ item.name $} - {% if item.shortDescription %}{$ item.shortDescription | marked $}{% endif %} + {$ item.name $} + + + {% if item.deprecated !== undefined %}{$ ('**Deprecated:** ' + item.deprecated) | marked $}{% endif %} + {% if item.shortDescription %}{$ item.shortDescription | marked $}{% endif %} + {% endfor %} @@ -18,8 +23,17 @@ {% endif %} {% endmacro %} +{% block header %} +
+

{$ doc.name $}

+ + {% if doc.packageDeprecated or (not doc.isPrimaryPackage and doc.deprecated !== undefined) %}{% endif %} + {% if doc.security !== undefined %}{% endif %} + {% if doc.pipeOptions.pure === 'false' %}{% endif %} +
+{% endblock %} + {% block body -%} - {% include "includes/deprecation.html" %} {$ doc.shortDescription | marked $} {% if doc.description %}{$ doc.description | marked $}{% endif %} @@ -32,6 +46,7 @@ {% endif %}

{% if doc.isPrimaryPackage %}Primary entry{% else %}Entry{% endif %} point exports

+ {% include "includes/deprecation.html" %} {$ listItems(doc.ngmodules, 'NgModules') $} {$ listItems(doc.classes, 'Classes') $} {$ listItems(doc.decorators, 'Decorators') $}