From 12be311618e2ae32df15a523782237907725d51a Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Thu, 1 Mar 2018 10:56:32 +0000 Subject: [PATCH] fix(aio): remove all links from toc titles (#22533) The previous approach just removed the first `a` tag that was found, but now that the header-link anchor is not at the start of the heading, it could fail. Closes #22493 PR Close #22533 --- aio/src/app/shared/toc.service.spec.ts | 28 +++++++++++++++----------- aio/src/app/shared/toc.service.ts | 24 +++++++++++++++------- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/aio/src/app/shared/toc.service.spec.ts b/aio/src/app/shared/toc.service.spec.ts index 8b19fc9371..f3af3d071c 100644 --- a/aio/src/app/shared/toc.service.spec.ts +++ b/aio/src/app/shared/toc.service.spec.ts @@ -291,23 +291,20 @@ describe('TocService', () => { }); }); - describe('TocItem for an h2 with anchor link and extra whitespace', () => { + describe('TocItem for an h2 with links and extra whitespace', () => { let docId: string; - let docEl: HTMLDivElement; let tocItem: TocItem; - let expectedTocContent: string; beforeEach(() => { docId = 'fizz/buzz/'; - expectedTocContent = 'Setup to develop locally.'; // An almost-actual

... with extra whitespace - docEl = callGenToc(` + callGenToc(`

- develop locally. + - ${expectedTocContent}

`, docId); @@ -331,7 +328,7 @@ describe('TocService', () => { it('should have bypassed HTML sanitizing of heading\'s innerHTML ', () => { const domSanitizer: TestDomSanitizer = injector.get(DomSanitizer); expect(domSanitizer.bypassSecurityTrustHtml) - .toHaveBeenCalledWith(expectedTocContent); + .toHaveBeenCalledWith('Setup to develop locally.'); }); }); }); @@ -352,13 +349,20 @@ class TestDomSanitizer { } class MockScrollSpyService { - $lastInfo: { + private $$lastInfo: { active: Subject, - unspy: jasmine.Spy - }; + unspy: jasmine.Spy, + } | undefined; + + get $lastInfo() { + if (!this.$$lastInfo) { + throw new Error('$lastInfo is not yet defined. You must call `spyOn` first.'); + } + return this.$$lastInfo; + } spyOn(headings: HTMLHeadingElement[]): ScrollSpyInfo { - return this.$lastInfo = { + return this.$$lastInfo = { active: new Subject(), unspy: jasmine.createSpy('unspy'), }; diff --git a/aio/src/app/shared/toc.service.ts b/aio/src/app/shared/toc.service.ts index a321f9b9fb..df5f50b94f 100644 --- a/aio/src/app/shared/toc.service.ts +++ b/aio/src/app/shared/toc.service.ts @@ -16,7 +16,7 @@ export interface TocItem { export class TocService { tocList = new ReplaySubject(1); activeItemIndex = new ReplaySubject(1); - private scrollSpyInfo: ScrollSpyInfo | null; + private scrollSpyInfo: ScrollSpyInfo | null = null; constructor( @Inject(DOCUMENT) private document: any, @@ -53,15 +53,25 @@ export class TocService { // This bad boy exists only to strip off the anchor link attached to a heading private extractHeadingSafeHtml(heading: HTMLHeadingElement) { - const a = this.document.createElement('a') as HTMLAnchorElement; - a.innerHTML = heading.innerHTML; - const anchorLink = a.querySelector('a'); - if (anchorLink) { - a.removeChild(anchorLink); + const div: HTMLDivElement = this.document.createElement('div'); + div.innerHTML = heading.innerHTML; + const anchorLinks: NodeListOf = div.querySelectorAll('a'); + for (let i = 0; i < anchorLinks.length; i++) { + const anchorLink = anchorLinks[i]; + if (!anchorLink.classList.contains('header-link')) { + // this is an anchor that contains actual content that we want to keep + // move the contents of the anchor into its parent + const parent = anchorLink.parentNode!; + while (anchorLink.childNodes.length) { + parent.insertBefore(anchorLink.childNodes[0], anchorLink); + } + } + // now remove the anchor + anchorLink.remove(); } // security: the document element which provides this heading content // is always authored by the documentation team and is considered to be safe - return this.domSanitizer.bypassSecurityTrustHtml(a.innerHTML.trim()); + return this.domSanitizer.bypassSecurityTrustHtml(div.innerHTML.trim()); } private findTocHeadings(docElement: Element): HTMLHeadingElement[] {