feat(docs-infra): add useful links if landed on 404 page and no search results found (#34978)
Added additional links which can help user find the things they are looking for when there are no search results (when searching or on a 404 page). Note: This commit increases the main bundle's payload size due to the extra content of the `aio-search-results` component. Fixes #31532 PR Close #34978
This commit is contained in:
parent
aec463c606
commit
01ab168774
|
@ -3,7 +3,7 @@
|
|||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 2987,
|
||||
"main-es2015": 449683,
|
||||
"main-es2015": 450612,
|
||||
"polyfills-es2015": 52195
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
|||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 2987,
|
||||
"main-es2015": 450541,
|
||||
"main-es2015": 451469,
|
||||
"polyfills-es2015": 52195
|
||||
}
|
||||
}
|
||||
|
@ -21,7 +21,7 @@
|
|||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime-es2015": 3097,
|
||||
"main-es2015": 427026,
|
||||
"main-es2015": 429230,
|
||||
"polyfills-es2015": 52195
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,30 +1,57 @@
|
|||
<div class="search-results">
|
||||
<div *ngIf="searchAreas.length; then searchResults; else notFound"></div>
|
||||
<div class="search-results" [ngSwitch]="searchState">
|
||||
|
||||
<ng-container *ngSwitchCase="'in-progress'">
|
||||
<p class="no-results">Searching ...</p>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'results-found'">
|
||||
<h2 class="visually-hidden">Search Results</h2>
|
||||
<div class="search-area" *ngFor="let area of searchAreas">
|
||||
<h3 class="search-section-header">{{area.name}} ({{area.pages.length + area.priorityPages.length}})</h3>
|
||||
<ul class="priority-pages" >
|
||||
<li class="search-page" *ngFor="let page of area.priorityPages">
|
||||
<a class="search-result-item" href="{{ page.path }}" (click)="onResultSelected(page, $event)">
|
||||
<span class="symbol {{page.type}}" *ngIf="area.name === 'api'"></span>
|
||||
<span [class.deprecated-api-item]="page.deprecated">{{ page.title }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li class="search-page" *ngFor="let page of area.pages">
|
||||
<a class="search-result-item" href="{{ page.path }}" (click)="onResultSelected(page, $event)">
|
||||
<span class="symbol {{page.type}}" *ngIf="area.name === 'api'"></span>
|
||||
<span [class.deprecated-api-item]="page.deprecated">{{ page.title }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ng-container>
|
||||
|
||||
<ng-container *ngSwitchCase="'no-results-found'">
|
||||
<div class="search-area">
|
||||
<p class="no-results">
|
||||
No results found.<br>
|
||||
Here are a few links that might be helpful in finding what you are looking for:
|
||||
</p>
|
||||
<ul class="priority-pages">
|
||||
<li class="search-page">
|
||||
<a class="search-result-item" href="api">API reference</a>
|
||||
</li>
|
||||
<li class="search-page">
|
||||
<a class="search-result-item" href="resources">Resources</a>
|
||||
</li>
|
||||
<li class="search-page">
|
||||
<a class="search-result-item" href="guide/glossary">Glossary</a>
|
||||
</li>
|
||||
<li class="search-page">
|
||||
<a class="search-result-item" href="guide/cheatsheet">Cheat-sheet</a>
|
||||
</li>
|
||||
<li class="search-page">
|
||||
<a class="search-result-item" href="https://blog.angular.io/">Angular blog</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ng-container>
|
||||
</div>
|
||||
|
||||
<ng-template #searchResults>
|
||||
<h2 class="visually-hidden">Search Results</h2>
|
||||
<div class="search-area" *ngFor="let area of searchAreas">
|
||||
<h3 class="search-section-header">{{area.name}} ({{area.pages.length + area.priorityPages.length}})</h3>
|
||||
<ul class="priority-pages" >
|
||||
<li class="search-page" *ngFor="let page of area.priorityPages">
|
||||
<a class="search-result-item" href="{{ page.path }}" (click)="onResultSelected(page, $event)">
|
||||
<span class="symbol {{page.type}}" *ngIf="area.name === 'api'"></span>
|
||||
<span [class.deprecated-api-item]="page.deprecated">{{ page.title }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul>
|
||||
<li class="search-page" *ngFor="let page of area.pages">
|
||||
<a class="search-result-item" href="{{ page.path }}" (click)="onResultSelected(page, $event)">
|
||||
<span class="symbol {{page.type}}" *ngIf="area.name === 'api'"></span>
|
||||
<span [class.deprecated-api-item]="page.deprecated">{{ page.title }}</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #notFound>
|
||||
<p class="not-found">{{notFoundMessage}}</p>
|
||||
</ng-template>
|
||||
|
|
|
@ -170,6 +170,12 @@ describe('SearchResultsComponent', () => {
|
|||
expect(getText()).toContain('Searching ...');
|
||||
});
|
||||
|
||||
it('should not display default links while searching', () => {
|
||||
fixture.detectChanges();
|
||||
const resultLinks = fixture.debugElement.queryAll(By.css('.search-page a'));
|
||||
expect(resultLinks.length).toEqual(0);
|
||||
});
|
||||
|
||||
describe('when a search result anchor is clicked', () => {
|
||||
let searchResult: SearchResult;
|
||||
let selected: SearchResult|null;
|
||||
|
@ -214,9 +220,25 @@ describe('SearchResultsComponent', () => {
|
|||
});
|
||||
|
||||
describe('when no query results', () => {
|
||||
it('should display "not found" message', () => {
|
||||
beforeEach(() => {
|
||||
setSearchResults('something', []);
|
||||
});
|
||||
|
||||
it('should display "not found" message', () => {
|
||||
expect(getText()).toContain('No results');
|
||||
});
|
||||
|
||||
it('should contain reference links', () => {
|
||||
const resultLinks = fixture.debugElement.queryAll(By.css('.search-page a'));
|
||||
const resultHrefs = resultLinks.map(a => a.nativeNode.getAttribute('href'));
|
||||
expect(resultHrefs.length).toEqual(5);
|
||||
expect(resultHrefs).toEqual([
|
||||
'api',
|
||||
'resources',
|
||||
'guide/glossary',
|
||||
'guide/cheatsheet',
|
||||
'https://blog.angular.io/',
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||
import { SearchResult, SearchResults, SearchArea } from 'app/search/interfaces';
|
||||
|
||||
enum SearchState {
|
||||
InProgress = 'in-progress',
|
||||
ResultsFound = 'results-found',
|
||||
NoResultsFound = 'no-results-found'
|
||||
}
|
||||
|
||||
/**
|
||||
* A component to display search results in groups
|
||||
*/
|
||||
|
@ -23,11 +29,18 @@ export class SearchResultsComponent implements OnChanges {
|
|||
resultSelected = new EventEmitter<SearchResult>();
|
||||
|
||||
readonly defaultArea = 'other';
|
||||
notFoundMessage = 'Searching ...';
|
||||
searchState: SearchState = SearchState.InProgress;
|
||||
readonly topLevelFolders = ['guide', 'tutorial'];
|
||||
searchAreas: SearchArea[] = [];
|
||||
|
||||
ngOnChanges() {
|
||||
if (this.searchResults === null) {
|
||||
this.searchState = SearchState.InProgress;
|
||||
} else if (this.searchResults.results.length) {
|
||||
this.searchState = SearchState.ResultsFound;
|
||||
} else {
|
||||
this.searchState = SearchState.NoResultsFound;
|
||||
}
|
||||
this.searchAreas = this.processSearchResults(this.searchResults);
|
||||
}
|
||||
|
||||
|
@ -43,7 +56,6 @@ export class SearchResultsComponent implements OnChanges {
|
|||
if (!search) {
|
||||
return [];
|
||||
}
|
||||
this.notFoundMessage = 'No results found.';
|
||||
const searchAreaMap: { [key: string]: SearchResult[] } = {};
|
||||
search.results.forEach(result => {
|
||||
if (!result.title) { return; } // bad data; should fix
|
||||
|
|
|
@ -67,12 +67,17 @@ aio-search-results {
|
|||
}
|
||||
}
|
||||
|
||||
.not-found {
|
||||
.no-results {
|
||||
color: $white;
|
||||
text-align: center;
|
||||
margin: 16px;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $white;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
display: block;
|
||||
}
|
||||
|
@ -104,6 +109,10 @@ aio-search-results {
|
|||
.not-found {
|
||||
color: $darkgray;
|
||||
}
|
||||
|
||||
a {
|
||||
color: $blue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue