fix(docs-infra): highlight the currently active node in top-bar (#33351)

Related to #33239 and #33255.

PR Close #33351
This commit is contained in:
George Kalpakas 2019-10-23 15:01:38 +03:00 committed by Miško Hevery
parent 34b84f61e0
commit 2aa940f55c
4 changed files with 81 additions and 28 deletions

View File

@ -22,7 +22,7 @@
<img *ngSwitchCase="true" src="assets/images/logos/angular/logo-nav@2x.png" width="150" height="40" title="Home" alt="Home"> <img *ngSwitchCase="true" src="assets/images/logos/angular/logo-nav@2x.png" width="150" height="40" title="Home" alt="Home">
<img *ngSwitchDefault src="assets/images/logos/angular/shield-large.svg" width="37" height="40" title="Home" alt="Home"> <img *ngSwitchDefault src="assets/images/logos/angular/shield-large.svg" width="37" height="40" title="Home" alt="Home">
</a> </a>
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes"></aio-top-menu> <aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes" [currentNode]="currentNodes?.TopBar"></aio-top-menu>
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box> <aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
<div class="toolbar-external-icons-container"> <div class="toolbar-external-icons-container">
<a href="https://twitter.com/angular" title="Twitter" aria-label="Angular on twitter"> <a href="https://twitter.com/angular" title="Twitter" aria-label="Angular on twitter">

View File

@ -10,6 +10,8 @@ describe('TopMenuComponent', () => {
const list: HTMLUListElement = fixture.debugElement.nativeElement.querySelector('ul'); const list: HTMLUListElement = fixture.debugElement.nativeElement.querySelector('ul');
return Array.from(list.querySelectorAll('li')); return Array.from(list.querySelectorAll('li'));
}; };
const getSelected = (items: HTMLLIElement[]) =>
items.filter(item => item.classList.contains('selected'));
beforeEach(() => { beforeEach(() => {
TestBed.configureTestingModule({ TestBed.configureTestingModule({
@ -38,4 +40,47 @@ describe('TopMenuComponent', () => {
expect(links.map(link => link.textContent)).toEqual(['API', 'Features']); expect(links.map(link => link.textContent)).toEqual(['API', 'Features']);
expect(links.map(link => link.title)).toEqual(['API docs', 'Angular features overview']); expect(links.map(link => link.title)).toEqual(['API docs', 'Angular features overview']);
}); });
it('should mark the currently selected node with `.selected`', () => {
const items = getListItems();
expect(getSelected(items)).toEqual([]);
component.currentNode = {url: 'api', view: 'foo', nodes: []};
fixture.detectChanges();
expect(getSelected(items)).toEqual([items[0]]);
component.currentNode = {url: 'features', view: 'foo', nodes: []};
fixture.detectChanges();
expect(getSelected(items)).toEqual([items[1]]);
component.currentNode = {url: 'something/else', view: 'foo', nodes: []};
fixture.detectChanges();
expect(getSelected(items)).toEqual([]);
});
it('should not mark any node with `.selected` if the current URL is undefined', () => {
component.nodes = [
{url: '', title: 'API', tooltip: 'API docs'},
{url: undefined, title: 'Features', tooltip: 'Angular features overview'},
];
fixture.detectChanges();
const items = getListItems();
component.currentNode = undefined;
fixture.detectChanges();
expect(getSelected(items)).toEqual([]);
});
it('should correctly mark a node with `.selected` even if its URL is empty', () => {
component.nodes = [
{url: '', title: 'API', tooltip: 'API docs'},
{url: undefined, title: 'Features', tooltip: 'Angular features overview'},
];
fixture.detectChanges();
const items = getListItems();
component.currentNode = {url: '', view: 'Empty url', nodes: []};
fixture.detectChanges();
expect(getSelected(items)).toEqual([items[0]]);
});
}); });

View File

@ -1,11 +1,11 @@
import { Component, Input } from '@angular/core'; import { Component, Input } from '@angular/core';
import { NavigationNode } from 'app/navigation/navigation.service'; import { CurrentNode, NavigationNode } from 'app/navigation/navigation.service';
@Component({ @Component({
selector: 'aio-top-menu', selector: 'aio-top-menu',
template: ` template: `
<ul role="navigation"> <ul role="navigation">
<li *ngFor="let node of nodes"> <li *ngFor="let node of nodes" [ngClass]="{selected: node.url === currentUrl}">
<a class="nav-link" [href]="node.url" [title]="node.tooltip"> <a class="nav-link" [href]="node.url" [title]="node.tooltip">
<span class="nav-link-inner">{{ node.title }}</span> <span class="nav-link-inner">{{ node.title }}</span>
</a> </a>
@ -14,5 +14,7 @@ import { NavigationNode } from 'app/navigation/navigation.service';
}) })
export class TopMenuComponent { export class TopMenuComponent {
@Input() nodes: NavigationNode[]; @Input() nodes: NavigationNode[];
@Input() currentNode: CurrentNode | undefined;
get currentUrl(): string | null { return this.currentNode ? this.currentNode.url : null; }
} }

View File

@ -149,7 +149,6 @@ aio-top-menu {
&:focus { &:focus {
outline: none; outline: none;
} }
}
a.nav-link { a.nav-link {
margin: 0 4px; margin: 0 4px;
@ -157,11 +156,11 @@ aio-top-menu {
cursor: pointer; cursor: pointer;
.nav-link-inner { .nav-link-inner {
border-radius: 4px;
padding: 8px 16px; padding: 8px 16px;
&:hover { &:hover {
background: rgba($white, 0.15); background: rgba($white, 0.15);
border-radius: 4px;
} }
} }
@ -178,12 +177,19 @@ aio-top-menu {
&:active { &:active {
.nav-link-inner { .nav-link-inner {
background: rgba($white, 0.15); background: rgba($white, 0.15);
border-radius: 4px;
}
} }
} }
} }
&.selected {
a.nav-link {
.nav-link-inner {
background: rgba($white, 0.15);
}
}
}
}
}
} }
// SEARCH BOX // SEARCH BOX