feat(aio): sort search results by area/title

Display area names in all caps
Exclude results with no title because they don’t show & can’t be clicked.
Should find these and give their docs a title.
This commit is contained in:
Ward Bell 2017-04-15 00:04:34 -07:00 committed by Pete Bacon Darwin
parent 6649743a2d
commit dfc81c3dab
3 changed files with 51 additions and 8 deletions

View File

@ -42,30 +42,63 @@ describe('SearchResultsComponent', () => {
searchResults.next({ query: '', results: results}); searchResults.next({ query: '', results: results});
expect(currentAreas).toEqual([ expect(currentAreas).toEqual([
{ name: 'api', pages: [
{ path: 'api/c', title: 'API C', type: 'class', keywords: '', titleWords: '' }
] },
{ name: 'guide', pages: [ { name: 'guide', pages: [
{ path: 'guide/a', title: 'Guide A', type: 'content', keywords: '', titleWords: '' }, { path: 'guide/a', title: 'Guide A', type: 'content', keywords: '', titleWords: '' },
{ path: 'guide/b', title: 'Guide B', type: 'content', keywords: '', titleWords: '' }, { path: 'guide/b', title: 'Guide B', type: 'content', keywords: '', titleWords: '' },
{ path: 'guide/b/c', title: 'Guide B - C', type: 'content', keywords: '', titleWords: '' } { path: 'guide/b/c', title: 'Guide B - C', type: 'content', keywords: '', titleWords: '' }
] },
{ name: 'api', pages: [
{ path: 'api/c', title: 'API C', type: 'class', keywords: '', titleWords: '' }
] } ] }
]); ]);
}); });
it('should put search results with no containing folder into the default area (Other)', () => { it('should sort by title within sorted area', () => {
const results = [
{path: 'guide/b', title: 'Guide B', type: 'content', keywords: '', titleWords: '' },
{path: 'guide/a', title: 'Guide A', type: 'content', keywords: '', titleWords: '' },
{path: 'api/d', title: 'API D', type: 'class', keywords: '', titleWords: '' },
{path: 'guide/a/c', title: 'Guide A - C', type: 'content', keywords: '', titleWords: '' },
{path: 'api/c', title: 'API C', type: 'class', keywords: '', titleWords: '' },
];
searchResults.next({ query: '', results: results });
expect(currentAreas).toEqual([
{ name: 'api', pages: [
{path: 'api/c', title: 'API C', type: 'class', keywords: '', titleWords: '' },
{path: 'api/d', title: 'API D', type: 'class', keywords: '', titleWords: '' },
] },
{ name: 'guide', pages: [
{path: 'guide/a', title: 'Guide A', type: 'content', keywords: '', titleWords: '' },
{path: 'guide/a/c', title: 'Guide A - C', type: 'content', keywords: '', titleWords: '' },
{path: 'guide/b', title: 'Guide B', type: 'content', keywords: '', titleWords: '' },
] }
]);
});
it('should put search results with no containing folder into the default area (other)', () => {
const results = [ const results = [
{path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' } {path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' }
]; ];
searchResults.next({ query: '', results: results }); searchResults.next({ query: '', results: results });
expect(currentAreas).toEqual([ expect(currentAreas).toEqual([
{ name: 'Other', pages: [ { name: 'other', pages: [
{ path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' } { path: 'news', title: 'News', type: 'marketing', keywords: '', titleWords: '' }
] } ] }
]); ]);
}); });
it('should omit search results with no title', () => {
const results = [
{path: 'news', title: undefined, type: 'marketing', keywords: '', titleWords: '' }
];
searchResults.next({ query: '', results: results });
expect(currentAreas).toEqual([]);
});
it('should emit an "resultSelected" event when a search result anchor is clicked', () => { it('should emit an "resultSelected" event when a search result anchor is clicked', () => {
let selectedResult: SearchResult; let selectedResult: SearchResult;
component.resultSelected.subscribe((result: SearchResult) => selectedResult = result); component.resultSelected.subscribe((result: SearchResult) => selectedResult = result);

View File

@ -18,7 +18,7 @@ export interface SearchArea {
}) })
export class SearchResultsComponent implements OnInit { export class SearchResultsComponent implements OnInit {
readonly defaultArea = 'Other'; readonly defaultArea = 'other';
showResults = false; showResults = false;
@ -57,11 +57,16 @@ export class SearchResultsComponent implements OnInit {
this.showResults = true; this.showResults = true;
const searchAreaMap = {}; const searchAreaMap = {};
search.results.forEach(result => { search.results.forEach(result => {
if (!result.title) { return; } // bad data; should fix
const areaName = this.computeAreaName(result) || this.defaultArea; const areaName = this.computeAreaName(result) || this.defaultArea;
const area = searchAreaMap[areaName] = searchAreaMap[areaName] || []; const area = searchAreaMap[areaName] = searchAreaMap[areaName] || [];
area.push(result); area.push(result);
}); });
return Object.keys(searchAreaMap).map(name => ({ name, pages: searchAreaMap[name] })); const keys = Object.keys(searchAreaMap).sort((l, r) => l > r ? 1 : -1);
return keys.map(name => ({
name,
pages: searchAreaMap[name].sort(compareResults)
}));
} }
// Split the search result path and use the top level folder, if there is one, as the area name. // Split the search result path and use the top level folder, if there is one, as the area name.
@ -70,3 +75,7 @@ export class SearchResultsComponent implements OnInit {
return rest && areaName; return rest && areaName;
} }
} }
function compareResults(l: {title: string}, r: {title: string}) {
return l.title.toUpperCase() > r.title.toUpperCase() ? 1 : -1;
}

View File

@ -37,6 +37,7 @@ aio-search-results {
h2 { h2 {
font-size: 16px; font-size: 16px;
margin: 10px 0px 5px; margin: 10px 0px 5px;
text-transform: uppercase;
} }
a { a {
font-size: 14px; font-size: 14px;
@ -56,4 +57,4 @@ aio-search-results {
width: 100%; width: 100%;
display: block; display: block;
} }
} }