feat(aio): add tooltip to nav node if none specified

This commit is contained in:
Ward Bell 2017-04-28 17:18:48 -07:00 committed by Matias Niemelä
parent efaf502e95
commit d32bc5df3e
2 changed files with 74 additions and 14 deletions

View File

@ -80,20 +80,54 @@ describe('NavigationService', () => {
it('should do WHAT(?) if the request fails'); it('should do WHAT(?) if the request fails');
}); });
describe('node.tooltip', () => {
let view: NavigationNode[];
const sideNav: NavigationNode[] = [
{ title: 'a', tooltip: 'a tip' },
{ title: 'b' },
{ title: 'c!'},
{ url: 'foo' }
];
beforeEach(() => {
navService.navigationViews.subscribe(views => view = views.sideNav);
backend.connectionsArray[0].mockRespond(createResponse({sideNav}));
});
it('should have the supplied tooltip', () => {
expect(view[0].tooltip).toEqual('a tip');
});
it('should create a tooltip from title + period', () => {
expect(view[1].tooltip).toEqual('b.');
});
it('should create a tooltip from title, keeping its trailing punctuation', () => {
expect(view[2].tooltip).toEqual('c!');
});
it('should not create a tooltip if there is no title', () => {
expect(view[3].tooltip).toBeUndefined();
});
});
describe('currentNode', () => { describe('currentNode', () => {
let currentNode: CurrentNode; let currentNode: CurrentNode;
let locationService: MockLocationService; let locationService: MockLocationService;
const topBarNodes: NavigationNode[] = [{ url: 'features', title: 'Features' }]; const topBarNodes: NavigationNode[] = [
{ url: 'features', title: 'Features', tooltip: 'tip' }
];
const sideNavNodes: NavigationNode[] = [ const sideNavNodes: NavigationNode[] = [
{ title: 'a', children: [ { title: 'a', tooltip: 'tip', children: [
{ url: 'b', title: 'b', children: [ { url: 'b', title: 'b', tooltip: 'tip', children: [
{ url: 'c', title: 'c' }, { url: 'c', title: 'c', tooltip: 'tip' },
{ url: 'd', title: 'd' } { url: 'd', title: 'd', tooltip: 'tip' }
] }, ] },
{ url: 'e', title: 'e' } { url: 'e', title: 'e', tooltip: 'tip' }
] }, ] },
{ url: 'f', title: 'f' } { url: 'f', title: 'f', tooltip: 'tip' }
]; ];
const navJson = { const navJson = {
@ -199,6 +233,7 @@ describe('NavigationService', () => {
describe('docVersions', () => { describe('docVersions', () => {
let actualDocVersions: NavigationNode[]; let actualDocVersions: NavigationNode[];
let docVersions: NavigationNode[]; let docVersions: NavigationNode[];
let expectedDocVersions: NavigationNode[];
beforeEach(() => { beforeEach(() => {
actualDocVersions = []; actualDocVersions = [];
@ -207,12 +242,16 @@ describe('NavigationService', () => {
{ title: 'v2', url: 'https://v2.angular.io' } { title: 'v2', url: 'https://v2.angular.io' }
]; ];
expectedDocVersions = docVersions.map(v => (
{...v, ...{ tooltip: v.title + '.'}})
);
navService.navigationViews.subscribe(views => actualDocVersions = views.docVersions); navService.navigationViews.subscribe(views => actualDocVersions = views.docVersions);
}); });
it('should extract the docVersions', () => { it('should extract the docVersions', () => {
backend.connectionsArray[0].mockRespond(createResponse({ docVersions })); backend.connectionsArray[0].mockRespond(createResponse({ docVersions }));
expect(actualDocVersions).toEqual(docVersions); expect(actualDocVersions).toEqual(expectedDocVersions);
}); });
}); });
}); });

View File

@ -90,7 +90,7 @@ export class NavigationService {
*/ */
private getCurrentNode(navigationViews: Observable<NavigationViews>): Observable<CurrentNode> { private getCurrentNode(navigationViews: Observable<NavigationViews>): Observable<CurrentNode> {
const currentNode = combineLatest( const currentNode = combineLatest(
navigationViews.map(this.computeUrlToNavNodesMap), navigationViews.map(views => this.computeUrlToNavNodesMap(views)),
this.location.currentPath, this.location.currentPath,
(navMap, url) => { (navMap, url) => {
const urlKey = url.startsWith('api/') ? 'api' : url; const urlKey = url.startsWith('api/') ? 'api' : url;
@ -110,21 +110,42 @@ export class NavigationService {
private computeUrlToNavNodesMap(navigation: NavigationViews) { private computeUrlToNavNodesMap(navigation: NavigationViews) {
const navMap = new Map<string, CurrentNode>(); const navMap = new Map<string, CurrentNode>();
Object.keys(navigation) Object.keys(navigation)
.forEach(view => navigation[view].forEach(node => walkNodes(view, node))); .forEach(view => navigation[view]
.forEach(node => this.walkNodes(view, navMap, node)));
return navMap; return navMap;
}
function walkNodes(view: string, node: NavigationNode, ancestors: NavigationNode[] = []) { /**
* Add tooltip to node if it doesn't have one and have title.
* If don't want tooltip, specify `"tooltip": ""` in navigation.json
*/
private ensureHasTooltip(node: NavigationNode) {
const title = node.title;
const tooltip = node.tooltip;
if (tooltip == null && title ) {
// add period if no trailing punctuation
node.tooltip = title + (/[a-zA-Z0-9]$/.test(title) ? '.' : '');
}
}
/**
* Walk the nodes of a navigation tree-view,
* patching them and computing their ancestor nodes
*/
private walkNodes(
view: string, navMap: Map<string, CurrentNode>,
node: NavigationNode, ancestors: NavigationNode[] = []) {
const nodes = [node, ...ancestors]; const nodes = [node, ...ancestors];
const url = node.url; const url = node.url;
this.ensureHasTooltip(node);
// only map to this node if it has a url associated with it // only map to this node if it has a url
if (url) { if (url) {
// Strip off trailing slashes from nodes in the navMap - they are not relevant to matching // Strip off trailing slashes from nodes in the navMap - they are not relevant to matching
navMap[url.replace(/\/$/, '')] = { url, view, nodes }; navMap[url.replace(/\/$/, '')] = { url, view, nodes };
} }
if (node.children) { if (node.children) {
node.children.forEach(child => walkNodes(view, child, nodes)); node.children.forEach(child => this.walkNodes(view, navMap, child, nodes));
}
} }
} }
} }