Skip to content

Commit 0ec3ce9

Browse files
committed
fix(docs-infra): highlight the currently active node in top-bar
Related to #33239 and #33255.
1 parent 79b7ddc commit 0ec3ce9

4 files changed

Lines changed: 78 additions & 25 deletions

File tree

aio/src/app/app.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
<img *ngSwitchCase="true" src="assets/images/logos/angular/[email protected]" width="150" height="40" title="Home" alt="Home">
2323
<img *ngSwitchDefault src="assets/images/logos/angular/shield-large.svg" width="37" height="40" title="Home" alt="Home">
2424
</a>
25-
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes"></aio-top-menu>
25+
<aio-top-menu *ngIf="isSideBySide" [nodes]="topMenuNodes" [currentNode]="currentNodes?.TopBar"></aio-top-menu>
2626
<aio-search-box class="search-container" #searchBox (onSearch)="doSearch($event)" (onFocus)="doSearch($event)"></aio-search-box>
2727
<div class="toolbar-external-icons-container">
2828
<a href="https://twitter.com/angular" title="Twitter" aria-label="Angular on twitter">

aio/src/app/layout/top-menu/top-menu.component.spec.ts

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ describe('TopMenuComponent', () => {
1010
const list: HTMLUListElement = fixture.debugElement.nativeElement.querySelector('ul');
1111
return Array.from(list.querySelectorAll('li'));
1212
};
13+
const getSelected = (items: HTMLLIElement[]) =>
14+
items.filter(item => item.classList.contains('selected'));
1315

1416
beforeEach(() => {
1517
TestBed.configureTestingModule({
@@ -38,4 +40,47 @@ describe('TopMenuComponent', () => {
3840
expect(links.map(link => link.textContent)).toEqual(['API', 'Features']);
3941
expect(links.map(link => link.title)).toEqual(['API docs', 'Angular features overview']);
4042
});
43+
44+
it('should mark the currently selected node with `.selected`', () => {
45+
const items = getListItems();
46+
expect(getSelected(items)).toEqual([]);
47+
48+
component.currentNode = {url: 'api', view: 'foo', nodes: []};
49+
fixture.detectChanges();
50+
expect(getSelected(items)).toEqual([items[0]]);
51+
52+
component.currentNode = {url: 'features', view: 'foo', nodes: []};
53+
fixture.detectChanges();
54+
expect(getSelected(items)).toEqual([items[1]]);
55+
56+
component.currentNode = {url: 'something/else', view: 'foo', nodes: []};
57+
fixture.detectChanges();
58+
expect(getSelected(items)).toEqual([]);
59+
});
60+
61+
it('should not mark any node with `.selected` if the current URL is undefined', () => {
62+
component.nodes = [
63+
{url: '', title: 'API', tooltip: 'API docs'},
64+
{url: undefined, title: 'Features', tooltip: 'Angular features overview'},
65+
];
66+
fixture.detectChanges();
67+
const items = getListItems();
68+
69+
component.currentNode = undefined;
70+
fixture.detectChanges();
71+
expect(getSelected(items)).toEqual([]);
72+
});
73+
74+
it('should correctly mark a node with `.selected` even if its URL is empty', () => {
75+
component.nodes = [
76+
{url: '', title: 'API', tooltip: 'API docs'},
77+
{url: undefined, title: 'Features', tooltip: 'Angular features overview'},
78+
];
79+
fixture.detectChanges();
80+
const items = getListItems();
81+
82+
component.currentNode = {url: '', view: 'Empty url', nodes: []};
83+
fixture.detectChanges();
84+
expect(getSelected(items)).toEqual([items[0]]);
85+
});
4186
});
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { Component, Input } from '@angular/core';
2-
import { NavigationNode } from 'app/navigation/navigation.service';
2+
import { CurrentNode, NavigationNode } from 'app/navigation/navigation.service';
33

44
@Component({
55
selector: 'aio-top-menu',
66
template: `
77
<ul role="navigation">
8-
<li *ngFor="let node of nodes">
8+
<li *ngFor="let node of nodes" [ngClass]="{selected: node.url === currentUrl}">
99
<a class="nav-link" [href]="node.url" [title]="node.tooltip">
1010
<span class="nav-link-inner">{{ node.title }}</span>
1111
</a>
@@ -14,5 +14,7 @@ import { NavigationNode } from 'app/navigation/navigation.service';
1414
})
1515
export class TopMenuComponent {
1616
@Input() nodes: NavigationNode[];
17+
@Input() currentNode: CurrentNode | undefined;
1718

19+
get currentUrl(): string | null { return this.currentNode ? this.currentNode.url : null; }
1820
}

aio/src/styles/1-layouts/_top-menu.scss

Lines changed: 28 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -149,41 +149,47 @@ aio-top-menu {
149149
&:focus {
150150
outline: none;
151151
}
152-
}
153-
154-
a.nav-link {
155-
margin: 0 4px;
156-
padding: 0px;
157-
cursor: pointer;
158152

159-
.nav-link-inner {
160-
padding: 8px 16px;
153+
a.nav-link {
154+
margin: 0 4px;
155+
padding: 0px;
156+
cursor: pointer;
161157

162-
&:hover {
163-
background: rgba($white, 0.15);
158+
.nav-link-inner {
164159
border-radius: 4px;
160+
padding: 8px 16px;
161+
162+
&:hover {
163+
background: rgba($white, 0.15);
164+
}
165165
}
166-
}
167166

168-
&:focus {
169-
outline: none;
167+
&:focus {
168+
outline: none;
170169

171-
.nav-link-inner {
172-
background: rgba($white, 0.15);
173-
border-radius: 1px;
174-
box-shadow: 0 0 1px 2px $focus-outline-ondark;
170+
.nav-link-inner {
171+
background: rgba($white, 0.15);
172+
border-radius: 1px;
173+
box-shadow: 0 0 1px 2px $focus-outline-ondark;
174+
}
175+
}
176+
177+
&:active {
178+
.nav-link-inner {
179+
background: rgba($white, 0.15);
180+
}
175181
}
176182
}
177183

178-
&:active {
179-
.nav-link-inner {
180-
background: rgba($white, 0.15);
181-
border-radius: 4px;
184+
&.selected {
185+
a.nav-link {
186+
.nav-link-inner {
187+
background: rgba($white, 0.15);
188+
}
182189
}
183190
}
184191
}
185192
}
186-
187193
}
188194

189195
// SEARCH BOX

0 commit comments

Comments
 (0)