Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9837e10
build(docs-infra): improve directive API doc templates
petebacondarwin Aug 31, 2018
cb70463
build(docs-infra): improve directive selector rendering
petebacondarwin Sep 19, 2018
35aa44f
build(docs-infra): remove class overview from directive pages
petebacondarwin Sep 19, 2018
e4bc804
build(docs-infra): rename example template variable in directive pages
petebacondarwin Sep 19, 2018
b99c868
build(docs-infra): directive inputs and outputs
petebacondarwin Sep 19, 2018
ff74726
build(docs-infra): display inherited members on directives
petebacondarwin Sep 20, 2018
ac7d689
build(docs-infra): include directives etc in class descendants lists
petebacondarwin Sep 20, 2018
fe41985
build(docs-infra): move directive macros into memberHelpers.html
petebacondarwin Sep 21, 2018
4973ddc
build(docs-infra): improve directive selector rendering
petebacondarwin Sep 21, 2018
9bbab42
build(docs-infra): pluralize NgModule(s) heading as appropriate
petebacondarwin Sep 21, 2018
d5710f0
build(docs-infra): add short description "See more" link
petebacondarwin Sep 22, 2018
84fcf21
build(docs-infra): remove unused property table heading
petebacondarwin Sep 26, 2018
562b6d3
fixup! build(docs-infra): improve directive API doc templates
petebacondarwin Sep 26, 2018
e6f728f
fixup! build(docs-infra): include directives etc in class descendants…
petebacondarwin Sep 26, 2018
5e8ebcc
fixup! build(docs-infra): move directive macros into memberHelpers.html
petebacondarwin Sep 26, 2018
28dae87
build(docs-infra): simplify property syntax rendering
petebacondarwin Sep 26, 2018
779ca39
build(docs-infra): fix formatting of entry point export table
petebacondarwin Sep 26, 2018
9210090
fixup! build(docs-infra): simplify property syntax rendering
petebacondarwin Sep 26, 2018
a5d9691
fixup! build(docs-infra): improve directive API doc templates
petebacondarwin Sep 26, 2018
d882c07
fixup! build(docs-infra): simplify property syntax rendering
petebacondarwin Sep 28, 2018
9772127
fix(docs-infra): remove unnecessary margin on short descriptions
petebacondarwin Sep 28, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 29 additions & 18 deletions aio/src/styles/2-modules/_api-pages.scss
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@
}

.method-table, .option-table, .list-table {
td > code {
background-color: inherit;
white-space: pre;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that whitespace is significant, helpers that render inside these code blocks (renderMemberSyntax or similar) need to be more "careful" to avoid:

redundant-whitespace
I manually tweaked the last entry to remove whitespace.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed in 8d644eec178199d4629d9c4fd0e55b81586635ba

}

.with-github-links {
align-items: center;
display: flex;
Expand Down Expand Up @@ -66,23 +71,6 @@
font-weight: bold;
}

.short-description {
margin: 6px 0 0 10px;
}

.properties-table {
font-size: 14px;

thead th {
&:nth-child(1) {
width: 20%;
}
&:nth-child(2) {
width: 20%;
}
}
}

.parameters-table {
margin-top: 0;
font-size: 14px;
Expand All @@ -104,7 +92,7 @@
}
}

.from-constructor, .read-only-property {
.from-constructor, .read-only-property, .write-only-property {
font-style: italic;
color: $blue;
}
Expand All @@ -113,6 +101,29 @@
list-style: none;
padding: 0;
}

.selector-list, .inherited-members-list {
ul {
padding: 0;
li {
list-style: none;
margin-bottom: 12px;
}
}
}

.selector-list {
li, a {
font-weight: bold;
i {
font-weight: normal;
}
}
}

.member-name {
font-weight: bold;
}
}

.deprecated-api-item {
Expand Down
1 change: 1 addition & 0 deletions aio/src/styles/2-modules/_code.scss
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ aio-code pre {
.sidenav-content code a {
color: inherit;
font-size: inherit;
font-weight: inherit;
}

/* PRETTY PRINTING STYLES for prettify.js. */
Expand Down
21 changes: 14 additions & 7 deletions aio/tests/e2e/api.e2e-spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,19 @@ describe('Api pages', function() {

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']);
expect(page.getDescendants('class')).toEqual([
'ControlContainer',
'AbstractFormGroupDirective',
'NgModelGroup',
'FormGroupName',
'NgForm',
'FormGroupDirective',
'FormArrayName',
'NgControl',
'NgModel',
'FormControlDirective',
'FormControlName'
]);
});

it('should show child interfaces that extend an interface', () => {
Expand All @@ -26,14 +38,9 @@ describe('Api pages', function() {
expect(page.getOverview('type-alias').getText()).toContain('type HttpEvent<T>');
});

it('should show readonly properties as getters', () => {
const page = new ApiPage('api/common/http/HttpRequest');
expect(page.getOverview('class').getText()).toContain('get body: T | null');
});

it('should not show parenthesis for getters', () => {
const page = new ApiPage('api/core/NgModuleRef');
expect(page.getOverview('class').getText()).toContain('get injector: Injector');
expect(page.getOverview('class').getText()).toContain('injector: Injector');
});

it('should show both type and initializer if set', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,64 +21,63 @@ module.exports = function matchUpDirectiveDecorators() {
if (doc.docType === 'directive') {

doc.selector = stripQuotes(doc.directiveOptions.selector);
doc.selectorArray = doc.selector ? doc.selector.split(',') : [];
doc.exportAs = stripQuotes(doc.directiveOptions.exportAs);
doc.exportAsArray = doc.exportAs ? doc.exportAs.split(',') : [];

doc.inputs = getBindingInfo(doc.directiveOptions.inputs, doc.members, 'Input');
doc.outputs = getBindingInfo(doc.directiveOptions.outputs, doc.members, 'Output');
attachBindingInfo(doc.directiveOptions.inputs, doc.members, 'Input');
attachBindingInfo(doc.directiveOptions.outputs, doc.members, 'Output');
}
});
}
};
};

function getBindingInfo(directiveBindings, members, bindingType) {
function attachBindingInfo(directiveBindings, members, bindingType) {
const bindings = {};

// Parse the bindings from the directive decorator
if (directiveBindings) {
directiveBindings.forEach(function(binding) {
const bindingInfo = parseBinding(binding);
bindings[bindingInfo.propertyName] = bindingInfo;
});
}

if (members) {
// Parse the bindings from the directive decorator
if (directiveBindings) {
directiveBindings.forEach(function(binding) {
const bindingInfo = parseBinding(bindingType, binding);
bindings[bindingInfo.propertyName] = bindingInfo;
});
}

members.forEach(function(member) {
if (member.decorators) {
// Search for members with binding decorators
member.decorators.forEach(function(decorator) {
if (decorator.name === bindingType) {
bindings[member.name] = createBindingInfo(member.name, decorator.arguments[0] || member.name);
bindings[member.name] = createBindingInfo(bindingType, member.name, decorator.arguments[0] || member.name);
}
});
}

// Now ensure that any bindings have the associated member attached
// Note that this binding could have come from the directive decorator
// Attach binding info to the member
// Note: this may have come from the `@Directive` decorator or from a property decorator such as `@Input`.
if (bindings[member.name]) {
bindings[member.name].memberDoc = member;
member.boundTo = bindings[member.name];
}
});
}

// Convert the map back to an array
return Object.keys(bindings).map(function(key) { return bindings[key]; });
}

function stripQuotes(value) {
return (typeof(value) === 'string') ? value.trim().replace(/^(['"])(.*)\1$/, '$2') : value;
}

function parseBinding(option) {
function parseBinding(bindingType, option) {
// Directive decorator bindings are of the form: "propName : bindingName" (bindingName is optional)
const optionPair = option.split(':');
const propertyName = optionPair[0].trim();
const bindingName = (optionPair[1] || '').trim() || propertyName;
return createBindingInfo(propertyName, bindingName);
return createBindingInfo(bindingType, propertyName, bindingName);
}

function createBindingInfo(propertyName, bindingName) {
function createBindingInfo(bindingType, propertyName, bindingName) {
return {
type: bindingType,
propertyName: stripQuotes(propertyName),
bindingName: stripQuotes(bindingName)
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe('matchUpDirectiveDecorators processor', () => {
});

it('should extract inputs and outputs from the directive decorator', () => {
const docs = [{
const doc = {
docType: 'directive',
directiveOptions: {
inputs: ['in1:in2', 'in3', ' in4:in5 ', ' in6 '],
Expand All @@ -70,28 +70,30 @@ describe('matchUpDirectiveDecorators processor', () => {
{ name: 'in3' },
{ name: 'in4' },
{ name: 'in6' },
{ name: 'prop1' },
{ name: 'out1' },
{ name: 'out2' },
{ name: 'out4' }
{ name: 'out4' },
{ name: 'prop2' },
]
}];
processorFactory().$process(docs);
expect(docs[0].inputs).toEqual([
{ propertyName: 'in1', bindingName: 'in2', memberDoc: docs[0].members[0] },
{ propertyName: 'in3', bindingName: 'in3', memberDoc: docs[0].members[1] },
{ propertyName: 'in4', bindingName: 'in5', memberDoc: docs[0].members[2] },
{ propertyName: 'in6', bindingName: 'in6', memberDoc: docs[0].members[3] }
]);
};

expect(docs[0].outputs).toEqual([
{ propertyName: 'out1', bindingName: 'out1', memberDoc: docs[0].members[4] },
{ propertyName: 'out2', bindingName: 'out3', memberDoc: docs[0].members[5] },
{ propertyName: 'out4', bindingName: 'out4', memberDoc: docs[0].members[6] }
processorFactory().$process([doc]);
expect(doc.members).toEqual([
{ name: 'in1', boundTo: { type: 'Input', propertyName: 'in1', bindingName: 'in2' } },
{ name: 'in3', boundTo: { type: 'Input', propertyName: 'in3', bindingName: 'in3' } },
{ name: 'in4', boundTo: { type: 'Input', propertyName: 'in4', bindingName: 'in5' } },
{ name: 'in6', boundTo: { type: 'Input', propertyName: 'in6', bindingName: 'in6' }},
{ name: 'prop1' },
{ name: 'out1', boundTo: { type: 'Output', propertyName: 'out1', bindingName: 'out1' } },
{ name: 'out2', boundTo: { type: 'Output', propertyName: 'out2', bindingName: 'out3' } },
{ name: 'out4', boundTo: { type: 'Output', propertyName: 'out4', bindingName: 'out4' }},
{ name: 'prop2' },
]);
});

it('should extract inputs and outputs from decorated properties', () => {
const docs = [{
const doc = {
docType: 'directive',
directiveOptions: {},
members: [
Expand All @@ -100,21 +102,18 @@ describe('matchUpDirectiveDecorators processor', () => {
{ name: 'c1', decorators: [{ name: 'Input', arguments: [] }] },
{ name: 'd1', decorators: [{ name: 'Output', arguments: [] }] },
]
}];
processorFactory().$process(docs);
expect(docs[0].inputs).toEqual([
{ propertyName: 'a1', bindingName: 'a2', memberDoc: docs[0].members[0] },
{ propertyName: 'c1', bindingName: 'c1', memberDoc: docs[0].members[2] }
]);

expect(docs[0].outputs).toEqual([
{ propertyName: 'b1', bindingName: 'b2', memberDoc: docs[0].members[1] },
{ propertyName: 'd1', bindingName: 'd1', memberDoc: docs[0].members[3] }
};
processorFactory().$process([doc]);
expect(doc.members).toEqual([
{ name: 'a1', decorators: [{ name: 'Input', arguments: ['a2'] }], boundTo: { type: 'Input', propertyName: 'a1', bindingName: 'a2' } },
{ name: 'b1', decorators: [{ name: 'Output', arguments: ['b2'] }], boundTo: { type: 'Output', propertyName: 'b1', bindingName: 'b2' } },
{ name: 'c1', decorators: [{ name: 'Input', arguments: [] }], boundTo: { type: 'Input', propertyName: 'c1', bindingName: 'c1' } },
{ name: 'd1', decorators: [{ name: 'Output', arguments: [] }], boundTo: { type: 'Output', propertyName: 'd1', bindingName: 'd1' } },
]);
});

it('should merge directive inputs/outputs with decorator property inputs/outputs', () => {
const docs = [{
const doc = {
docType: 'directive',
directiveOptions: {
inputs: ['a1:a2'],
Expand All @@ -126,16 +125,13 @@ describe('matchUpDirectiveDecorators processor', () => {
{ name: 'b1' },
{ name: 'b3', decorators: [{ name: 'Output', arguments: ['b4'] }] },
]
}];
processorFactory().$process(docs);
expect(docs[0].inputs).toEqual([
{ propertyName: 'a1', bindingName: 'a2', memberDoc: docs[0].members[0] },
{ propertyName: 'a3', bindingName: 'a4', memberDoc: docs[0].members[1] }
]);

expect(docs[0].outputs).toEqual([
{ propertyName: 'b1', bindingName: 'b2', memberDoc: docs[0].members[2] },
{ propertyName: 'b3', bindingName: 'b4', memberDoc: docs[0].members[3] }
};
processorFactory().$process([doc]);
expect(doc.members).toEqual([
{ name: 'a1', boundTo: { type: 'Input', propertyName: 'a1', bindingName: 'a2' } },
{ name: 'a3', boundTo: { type: 'Input', propertyName: 'a3', bindingName: 'a4' }, decorators: [{ name: 'Input', arguments: ['a4'] }] },
{ name: 'b1', boundTo: { type: 'Output', propertyName: 'b1', bindingName: 'b2' } },
{ name: 'b3', boundTo: { type: 'Output', propertyName: 'b3', bindingName: 'b4' }, decorators: [{ name: 'Output', arguments: ['b4'] }] },
]);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module.exports = function() {
return {name: 'selectors'};
};
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ module.exports = function filterBy() {
name: 'filterByPropertyValue',
process: function(list, property, value) {
if (!list) return list;
return list.filter(item => item[property] === value);
const values = Array.isArray(value) ? value : [value];
return list.filter(item => values.some(value => compare(item[property], value)));
}
};
};
};

function compare(actual, expected) {
return expected instanceof(RegExp) ? expected.test(actual) : actual === expected;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,19 @@ describe('filterByPropertyValue filter', () => {

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() {
it('should filter out items that do not match the given property string 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 }]);
});


it('should filter out items that do not match the given property regex value', function() {
expect(filter.process([{ a: '1' }, { a: '12' }, { b: '1' }, { a: 'a' }, { a: '1', b: '2' }, { a: null }, { a: undefined }], 'a', /\d/))
.toEqual([{ a: '1' }, { a: '12' }, { a: '1', b: '2' }]);
});

it('should filter out items that do not match the given array of property regex/string values', function() {
expect(filter.process([{ a: '1' }, { a: '12' }, { b: '1' }, { a: 'a' }, { a: '1', b: '2' }, { a: null }, { a: undefined }], 'a', [/\d/, 'a']))
.toEqual([{ a: '1' }, { a: '12' }, { a: 'a' }, { a: '1', b: '2' }]);
});
});
18 changes: 16 additions & 2 deletions aio/tools/transforms/angular-base-package/rendering/hasValues.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,21 @@ module.exports = function hasValues() {
name: 'hasValues',
process: function(list, property) {
if (!list || !Array.isArray(list)) return false;
return list.some(item => item[property]);
return list.some(item => readProperty(item, property.split('.'), 0));
}
};
};
};

/**
* Search deeply into an object via a collection of property segments, starting at the
* indexed segment.
*
* E.g. if `obj = { a: { b: { c: 10 }}}` then
* `readProperty(obj, ['a', 'b', 'c'], 0)` will return true;
* but
* `readProperty(obj, ['a', 'd'], 0)` will return false;
*/
function readProperty(obj, propertySegments, index) {
const value = obj[propertySegments[index]];
return !!value && (index === propertySegments.length - 1 || readProperty(value, propertySegments, index + 1));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure if it is intentional, but I just realized this will return false if the target property exists but is falsy. E.g. readProperty(obj, ['a', 'b'], 0) will return true for obj = {a: {b: 1}} but false for obj = {a: {b: 0}}.
(This is possibly the intended behavior, though.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes this is intended because we don't want "" to count as a real value - and so only truthy values are significant here.

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be safer to support not having an intermediate object at all. E.g.: readProperty({a: {}}, ['a', 'b', 'c'], 0)

Loading