diff --git a/aio/e2e/app.e2e-spec.ts b/aio/e2e/app.e2e-spec.ts
index bf8d64e00e..f8e219fccd 100644
--- a/aio/e2e/app.e2e-spec.ts
+++ b/aio/e2e/app.e2e-spec.ts
@@ -15,6 +15,16 @@ describe('site App', function() {
expect(page.getDocViewerText()).toMatch(/Progressive web apps/i);
});
+ it('should set appropriate window titles', () => {
+ expect(browser.getTitle()).toBe('Angular');
+
+ page.getTopMenuLink('features').click();
+ expect(browser.getTitle()).toBe('Angular - FEATURES & BENEFITS');
+
+ page.homeLink.click();
+ expect(browser.getTitle()).toBe('Angular');
+ });
+
it('should show the tutorial index page at `/tutorial/` after jitterbugging through features', () => {
// check that we can navigate directly to the tutorial page
page.navigateTo('tutorial/');
diff --git a/aio/e2e/app.po.ts b/aio/e2e/app.po.ts
index ed62a3a568..7beb57d1ea 100644
--- a/aio/e2e/app.po.ts
+++ b/aio/e2e/app.po.ts
@@ -5,6 +5,7 @@ const githubRegex = /https:\/\/github.com\/angular\/angular\//;
export class SitePage {
links = element.all(by.css('md-toolbar a'));
+ homeLink = element(by.css('a.home'));
docsMenuLink = element(by.cssContainingText('aio-top-menu a', 'Docs'));
docViewer = element(by.css('aio-doc-viewer'));
codeExample = element.all(by.css('aio-doc-viewer pre > code'));
diff --git a/aio/package.json b/aio/package.json
index e7be7549ea..1c09b5b010 100644
--- a/aio/package.json
+++ b/aio/package.json
@@ -126,7 +126,6 @@
"lunr": "^2.1.0",
"protractor": "^5.2.0",
"rehype": "^4.0.0",
- "rehype-autolink-headings": "^2.0.0",
"rehype-slug": "^2.0.0",
"remark": "^7.0.0",
"remark-html": "^6.0.0",
diff --git a/aio/src/app/layout/doc-viewer/doc-viewer.component.spec.ts b/aio/src/app/layout/doc-viewer/doc-viewer.component.spec.ts
index 57484e9474..1ab9eb630a 100644
--- a/aio/src/app/layout/doc-viewer/doc-viewer.component.spec.ts
+++ b/aio/src/app/layout/doc-viewer/doc-viewer.component.spec.ts
@@ -361,6 +361,22 @@ describe('DocViewerComponent', () => {
fixture.detectChanges();
expect(titleService.setTitle).toHaveBeenCalledWith('Angular - Text Content');
});
+
+ it('should still use `innerText` if available but empty', () => {
+ const querySelector_ = docViewerEl.querySelector;
+ spyOn(docViewerEl, 'querySelector').and.callFake((selector: string) => {
+ const elem = querySelector_.call(docViewerEl, selector);
+ Object.defineProperties(elem, {
+ innerText: { value: '' },
+ textContent: { value: 'Text Content' }
+ });
+ return elem;
+ });
+
+ setCurrentDoc('
link
Some content');
+ fixture.detectChanges();
+ expect(titleService.setTitle).toHaveBeenCalledWith('Angular');
+ });
});
describe('TOC', () => {
diff --git a/aio/src/app/layout/doc-viewer/doc-viewer.component.ts b/aio/src/app/layout/doc-viewer/doc-viewer.component.ts
index 1687f8734c..5808d2a45d 100644
--- a/aio/src/app/layout/doc-viewer/doc-viewer.component.ts
+++ b/aio/src/app/layout/doc-viewer/doc-viewer.component.ts
@@ -92,17 +92,19 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
private addTitleAndToc(docId: string) {
this.tocService.reset();
- let title = '';
const titleEl = this.hostElement.querySelector('h1');
+ let title = '';
+
// Only create TOC for docs with an
title
// If you don't want a TOC, add "no-toc" class to
if (titleEl) {
- title = (titleEl.innerText || titleEl.textContent).trim();
+ title = (typeof titleEl.innerText === 'string') ? titleEl.innerText : titleEl.textContent;
if (!/(no-toc|notoc)/i.test(titleEl.className)) {
this.tocService.genToc(this.hostElement, docId);
titleEl.insertAdjacentHTML('afterend', '');
}
}
+
this.titleService.setTitle(title ? `Angular - ${title}` : 'Angular');
}
diff --git a/aio/tools/transforms/angular-base-package/post-processors/autolink-headings.js b/aio/tools/transforms/angular-base-package/post-processors/autolink-headings.js
index 5b9c353f78..7daa72623e 100644
--- a/aio/tools/transforms/angular-base-package/post-processors/autolink-headings.js
+++ b/aio/tools/transforms/angular-base-package/post-processors/autolink-headings.js
@@ -1,9 +1,34 @@
+const has = require('hast-util-has-property');
+const is = require('hast-util-is-element');
const slug = require('rehype-slug');
-const link = require('rehype-autolink-headings');
+const visit = require('unist-util-visit');
/**
- * Get remark to inject anchors into headings
+ * Get remark to add IDs to headings and inject anchors into them.
+ * This is a stripped-down equivalent of [rehype-autolink-headings](https://github.com/wooorm/rehype-autolink-headings)
+ * that supports ignoring headings with the `no-anchor` class.
*/
+const HEADINGS = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6'];
+const NO_ANCHOR_CLASS = 'no-anchor';
+
+const clone = obj => JSON.parse(JSON.stringify(obj));
+const hasClass = (node, cls) => {
+ const className = node.properties.className;
+ return className && className.includes(cls);
+};
+
+const link = options =>
+ tree => visit(tree, node => {
+ if (is(node, HEADINGS) && has(node, 'id') && !hasClass(node, NO_ANCHOR_CLASS)) {
+ node.children.unshift({
+ type: 'element',
+ tagName: 'a',
+ properties: Object.assign(clone(options.properties), {href: `#${node.properties.id}`}),
+ children: clone(options.content)
+ });
+ }
+ });
+
module.exports = [
slug,
[link, {
@@ -12,11 +37,13 @@ module.exports = [
className: ['header-link'],
'aria-hidden': 'true'
},
- content: {
- type: 'element',
- tagName: 'i',
- properties: {className: ['material-icons']},
- children: [{ type: 'text', value: 'link' }]
- }
+ content: [
+ {
+ type: 'element',
+ tagName: 'i',
+ properties: {className: ['material-icons']},
+ children: [{ type: 'text', value: 'link' }]
+ }
+ ]
}]
];
diff --git a/aio/tools/transforms/angular-base-package/post-processors/autolink-headings.spec.js b/aio/tools/transforms/angular-base-package/post-processors/autolink-headings.spec.js
index 618da3a568..05ac0a8dc5 100644
--- a/aio/tools/transforms/angular-base-package/post-processors/autolink-headings.spec.js
+++ b/aio/tools/transforms/angular-base-package/post-processors/autolink-headings.spec.js
@@ -14,20 +14,37 @@ describe('autolink-headings postprocessor', () => {
});
it('should add anchors to headings', () => {
- const docs = [ {
- docType: 'a',
- renderedContent: `
- Heading 1
- Heading with bold
- Heading with encoded chars &
- `
- }];
+ const originalContent = `
+ Heading 1
+ Heading with bold
+ Heading with encoded chars &
+ `;
+ const processedContent = `
+ Heading 1
+ Heading with bold
+ Heading with encoded chars &
+ `;
+
+ const docs = [{docType: 'a', renderedContent: originalContent}];
processor.$process(docs);
- expect(docs[0].renderedContent).toEqual(`
- Heading 1
- Heading with bold
- Heading with encoded chars &
- `);
+ expect(docs[0].renderedContent).toBe(processedContent);
+ });
+
+ it('should ignore headings with the `no-anchor` class', () => {
+ const originalContent = `
+ Heading 1
+ Heading with bold
+ Heading with encoded chars &
+ `;
+ const processedContent = `
+ Heading 1
+ Heading with bold
+ Heading with encoded chars &
+ `;
+
+ const docs = [{docType: 'a', renderedContent: originalContent}];
+ processor.$process(docs);
+ expect(docs[0].renderedContent).toBe(processedContent);
});
});
diff --git a/aio/yarn.lock b/aio/yarn.lock
index f28c52d8c7..cdeea17743 100644
--- a/aio/yarn.lock
+++ b/aio/yarn.lock
@@ -2977,7 +2977,7 @@ express@^4.13.3:
utils-merge "1.0.1"
vary "~1.1.2"
-extend@3, extend@^3.0.0, extend@^3.0.1, extend@~3.0.0, extend@~3.0.1:
+extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444"
@@ -6999,15 +6999,6 @@ regjsparser@^0.1.4:
dependencies:
jsesc "~0.5.0"
-rehype-autolink-headings@^2.0.0:
- version "2.0.2"
- resolved "https://registry.yarnpkg.com/rehype-autolink-headings/-/rehype-autolink-headings-2.0.2.tgz#48c7161b1a1020e942c758eb6b2c55cb1bc504d0"
- dependencies:
- extend "^3.0.1"
- hast-util-has-property "^1.0.0"
- hast-util-is-element "^1.0.0"
- unist-util-visit "^1.1.0"
-
rehype-parse@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-3.1.0.tgz#7f5227a597a3f39fc4b938646161539c444ee728"