Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 46dfb92

Browse files
matskomhevery
authored andcommitted
feat(ngdocs): provide support for user to jump between different versions of the angularjs documentation
1 parent ef22968 commit 46dfb92

File tree

10 files changed

+292
-67
lines changed

10 files changed

+292
-67
lines changed
+125
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
describe('DocsApp', function() {
2+
3+
beforeEach(module('docsApp'));
4+
5+
describe('DocsVersionsCtrl', function() {
6+
var $scope, ctrl, window, version = '9.8.7';
7+
8+
beforeEach(function() {
9+
module(function($provide) {
10+
$provide.value('NG_VERSIONS',[
11+
'1.0.0',
12+
'1.0.1',
13+
'1.0.2',
14+
'1.0.3',
15+
'1.0.4',
16+
'1.0.5',
17+
'1.0.6',
18+
'1.1.0',
19+
'1.1.1',
20+
'1.1.2',
21+
'1.1.3',
22+
'1.1.4',
23+
'2.1.3'
24+
]);
25+
$provide.value('$window', window = angular.mock.createMockWindow());
26+
});
27+
inject(function($controller, $rootScope) {
28+
$scope = $rootScope.$new();
29+
$scope.version = version;
30+
ctrl = $controller('DocsVersionsCtrl',{
31+
$scope : $scope,
32+
$window : window
33+
});
34+
});
35+
});
36+
37+
it('should have the correct version of angular', function() {
38+
expect(version).toBe($scope.version);
39+
});
40+
41+
it('should order versions in decending order', function() {
42+
expect($scope.versions.length).toBeGreaterThan(0);
43+
44+
var one = $scope.versions[0].version;
45+
var two = $scope.versions[1].version;
46+
47+
expect(one).toBeGreaterThan(two);
48+
});
49+
50+
it('should list unstable versions at the top of the list', function() {
51+
expect($scope.versions[0].stable).toBe(false);
52+
});
53+
54+
it('should list all items below the last stable as stable regardless of version number', function() {
55+
var limit = $scope.versions.length - 1,
56+
lastUnstableIndex = 0;
57+
58+
while(lastUnstableIndex <= limit) {
59+
if($scope.versions[lastUnstableIndex++].stable) break;
60+
}
61+
62+
for(var i=lastUnstableIndex;i<=limit;i++) {
63+
expect($scope.versions[i].stable).toBe(true);
64+
}
65+
});
66+
67+
describe('changing the URL', function() {
68+
it('should not support the old < 1.0 docs pages', function() {
69+
window.location = 'old';
70+
71+
$scope.versions.unshift({
72+
stable : true,
73+
version : '0.9.10'
74+
});
75+
$scope.jumpToDocsVersion('0.9.10');
76+
expect(window.location).toBe('old');
77+
78+
$scope.versions.unshift({
79+
stable : true,
80+
version : '0.10.1'
81+
});
82+
$scope.jumpToDocsVersion('0.10.1');
83+
expect(window.location).toBe('old');
84+
85+
$scope.jumpToDocsVersion('2.1.3');
86+
expect(window.location).toBe('http://code.angularjs.org/2.1.3/docs');
87+
});
88+
89+
it('should jump to the older versions of current docs for version >= 1.0.2', function() {
90+
$scope.jumpToDocsVersion('1.0.1');
91+
expect(window.location).toBe('http://code.angularjs.org/1.0.1/docs-1.0.1');
92+
93+
$scope.jumpToDocsVersion('1.0.2');
94+
expect(window.location).toBe('http://code.angularjs.org/1.0.2/docs');
95+
96+
$scope.jumpToDocsVersion('1.1.2');
97+
expect(window.location).toBe('http://code.angularjs.org/1.1.2/docs');
98+
});
99+
100+
it('should use the current docs.angularjs.org page when the selected version is the last stable version', function() {
101+
$scope.versions = [{
102+
stable : true,
103+
title : 'test',
104+
version : '1.1.1'
105+
}];
106+
107+
$scope.jumpToDocsVersion('1.1.1');
108+
expect(window.location).toBe('http://docs.angularjs.org');
109+
110+
$scope.versions.unshift({
111+
stable : true,
112+
title : 'test2',
113+
version : '1.2.1'
114+
});
115+
116+
$scope.jumpToDocsVersion('1.1.1');
117+
expect(window.location).toBe('http://code.angularjs.org/1.1.1/docs');
118+
$scope.jumpToDocsVersion('1.2.1');
119+
expect(window.location).toBe('http://docs.angularjs.org');
120+
});
121+
122+
});
123+
});
124+
125+
});

docs/src/appCache.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ function appCacheTemplate() {
6161
"syntaxhighlighter/syntaxhighlighter-combined.js",
6262
"../angular.min.js",
6363
"docs-combined.js",
64-
"docs-keywords.js",
64+
"docs-data.js",
6565
"docs-combined.css",
6666
"syntaxhighlighter/syntaxhighlighter-combined.css",
6767
"img/texture_1.png",

docs/src/gen-docs.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ writer.makeDir('build/docs/', true).then(function() {
4343

4444
function writeTheRest(writesFuture) {
4545
var metadata = ngdoc.metadata(docs);
46+
var versions = ngdoc.ngVersions();
47+
var currentVersion = ngdoc.ngCurrentVersion();
4648

4749
writesFuture.push(writer.symlink('../../docs/content/notes', 'build/docs/notes', 'dir'));
4850
writesFuture.push(writer.symlinkTemplate('css', 'dir'));
@@ -90,8 +92,12 @@ function writeTheRest(writesFuture) {
9092
writesFuture.push(writer.copyTemplate('docs-scenario.html')); // will be rewritten, don't symlink
9193
writesFuture.push(writer.output('docs-scenario.js', ngdoc.scenarios(docs)));
9294

93-
writesFuture.push(writer.output('docs-keywords.js',
94-
['NG_PAGES=', JSON.stringify(metadata).replace(/{/g, '\n{'), ';']));
95+
writesFuture.push(writer.output('docs-data.js',[
96+
"angular.module('docsData', [])",
97+
".value('NG_PAGES'," + JSON.stringify(metadata).replace(/{/g, '\n{') + ")",
98+
".value('NG_VERSION'," + JSON.stringify(currentVersion) + ")",
99+
".value('NG_VERSIONS'," + JSON.stringify(versions) + ");"
100+
]));
95101
writesFuture.push(writer.output('sitemap.xml', new SiteMap(docs).render()));
96102

97103
writesFuture.push(writer.output('robots.txt', 'Sitemap: http://docs.angularjs.org/sitemap.xml\n'));

docs/src/ngdoc.js

+19
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,32 @@ var globalID = 0;
1111
var fs = require('fs');
1212
var fspath = require('path');
1313
var markdown = new Showdown.converter({ extensions : ['table'] });
14+
var shell = require('shelljs');
15+
var gruntUtil = require('../../lib/grunt/utils.js');
1416

1517
exports.trim = trim;
1618
exports.metadata = metadata;
1719
exports.scenarios = scenarios;
1820
exports.merge = merge;
1921
exports.Doc = Doc;
2022

23+
exports.ngVersions = function() {
24+
var line, versions = [], regex = /^v([1-9]\d*(?:\.\d+)+)$/; //only fetch >= 1.0.0 versions
25+
shell.exec('git tag', {silent: true}).output.split(/\s*\n\s*/)
26+
.forEach(function(line) {
27+
var matches = regex.exec(line);
28+
if(matches && matches.length > 0) {
29+
versions.push(matches[1]);
30+
}
31+
});
32+
versions.push(exports.ngCurrentVersion().number);
33+
return versions;
34+
};
35+
36+
exports.ngCurrentVersion = function() {
37+
return gruntUtil.getVersion();
38+
};
39+
2140
var BOOLEAN_ATTR = {};
2241
['multiple', 'selected', 'checked', 'disabled', 'readOnly', 'required'].forEach(function(value) {
2342
BOOLEAN_ATTR[value] = true;

docs/src/templates/css/docs.css

+4
Original file line numberDiff line numberDiff line change
@@ -461,4 +461,8 @@ pre ol li {
461461
box-shadow:-6px 0 5px #555;
462462
display:block;
463463
border-radius:10px;
464+
465+
.docs-version-jump {
466+
width:180px;
467+
margin-bottom:20px;
464468
}

docs/src/templates/index.html

+66-57
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,8 @@
4848
addTag('script', {src: 'components/google-code-prettify.js' }, sync);
4949
addTag('script', {src: 'components/' + (debug ? 'lunr.js' : 'lunr.min.js') }, sync);
5050
addTag('script', {src: 'components/' + (debug ? 'showdown.js' : 'showdown.min.js') }, sync);
51+
addTag('script', {src: 'docs-data.js'}, sync);
5152
addTag('script', {src: 'js/docs.js'}, sync);
52-
addTag('script', {src: 'docs-keywords.js'}, sync);
5353

5454
function path(name) {
5555
if (production) {
@@ -233,74 +233,83 @@ <h4>{{ key }}</h4>
233233

234234
<div class="row">
235235
<div class="span3">
236-
<form class="well form-search" ng-submit="submitForm()">
237-
<div class="dropdown search"
238-
ng-class="{open: focused && bestMatch.rank > 0 && bestMatch.page != currentPage}">
239-
<input type="text" ng-model="search" placeholder="search the docs"
240-
tabindex="1" accesskey="s" class="input-medium search-query" focused="focused">
241-
<ul class="dropdown-menu">
242-
<li>
243-
<a href="{{bestMatch.page.url}}">{{bestMatch.page.shortName}}</a>
244-
</li>
245-
</ul>
236+
<div class="well">
237+
<div ng-controller="DocsVersionsCtrl">
238+
<select ng-options="v.version as v.title group by v.group for v in versions"
239+
ng-model="version"
240+
ng-change="jumpToDocsVersion(version)"
241+
class="docs-version-jump">
242+
</select>
246243
</div>
244+
<form class="form-search" ng-submit="submitForm()">
245+
<div class="dropdown search"
246+
ng-class="{open: focused && bestMatch.rank > 0 && bestMatch.page != currentPage}">
247+
<input type="text" ng-model="search" placeholder="search the docs"
248+
tabindex="1" accesskey="s" class="input-medium search-query" focused="focused">
249+
<ul class="dropdown-menu">
250+
<li>
251+
<a href="{{bestMatch.page.url}}">{{bestMatch.page.shortName}}</a>
252+
</li>
253+
</ul>
254+
</div>
247255

248-
<div class="spacer"></div>
249-
<div ng-show="search">Filtered results:</div>
256+
<div class="spacer"></div>
257+
<div ng-show="search">Filtered results:</div>
250258

251-
<ul class="nav nav-list" ng-hide="page">
252-
<li ng-repeat="page in pages track by page.url" ng-class="navClass(page)" class="api-list-item">
253-
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
254-
</li>
255-
</ul>
259+
<ul class="nav nav-list" ng-hide="page">
260+
<li ng-repeat="page in pages track by page.url" ng-class="navClass(page)" class="api-list-item">
261+
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
262+
</li>
263+
</ul>
256264

257265

258-
<ul class="nav nav-list well" ng-repeat="module in modules track by module.url" class="api-list-item">
259-
<li class="nav-header module">
260-
<a class="guide" href="{{URL.module}}">module</a>
261-
<a class="code" href="{{module.url}}">{{module.name}}</a>
262-
</li>
266+
<ul class="nav nav-list well" ng-repeat="module in modules track by module.url" class="api-list-item">
267+
<li class="nav-header module">
268+
<a class="guide" href="{{URL.module}}">module</a>
269+
<a class="code" href="{{module.url}}">{{module.name}}</a>
270+
</li>
263271

264-
<li class="nav-header section" ng-show="module.directives">
265-
<a href="{{URL.directive}}" class="guide">directive</a>
266-
</li>
267-
<li ng-repeat="page in module.directives track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item">
268-
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
269-
</li>
272+
<li class="nav-header section" ng-show="module.directives">
273+
<a href="{{URL.directive}}" class="guide">directive</a>
274+
</li>
275+
<li ng-repeat="page in module.directives track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item">
276+
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
277+
</li>
270278

271-
<li class="nav-header section" ng-show="module.filters">
272-
<a href="{{URL.filter}}" class="guide">filter</a>
273-
</li>
274-
<li ng-repeat="page in module.filters track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item">
275-
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
276-
</li>
279+
<li class="nav-header section" ng-show="module.filters">
280+
<a href="{{URL.filter}}" class="guide">filter</a>
281+
</li>
282+
<li ng-repeat="page in module.filters track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item">
283+
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
284+
</li>
277285

278-
<li class="nav-header section" ng-show="module.services">
279-
<a href="{{URL.service}}" class="guide">service</a>
280-
</li>
281-
<li ng-repeat="service in module.services track by service.instance.url" ng-animate="'expand'" ng-class="navClass(service.instance, service.provider)" class="api-list-item">
282-
<a ng-show="service.provider" class="pull-right" href="{{service.provider.url}}" tabindex="2"><i class="icon-cog"></i></a>
283-
<a href="{{service.instance.url}}" tabindex="2">{{service.name}}</a>
284-
</li>
286+
<li class="nav-header section" ng-show="module.services">
287+
<a href="{{URL.service}}" class="guide">service</a>
288+
</li>
289+
<li ng-repeat="service in module.services track by service.instance.url" ng-animate="'expand'" ng-class="navClass(service.instance, service.provider)" class="api-list-item">
290+
<a ng-show="service.provider" class="pull-right" href="{{service.provider.url}}" tabindex="2"><i class="icon-cog"></i></a>
291+
<a href="{{service.instance.url}}" tabindex="2">{{service.name}}</a>
292+
</li>
285293

286-
<li class="nav-header section" ng-show="module.types">
287-
<a href="{{URL.type}}" class="guide">Types</a>
288-
</li>
289-
<li ng-repeat="page in module.types track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item">
290-
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
291-
</li>
294+
<li class="nav-header section" ng-show="module.types">
295+
<a href="{{URL.type}}" class="guide">Types</a>
296+
</li>
297+
<li ng-repeat="page in module.types track by page.url" ng-class="navClass(page)" ng-animate="'expand'" class="api-list-item">
298+
<a href="{{page.url}}" tabindex="2">{{page.shortName}}</a>
299+
</li>
292300

293-
<li class="nav-header section" ng-show="module.globals">
294-
<a href="{{URL.api}}" class="global guide">global APIs</a>
295-
&nbsp;
296-
</li>
297-
<li ng-repeat="page in module.globals track by page.url" ng-class="navClass(page)" class="api-list-item">
298-
<a href="{{page.url}}" tabindex="2">{{page.id}}</a>
299-
</li>
301+
<li class="nav-header section" ng-show="module.globals">
302+
<a href="{{URL.api}}" class="global guide">global APIs</a>
303+
&nbsp;
304+
</li>
305+
<li ng-repeat="page in module.globals track by page.url" ng-class="navClass(page)" class="api-list-item">
306+
<a href="{{page.url}}" tabindex="2">{{page.id}}</a>
307+
</li>
300308

301-
</ul>
309+
</ul>
302310

303-
</form>
311+
</form>
312+
</div>
304313
</div>
305314
<div class="span9">
306315

0 commit comments

Comments
 (0)