diff --git a/aio/src/styles/2-modules/_label.scss b/aio/src/styles/2-modules/_label.scss index 45d225fa8a..67d546e2c8 100644 --- a/aio/src/styles/2-modules/_label.scss +++ b/aio/src/styles/2-modules/_label.scss @@ -48,6 +48,10 @@ label.raised, .api-header label { &.api-status-label { background-color: $mediumgray; + + &.impure-pipe { + background-color: $brightred; + } } &.api-type-label { diff --git a/aio/tests/e2e/api.e2e-spec.ts b/aio/tests/e2e/api.e2e-spec.ts index b09ab70e44..9ff6625eef 100644 --- a/aio/tests/e2e/api.e2e-spec.ts +++ b/aio/tests/e2e/api.e2e-spec.ts @@ -51,6 +51,12 @@ describe('Api pages', function() { expect(page.getSection('instance-properties').isPresent()).toBe(false); }); + it('should show "impure" badge if pipe is not pure', () => { + const page = new ApiPage('api/common/AsyncPipe'); + const impureBadge = page.getBadge('impure-pipe'); + expect(impureBadge.isPresent()).toBe(true); + }); + it('should show links to github', () => { const page = new ApiPage('api/core/EventEmitter'); /* tslint:disable:max-line-length */ diff --git a/aio/tests/e2e/api.po.ts b/aio/tests/e2e/api.po.ts index dcf9f0371e..b2bb4a3b15 100644 --- a/aio/tests/e2e/api.po.ts +++ b/aio/tests/e2e/api.po.ts @@ -35,4 +35,8 @@ export class ApiPage extends SitePage { getSection(cls) { return element(by.css(`section.${cls}`)); } + + getBadge(cls) { + return element(by.css('.api-status-label.' + cls)); + } } diff --git a/aio/tools/transforms/angular-api-package/index.js b/aio/tools/transforms/angular-api-package/index.js index 5ead537ca4..1c26fec515 100644 --- a/aio/tools/transforms/angular-api-package/index.js +++ b/aio/tools/transforms/angular-api-package/index.js @@ -21,6 +21,7 @@ module.exports = new Package('angular-api', [basePackage, typeScriptPackage]) .processor(require('./processors/addNotYetDocumentedProperty')) .processor(require('./processors/mergeDecoratorDocs')) .processor(require('./processors/extractDecoratedClasses')) + .processor(require('./processors/extractPipeParams')) .processor(require('./processors/matchUpDirectiveDecorators')) .processor(require('./processors/addMetadataAliases')) .processor(require('./processors/computeApiBreadCrumbs')) diff --git a/aio/tools/transforms/angular-api-package/processors/extractPipeParams.js b/aio/tools/transforms/angular-api-package/processors/extractPipeParams.js new file mode 100644 index 0000000000..441377e019 --- /dev/null +++ b/aio/tools/transforms/angular-api-package/processors/extractPipeParams.js @@ -0,0 +1,19 @@ +module.exports = function extractPipeParams(createDocMessage) { + return { + $runAfter: ['extractDecoratedClassesProcessor'], + $runBefore: ['docs-processed'], + $process(docs) { + docs.forEach(doc => { + if (doc.docType === 'pipe') { + const transformFn = doc.members && doc.members.find(member => member.name === 'transform'); + if (!transformFn) { + throw new Error(createDocMessage('Missing `transform` method - pipes must implement PipeTransform interface', doc)); + } + doc.pipeName = doc.pipeOptions.name.replace(/^["']|["']$/g, ''); + doc.valueParam = transformFn.parameterDocs[0]; + doc.pipeParams = transformFn.parameterDocs.slice(1); + } + }); + } + }; +}; diff --git a/aio/tools/transforms/angular-api-package/processors/extractPipeParams.spec.js b/aio/tools/transforms/angular-api-package/processors/extractPipeParams.spec.js new file mode 100644 index 0000000000..75448ba477 --- /dev/null +++ b/aio/tools/transforms/angular-api-package/processors/extractPipeParams.spec.js @@ -0,0 +1,61 @@ +const testPackage = require('../../helpers/test-package'); +const Dgeni = require('dgeni'); + +describe('extractPipeParams processor', () => { + let processor; + beforeEach(() => { + const dgeni = new Dgeni([testPackage('angular-api-package')]); + const injector = dgeni.configureInjector(); + processor = injector.get('extractPipeParams'); + }); + + it('should be available on the injector', () => { + expect(processor.$process).toBeDefined(); + }); + + it('should run before the correct processor', () => { + expect(processor.$runBefore).toEqual(['docs-processed']); + }); + + it('should run after the correct processor', () => { + expect(processor.$runAfter).toEqual(['extractDecoratedClassesProcessor']); + }); + + it('should throw an error if the pipe document does not contain a `transform` method', () => { + expect(() => processor.$process([{ docType: 'pipe' }])).toThrowError('Missing `transform` method - pipes must implement PipeTransform interface - doc (pipe) '); + expect(() => processor.$process([{ docType: 'pipe', members: [] }])).toThrowError('Missing `transform` method - pipes must implement PipeTransform interface - doc (pipe) '); + expect(() => processor.$process([{ docType: 'pipe', members: [ { name: 'notTransform' }] }])).toThrowError('Missing `transform` method - pipes must implement PipeTransform interface - doc (pipe) '); + }); + + it('should extract the pipe name', () => { + const docs = [ { docType: 'pipe', pipeOptions: { name: 'testPipe' }, members: [ { name: 'transform', parameterDocs: [] }] } ]; + processor.$process(docs); + expect(docs[0].pipeName).toEqual('testPipe'); + }); + + it('should extract the value parameter', () => { + const valueParam = {}; + const pipeParam1 = {}; + const pipeParam2 = {}; + const docs = [ { docType: 'pipe', pipeOptions: { name: 'testPipe' }, members: [ + { name: 'transform', parameterDocs: [valueParam, pipeParam1, pipeParam2] } + ] } ]; + processor.$process(docs); + expect(docs[0].valueParam).toBe(valueParam); + }); + + it('should extract the pipe parameters', () => { + const valueParam = {}; + const pipeParam1 = {}; + const pipeParam2 = {}; + const docs = [ { docType: 'pipe', pipeOptions: { name: 'testPipe' }, members: [ + { name: 'transform', parameterDocs: [valueParam, pipeParam1, pipeParam2] } + ] } ]; + processor.$process(docs); + expect(docs[0].pipeParams.length).toEqual(2); + expect(docs[0].pipeParams[0]).toBe(pipeParam1); + expect(docs[0].pipeParams[1]).toBe(pipeParam2); + }); +}); + + diff --git a/aio/tools/transforms/templates/api/base.template.html b/aio/tools/transforms/templates/api/base.template.html index 69514248fd..7d93b9e861 100644 --- a/aio/tools/transforms/templates/api/base.template.html +++ b/aio/tools/transforms/templates/api/base.template.html @@ -25,6 +25,7 @@ {% if doc.deprecated !== undefined %}{% endif %} {% if doc.experimental !== undefined %}{% endif %} {% if doc.stable !== undefined %}{% endif %} + {% if doc.pipeOptions.pure === 'false' %}{% endif %} diff --git a/aio/tools/transforms/templates/api/includes/pipe-overview.html b/aio/tools/transforms/templates/api/includes/pipe-overview.html new file mode 100644 index 0000000000..f6bf09eb6a --- /dev/null +++ b/aio/tools/transforms/templates/api/includes/pipe-overview.html @@ -0,0 +1,21 @@ +{% import "lib/memberHelpers.html" as memberHelpers -%} +{% import "lib/paramList.html" as params -%} + +
+ {{ {$ doc.valueParam.name $}_expression | {$ doc.pipeName $} + {%- for param in doc.pipeParams %} + {%- if param.isOptional or param.defaultValue !== undefined %} [{% endif %} : {$ param.name $} + {%- endfor %} + {%- for param in doc.pipeParams %} + {%- if param.isOptional or param.defaultValue !== undefined %} ]{% endif %} + {%- endfor %} }} + + {% if doc.valueParam.type %} +

Input Value

+ {$ params.renderParameters([doc.valueParam], 'pipe-parameters', 'pipe-parameter', true) $} + {% endif %} + {% if doc.pipeParams.length %} +

Parameters

+ {$ params.renderParameters(doc.pipeParams, 'pipe-parameters', 'pipe-parameter', true) $} + {% endif %} +
diff --git a/aio/tools/transforms/templates/api/lib/paramList.html b/aio/tools/transforms/templates/api/lib/paramList.html index 0dab24e472..10fb182c84 100644 --- a/aio/tools/transforms/templates/api/lib/paramList.html +++ b/aio/tools/transforms/templates/api/lib/paramList.html @@ -11,19 +11,20 @@ {%- if returnType %}: {$ returnType | escape $}{% endif -%} {%- endmacro -%} -{%- macro renderParameters(parameters, containerClass, parameterClass) -%} +{%- macro renderParameters(parameters, containerClass, parameterClass, showType) -%} {%- if parameters.length -%} {% for parameter in parameters %} + {% if showType %}{% endif %} diff --git a/aio/tools/transforms/templates/api/pipe.template.html b/aio/tools/transforms/templates/api/pipe.template.html index 0bea954ec7..f4ad4d77d3 100644 --- a/aio/tools/transforms/templates/api/pipe.template.html +++ b/aio/tools/transforms/templates/api/pipe.template.html @@ -1,5 +1,8 @@ {% extends 'export-base.template.html' -%} +{% block overview %} +{% include "includes/pipe-overview.html" %} +{% endblock %} {% block details %} {% include "includes/description.html" %} {% endblock %}
{$ parameter.name $}{$ parameter.type $} {% marked %} {% if parameter.isOptional or parameter.defaultValue !== undefined %}Optional. Default is `{$ parameter.defaultValue === undefined and 'undefined' or parameter.defaultValue $}`.{% endif %} {% if parameter.description | trim %}{$ parameter.description $} - {% elseif parameter.type %}{$ parameter.type $} + {% elseif not showType and parameter.type %}

Type: {$ parameter.type $}.

{% endif %} {% endmarked %}