diff --git a/aio/e2e/api.e2e-spec.ts b/aio/e2e/api.e2e-spec.ts new file mode 100644 index 0000000000..df588e9480 --- /dev/null +++ b/aio/e2e/api.e2e-spec.ts @@ -0,0 +1,24 @@ +import { ApiPage } from './api.po'; + +describe('Api pages', function() { + it('should show direct subclasses of a class', () => { + const page = new ApiPage('api/forms/AbstractControlDirective'); + // We must use `as any` (here and below) because of broken typings for jasmine + expect(page.getDescendants('class', true)).toEqual(['ControlContainer', 'NgControl'] as any); + }); + + it('should show direct and indirect subclasses of a class', () => { + const page = new ApiPage('api/forms/AbstractControlDirective'); + expect(page.getDescendants('class')).toEqual(['ControlContainer', 'AbstractFormGroupDirective', 'NgControl'] as any); + }); + + it('should show child interfaces that extend an interface', () => { + const page = new ApiPage('api/forms/Validator'); + expect(page.getDescendants('interface')).toEqual(['AsyncValidator'] as any); + }); + + it('should show classes that implement an interface', () => { + const page = new ApiPage('api/animations/AnimationPlayer'); + expect(page.getDescendants('class')).toEqual(['NoopAnimationPlayer', 'MockAnimationPlayer'] as any); + }); +}); diff --git a/aio/e2e/api.po.ts b/aio/e2e/api.po.ts new file mode 100644 index 0000000000..f6cd527244 --- /dev/null +++ b/aio/e2e/api.po.ts @@ -0,0 +1,30 @@ +import { element, by } from 'protractor'; +import { SitePage } from './app.po'; + +export class ApiPage extends SitePage { + constructor(url: string) { + super(); + this.navigateTo(url); + } + + getDescendants(docType: string, onlyDirect = false) { + // This selector is horrible because we have potentially recursive HTML lists + // + // ul + // li + // code + // ul + // li + // code + // ul + // li + // code + // li + // code + // + // and we want to be able to pull out the code elements from only the first level + // if `onlyDirect` is set to `true`. + const selector = `.descendants.${docType} ${onlyDirect ? '>' : ''} li > :not(ul) code`; + return element.all(by.css(selector)).map(item => item.getText()); + } +} diff --git a/aio/package.json b/aio/package.json index 3d7cdd42e7..8e79c001d5 100644 --- a/aio/package.json +++ b/aio/package.json @@ -82,7 +82,7 @@ "concurrently": "^3.4.0", "cross-spawn": "^5.1.0", "dgeni": "^0.4.7", - "dgeni-packages": "^0.21.2", + "dgeni-packages": "^0.21.3", "entities": "^1.1.1", "eslint": "^3.19.0", "eslint-plugin-jasmine": "^2.2.0", diff --git a/aio/tools/transforms/angular-base-package/rendering/filterByPropertyValue.js b/aio/tools/transforms/angular-base-package/rendering/filterByPropertyValue.js new file mode 100644 index 0000000000..9b3c3b6fca --- /dev/null +++ b/aio/tools/transforms/angular-base-package/rendering/filterByPropertyValue.js @@ -0,0 +1,9 @@ +module.exports = function filterBy() { + return { + name: 'filterByPropertyValue', + process: function(list, property, value) { + if (!list) return list; + return list.filter(item => item[property] === value); + } + }; +}; \ No newline at end of file diff --git a/aio/tools/transforms/angular-base-package/rendering/filterByPropertyValue.spec.js b/aio/tools/transforms/angular-base-package/rendering/filterByPropertyValue.spec.js new file mode 100644 index 0000000000..d2c5823bb5 --- /dev/null +++ b/aio/tools/transforms/angular-base-package/rendering/filterByPropertyValue.spec.js @@ -0,0 +1,14 @@ +const factory = require('./filterByPropertyValue'); + +describe('filterByPropertyValue filter', () => { + let filter; + + beforeEach(function() { filter = factory(); }); + + it('should be called "filterByPropertyValue"', function() { expect(filter.name).toEqual('filterByPropertyValue'); }); + + it('should filter out items that do not match the given property value', function() { + expect(filter.process([{ a: 1 }, { a: 2 }, { b: 1 }, { a: 1, b: 2 }, { a: null }, { a: undefined }], 'a', 1)) + .toEqual([{ a: 1 }, { a: 1, b: 2 }]); + }); +}); \ No newline at end of file diff --git a/aio/tools/transforms/templates/api/class.template.html b/aio/tools/transforms/templates/api/class.template.html index 28f952c1b2..8394fb683f 100644 --- a/aio/tools/transforms/templates/api/class.template.html +++ b/aio/tools/transforms/templates/api/class.template.html @@ -1,4 +1,5 @@ {% import "lib/memberHelpers.html" as memberHelpers -%} +{% import "lib/descendants.html" as descendants -%} {% import "lib/paramList.html" as params -%} {% extends 'export-base.template.html' -%} @@ -6,6 +7,7 @@ {% block details %} {% block additional %}{% endblock %} {% include "includes/description.html" %} +{$ descendants.renderDescendants(doc, 'class', 'Subclasses') $} {$ memberHelpers.renderMemberDetails(doc.statics, 'static-members', 'static-member', 'Static Members') $} {% if doc.constructorDoc %}{$ memberHelpers.renderMemberDetails([doc.constructorDoc], 'constructors', 'constructor', 'Constructor') $}{% endif %} {$ memberHelpers.renderMemberDetails(doc.members, 'instance-members', 'instance-member', 'Members') $} diff --git a/aio/tools/transforms/templates/api/interface.template.html b/aio/tools/transforms/templates/api/interface.template.html index e4d99bc238..52327006e4 100644 --- a/aio/tools/transforms/templates/api/interface.template.html +++ b/aio/tools/transforms/templates/api/interface.template.html @@ -1,9 +1,12 @@ {% import "lib/paramList.html" as params -%} {% import "lib/memberHelpers.html" as memberHelper -%} +{% import "lib/descendants.html" as descendants -%} {% extends 'export-base.template.html' -%} {% block overview %}{% include "includes/interface-overview.html" %}{% endblock %} {% block details %} {% include "includes/description.html" %} + {$ descendants.renderDescendants(doc, 'interface', 'Child Interfaces') $} + {$ descendants.renderDescendants(doc, 'class', 'Class Implementations') $} {$ memberHelper.renderMemberDetails(doc.members, 'instance-members', 'instance-member', 'Members') $} {% endblock %} diff --git a/aio/tools/transforms/templates/api/lib/descendants.html b/aio/tools/transforms/templates/api/lib/descendants.html new file mode 100644 index 0000000000..cdf15d0be8 --- /dev/null +++ b/aio/tools/transforms/templates/api/lib/descendants.html @@ -0,0 +1,14 @@ +{% macro renderDescendants(doc, docType, title='', recursed=false) %} + {% set descendants = doc.descendants | filterByPropertyValue('docType', docType) %} + {% if descendants.length %} + {% if title %}

{$ title $}

{% endif %} + + {% endif %} +{% endmacro %} \ No newline at end of file diff --git a/aio/yarn.lock b/aio/yarn.lock index b2bd011f07..12ad34e9bf 100644 --- a/aio/yarn.lock +++ b/aio/yarn.lock @@ -2002,9 +2002,9 @@ devtools-timeline-model@1.1.6: chrome-devtools-frontend "1.0.401423" resolve "1.1.7" -dgeni-packages@^0.21.2: - version "0.21.2" - resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.21.2.tgz#b031194176507b7c7d1c9735ea14664970763866" +dgeni-packages@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/dgeni-packages/-/dgeni-packages-0.21.3.tgz#49d5264400cdd8c8a2f66040267e38c099d540f4" dependencies: canonical-path "0.0.2" catharsis "^0.8.1"