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": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 2987,
|
"runtime-es2015": 2987,
|
||||||
"main-es2015": 449683,
|
"main-es2015": 450612,
|
||||||
"polyfills-es2015": 52195
|
"polyfills-es2015": 52195
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -12,7 +12,7 @@
|
|||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 2987,
|
"runtime-es2015": 2987,
|
||||||
"main-es2015": 450541,
|
"main-es2015": 451469,
|
||||||
"polyfills-es2015": 52195
|
"polyfills-es2015": 52195
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,7 +21,7 @@
|
|||||||
"master": {
|
"master": {
|
||||||
"uncompressed": {
|
"uncompressed": {
|
||||||
"runtime-es2015": 3097,
|
"runtime-es2015": 3097,
|
||||||
"main-es2015": 427026,
|
"main-es2015": 429230,
|
||||||
"polyfills-es2015": 52195
|
"polyfills-es2015": 52195
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,30 +1,57 @@
|
|||||||
<div class="search-results">
|
<div class="search-results" [ngSwitch]="searchState">
|
||||||
<div *ngIf="searchAreas.length; then searchResults; else notFound"></div>
|
|
||||||
|
<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>
|
</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 ...');
|
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', () => {
|
describe('when a search result anchor is clicked', () => {
|
||||||
let searchResult: SearchResult;
|
let searchResult: SearchResult;
|
||||||
let selected: SearchResult|null;
|
let selected: SearchResult|null;
|
||||||
@ -214,9 +220,25 @@ describe('SearchResultsComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('when no query results', () => {
|
describe('when no query results', () => {
|
||||||
it('should display "not found" message', () => {
|
beforeEach(() => {
|
||||||
setSearchResults('something', []);
|
setSearchResults('something', []);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display "not found" message', () => {
|
||||||
expect(getText()).toContain('No results');
|
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 { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
|
||||||
import { SearchResult, SearchResults, SearchArea } from 'app/search/interfaces';
|
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
|
* A component to display search results in groups
|
||||||
*/
|
*/
|
||||||
@ -23,11 +29,18 @@ export class SearchResultsComponent implements OnChanges {
|
|||||||
resultSelected = new EventEmitter<SearchResult>();
|
resultSelected = new EventEmitter<SearchResult>();
|
||||||
|
|
||||||
readonly defaultArea = 'other';
|
readonly defaultArea = 'other';
|
||||||
notFoundMessage = 'Searching ...';
|
searchState: SearchState = SearchState.InProgress;
|
||||||
readonly topLevelFolders = ['guide', 'tutorial'];
|
readonly topLevelFolders = ['guide', 'tutorial'];
|
||||||
searchAreas: SearchArea[] = [];
|
searchAreas: SearchArea[] = [];
|
||||||
|
|
||||||
ngOnChanges() {
|
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);
|
this.searchAreas = this.processSearchResults(this.searchResults);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,7 +56,6 @@ export class SearchResultsComponent implements OnChanges {
|
|||||||
if (!search) {
|
if (!search) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
this.notFoundMessage = 'No results found.';
|
|
||||||
const searchAreaMap: { [key: string]: SearchResult[] } = {};
|
const searchAreaMap: { [key: string]: SearchResult[] } = {};
|
||||||
search.results.forEach(result => {
|
search.results.forEach(result => {
|
||||||
if (!result.title) { return; } // bad data; should fix
|
if (!result.title) { return; } // bad data; should fix
|
||||||
|
@ -67,12 +67,17 @@ aio-search-results {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.not-found {
|
.no-results {
|
||||||
color: $white;
|
color: $white;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
margin: 16px;
|
margin: 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $white;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
@ -104,6 +109,10 @@ aio-search-results {
|
|||||||
.not-found {
|
.not-found {
|
||||||
color: $darkgray;
|
color: $darkgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $blue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user