feat(docs-infra): enable filtering by package on API list page (#24631)

PR Close #24631
This commit is contained in:
Pete Bacon Darwin 2018-07-01 21:23:45 +01:00 committed by Matias Niemelä
parent d8c828c9b1
commit 74b250b146
4 changed files with 46 additions and 43 deletions

View File

@ -21,10 +21,10 @@
<article class="api-list-container l-content-small docs-content">
<div *ngFor="let section of filteredSections | async" >
<h2><a [href]="section.path">{{section.title}}</a></h2>
<ul class="api-list">
<h2 *ngIf="section.items"><a [href]="section.path">{{section.title}}</a></h2>
<ul class="api-list" *ngIf="section.items?.length">
<ng-container *ngFor="let item of section.items">
<li *ngIf="item.show" class="api-item">
<li class="api-item">
<a [href]="item.path"><span class="symbol {{item.docType}}"></span> {{item.title}}</a>
</li>
</ng-container>

View File

@ -36,18 +36,13 @@ describe('ApiListComponent', () => {
*/
function expectFilteredResult(label: string, itemTest: (item: ApiItem) => boolean) {
component.filteredSections.subscribe(filtered => {
let badItem: ApiItem|undefined;
filtered = filtered.filter(section => section.items);
expect(filtered.length).toBeGreaterThan(0, 'expected something');
expect(filtered.every(section => section.items.every(
item => {
const ok = item.show === itemTest(item);
if (!ok) { badItem = item; }
return ok;
}
))).toBe(true, `${label} fail: ${JSON.stringify(badItem, null, 2)}`);
expect(filtered.every(section => section.items!.every(itemTest))).toBe(true, label);
});
}
describe('#filteredSections', () => {
beforeEach(() => {
@ -65,34 +60,45 @@ describe('ApiListComponent', () => {
expectFilteredResult('query: class', item => /class/.test(item.name));
});
it('item.show should be true for every item in section when query matches section name', () => {
it('items should be an array for every item in section when query matches section name', () => {
component.setQuery('core');
component.filteredSections.subscribe(filtered => {
filtered = filtered.filter(section => Array.isArray(section.items));
expect(filtered.length).toBe(1, 'only one section');
expect(filtered[0].name).toBe('core');
expect(filtered[0].items.every(item => !!item.show)).toBe(true, 'all core items shown');
expect(filtered[0].items).toEqual(sections.find(section => section.name === 'core')!.items);
});
});
it('item.show should be true for items with selected status', () => {
component.setStatus({value: 'stable', title: 'Stable'});
expectFilteredResult('status: stable', item => item.stability === 'stable');
describe('section.items', () => {
it('should null if there are no matching items and the section itself does not match', () => {
component.setQuery('core');
component.filteredSections.subscribe(filtered => {
const commonSection = filtered.find(section => section.name === 'common')!;
expect(commonSection.items).toBe(null);
});
});
it('should be visible if they have the selected stability status', () => {
component.setStatus({value: 'stable', title: 'Stable'});
expectFilteredResult('status: stable', item => item.stability === 'stable');
});
it('should be visible if they have the selected security status', () => {
component.setStatus({value: 'security-risk', title: 'Security Risk'});
expectFilteredResult('status: security-risk', item => item.securityRisk);
});
it('should be visible if they match the selected API type', () => {
component.setType({value: 'class', title: 'Class'});
expectFilteredResult('type: class', item => item.docType === 'class');
});
});
it('item.show should be true for items with "security-risk" status when selected', () => {
component.setStatus({value: 'security-risk', title: 'Security Risk'});
expectFilteredResult('status: security-risk', item => item.securityRisk);
});
it('item.show should be true for items of selected type', () => {
component.setType({value: 'class', title: 'Class'});
expectFilteredResult('type: class', item => item.docType === 'class');
});
it('should have no sections and no items when no match', () => {
it('should have no sections and no items visible when there is no match', () => {
component.setQuery('fizbuzz');
component.filteredSections.subscribe(filtered => {
expect(filtered.length).toBe(0, 'expected no sections');
expect(filtered.some(section => !!section.items)).toBeFalsy();
});
});
});
@ -108,9 +114,11 @@ describe('ApiListComponent', () => {
fixture.detectChanges();
component.filteredSections.subscribe(filtered => {
filtered = filtered.filter(s => s.items);
console.log(filtered);
expect(filtered.length).toBe(1, 'sections');
expect(filtered[0].name).toBe(section, 'section name');
const items = filtered[0].items.filter(i => i.show);
const items = filtered[0].items!;
expect(items.length).toBe(1, 'items');
const item = items[0];

View File

@ -49,7 +49,8 @@ export class ApiListComponent implements OnInit {
{ value: 'function', title: 'Function' },
{ value: 'interface', title: 'Interface' },
{ value: 'pipe', title: 'Pipe'},
{ value: 'type-alias', title: 'Type Alias' }
{ value: 'type-alias', title: 'Type alias' },
{ value: 'package', title: 'Package'}
];
statuses: Option[] = [
@ -71,7 +72,8 @@ export class ApiListComponent implements OnInit {
this.apiService.sections,
this.criteriaSubject,
(sections, criteria) => {
return sections.filter(section => this.filterSection(section, criteria));
return sections
.map(section => ({ ...section, items: this.filterSection(section, criteria) }));
}
);
@ -107,13 +109,8 @@ export class ApiListComponent implements OnInit {
//////// Private //////////
private filterSection(section: ApiSection, { query, status, type }: SearchCriteria) {
let showSection = false;
section.items.forEach(item => {
item.show = matchesType() && matchesStatus() && matchesQuery();
// show section if any of its items will be shown
showSection = showSection || item.show;
const items = section.items!.filter(item => {
return matchesType() && matchesStatus() && matchesQuery();
function matchesQuery() {
return !query ||
@ -132,8 +129,8 @@ export class ApiListComponent implements OnInit {
}
});
return showSection;
// If there are no items we still return an empty array if the section name matches and the type is 'package'
return items.length ? items : (type === 'package' && (!query || section.name.indexOf(query) !== -1)) ? [] : null;
}
// Get initial search criteria from URL search params

View File

@ -14,15 +14,13 @@ export interface ApiItem {
docType: string;
stability: string;
securityRisk: boolean;
show?: boolean;
}
export interface ApiSection {
path: string;
name: string;
title: string;
items: ApiItem[];
items: ApiItem[]|null;
}
@Injectable()