feat(aio): allow template to position embedded ToC (#22570)
Previously the doc-viewer would insert an embedded `<aio-toc>` element into the DOM directly after the H1 element. Now it will not do this if there is already a such element in the doc contents. This allows the content-author/template-developer to position the ToC for specific cases. PR Close #22570
This commit is contained in:
parent
2a1e3d191f
commit
53b0fe8144
@ -175,6 +175,9 @@ describe('DocViewerComponent', () => {
|
|||||||
const DOC_WITHOUT_H1 = 'Some content';
|
const DOC_WITHOUT_H1 = 'Some content';
|
||||||
const DOC_WITH_H1 = '<h1>Features</h1>Some content';
|
const DOC_WITH_H1 = '<h1>Features</h1>Some content';
|
||||||
const DOC_WITH_NO_TOC_H1 = '<h1 class="no-toc">Features</h1>Some content';
|
const DOC_WITH_NO_TOC_H1 = '<h1 class="no-toc">Features</h1>Some content';
|
||||||
|
const DOC_WITH_EMBEDDED_TOC = '<h1>Features</h1><aio-toc class="embedded"></aio-toc>Some content';
|
||||||
|
const DOC_WITH_EMBEDDED_TOC_WITHOUT_H1 = '<aio-toc class="embedded"></aio-toc>Some content';
|
||||||
|
const DOC_WITH_EMBEDDED_TOC_WITH_NO_TOC_H1 = '<aio-toc class="embedded"></aio-toc>Some content';
|
||||||
const DOC_WITH_HIDDEN_H1_CONTENT = '<h1><i style="visibility: hidden">link</i>Features</h1>Some content';
|
const DOC_WITH_HIDDEN_H1_CONTENT = '<h1><i style="visibility: hidden">link</i>Features</h1>Some content';
|
||||||
let titleService: MockTitle;
|
let titleService: MockTitle;
|
||||||
let tocService: MockTocService;
|
let tocService: MockTocService;
|
||||||
@ -271,26 +274,45 @@ describe('DocViewerComponent', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('(ToC)', () => {
|
describe('(ToC)', () => {
|
||||||
it('should add an embedded ToC element if there is an `<h1>` heading', () => {
|
describe('needed', () => {
|
||||||
doPrepareTitleAndToc(DOC_WITH_H1);
|
it('should add an embedded ToC element if there is an `<h1>` heading', () => {
|
||||||
const tocEl = getTocEl()!;
|
doPrepareTitleAndToc(DOC_WITH_H1);
|
||||||
|
const tocEl = getTocEl()!;
|
||||||
|
|
||||||
expect(tocEl).toBeTruthy();
|
expect(tocEl).toBeTruthy();
|
||||||
expect(tocEl.classList.contains('embedded')).toBe(true);
|
expect(tocEl.classList.contains('embedded')).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add a second ToC element if there a hard coded one in place', () => {
|
||||||
|
doPrepareTitleAndToc(DOC_WITH_EMBEDDED_TOC);
|
||||||
|
expect(targetEl.querySelectorAll('aio-toc').length).toEqual(1);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not add a ToC element if there is a `.no-toc` `<h1>` heading', () => {
|
|
||||||
doPrepareTitleAndToc(DOC_WITH_NO_TOC_H1);
|
describe('not needed', () => {
|
||||||
expect(getTocEl()).toBeFalsy();
|
it('should not add a ToC element if there is a `.no-toc` `<h1>` heading', () => {
|
||||||
|
doPrepareTitleAndToc(DOC_WITH_NO_TOC_H1);
|
||||||
|
expect(getTocEl()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not add a ToC element if there is no `<h1>` heading', () => {
|
||||||
|
doPrepareTitleAndToc(DOC_WITHOUT_H1);
|
||||||
|
expect(getTocEl()).toBeFalsy();
|
||||||
|
|
||||||
|
doPrepareTitleAndToc(EMPTY_DOC);
|
||||||
|
expect(getTocEl()).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove ToC a hard coded one', () => {
|
||||||
|
doPrepareTitleAndToc(DOC_WITH_EMBEDDED_TOC_WITHOUT_H1);
|
||||||
|
expect(getTocEl()).toBeFalsy();
|
||||||
|
|
||||||
|
doPrepareTitleAndToc(DOC_WITH_EMBEDDED_TOC_WITH_NO_TOC_H1);
|
||||||
|
expect(getTocEl()).toBeFalsy();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not add a ToC element if there is no `<h1>` heading', () => {
|
|
||||||
doPrepareTitleAndToc(DOC_WITHOUT_H1);
|
|
||||||
expect(getTocEl()).toBeFalsy();
|
|
||||||
|
|
||||||
doPrepareTitleAndToc(EMPTY_DOC);
|
|
||||||
expect(getTocEl()).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should generate ToC entries if there is an `<h1>` heading', () => {
|
it('should generate ToC entries if there is an `<h1>` heading', () => {
|
||||||
doAddTitleAndToc(DOC_WITH_H1, 'foo');
|
doAddTitleAndToc(DOC_WITH_H1, 'foo');
|
||||||
|
@ -112,10 +112,15 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
|
|||||||
*/
|
*/
|
||||||
protected prepareTitleAndToc(targetElem: HTMLElement, docId: string): () => void {
|
protected prepareTitleAndToc(targetElem: HTMLElement, docId: string): () => void {
|
||||||
const titleEl = targetElem.querySelector('h1');
|
const titleEl = targetElem.querySelector('h1');
|
||||||
const hasToc = !!titleEl && !/no-?toc/i.test(titleEl.className);
|
const needsToc = !!titleEl && !/no-?toc/i.test(titleEl.className);
|
||||||
|
const embeddedToc = targetElem.querySelector('aio-toc.embedded');
|
||||||
|
|
||||||
if (hasToc) {
|
if (needsToc && !embeddedToc) {
|
||||||
|
// Add an embedded ToC if it's needed and there isn't one in the content already.
|
||||||
titleEl!.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
|
titleEl!.insertAdjacentHTML('afterend', '<aio-toc class="embedded"></aio-toc>');
|
||||||
|
} else if (!needsToc && embeddedToc) {
|
||||||
|
// Remove the embedded Toc if it's there and not needed.
|
||||||
|
embeddedToc.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
@ -127,7 +132,7 @@ export class DocViewerComponent implements DoCheck, OnDestroy {
|
|||||||
if (titleEl) {
|
if (titleEl) {
|
||||||
title = (typeof titleEl.innerText === 'string') ? titleEl.innerText : titleEl.textContent;
|
title = (typeof titleEl.innerText === 'string') ? titleEl.innerText : titleEl.textContent;
|
||||||
|
|
||||||
if (hasToc) {
|
if (needsToc) {
|
||||||
this.tocService.genToc(targetElem, docId);
|
this.tocService.genToc(targetElem, docId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user