diff --git a/src/app/content/components/Assigned.tsx b/src/app/content/components/Assigned.tsx
index 708e57e4d0..a803be3425 100644
--- a/src/app/content/components/Assigned.tsx
+++ b/src/app/content/components/Assigned.tsx
@@ -109,7 +109,7 @@ export default () => {
-
+
{prevNext
? {
);
};
+ describe('Content tweaks for assignable', () => {
+ let pageElement: HTMLElement;
+
+ const htmlHelper = async(html: string) => {
+ archiveLoader.mock.cachedPage.mockImplementation(() => ({
+ ...page,
+ content: html,
+ }));
+
+ const {root} = renderToDom(
+
+
+
+
+
+
+
+
+
+ );
+ const query = root.querySelector('#main-content');
+
+ if (!query) {
+ return expect(query).toBeTruthy();
+ }
+ pageElement = query;
+
+ // page lifecycle hooks
+ await Promise.resolve();
+
+ return pageElement.innerHTML;
+ };
+
+ it('changes heading levels when specified', async() => {
+ jest.spyOn(selectNavigation, 'query').mockReturnValue({
+ section: [page.id, shortPage.id],
+ });
+ jest.spyOn(select, 'book')
+ .mockReturnValue(formatBookData(book, mockCmsBook));
+
+ expect(await htmlHelper('Largest heading
Second largest heading
'))
+ .toEqual('Largest heading
Second largest heading
');
+ });
+ });
+
describe('Content tweaks for generic styles', () => {
let pageElement: HTMLElement;
diff --git a/src/app/content/components/Page/PageComponent.tsx b/src/app/content/components/Page/PageComponent.tsx
index da426581ff..46c53e6993 100644
--- a/src/app/content/components/Page/PageComponent.tsx
+++ b/src/app/content/components/Page/PageComponent.tsx
@@ -47,7 +47,7 @@ export default class PageComponent extends Component {
contentLinks.reduceReferences(parsedContent, this.props.contentLinks);
lazyResources.makeResourcesLazy(parsedContent);
- transformContent(parsedContent, parsedContent.body, this.props.intl, services);
+ transformContent(parsedContent, parsedContent.body, this.props, services);
if (this.props.lockNavigation) {
linksToOtherPagesOpenInNewTab(parsedContent.body, this.props.currentPath);
diff --git a/src/app/content/components/Page/connector.ts b/src/app/content/components/Page/connector.ts
index 54165bb51b..30d39f86bf 100644
--- a/src/app/content/components/Page/connector.ts
+++ b/src/app/content/components/Page/connector.ts
@@ -34,6 +34,7 @@ export interface PagePropTypes {
textSize: TextResizerValue;
lockNavigation: boolean;
ToastOverride: StyledComponent<'div', any, {}, never>;
+ topHeadingLevel?: number;
}
export default connect(
diff --git a/src/app/content/components/Page/contentDOMTransformations.ts b/src/app/content/components/Page/contentDOMTransformations.ts
index 6791541c86..7848f5e002 100644
--- a/src/app/content/components/Page/contentDOMTransformations.ts
+++ b/src/app/content/components/Page/contentDOMTransformations.ts
@@ -21,15 +21,19 @@ export function linksToOtherPagesOpenInNewTab(rootEl: HTMLElement, currentPath:
// .../src/scripts/modules/media/body/body.coffee#L123
// We are passing Document because it is required to prerender.
export const transformContent = (
- document: Document, rootEl: HTMLElement, intl: IntlShape, services: AppServices & MiddlewareAPI
+ document: Document,
+ rootEl: HTMLElement,
+ props: { intl: IntlShape, topHeadingLevel?: number },
+ services: AppServices & MiddlewareAPI
) => {
removeDocumentTitle(rootEl);
+ if (props.topHeadingLevel) { changeHeadingLevels(document, rootEl, props.topHeadingLevel); }
wrapElements(document, rootEl);
tweakFigures(rootEl);
fixLists(rootEl);
- wrapSolutions(document, rootEl, intl);
+ wrapSolutions(document, rootEl, props.intl);
expandSolutionForFragment(document);
- moveFootnotes(document, rootEl, intl);
+ moveFootnotes(document, rootEl, props.intl);
optimizeImages(rootEl, services);
};
@@ -42,6 +46,40 @@ function removeDocumentTitle(rootEl: HTMLElement) {
].join(',')).forEach((el) => el.remove());
}
+// set the top heading's level to topHeadingLevel and adjust other headings accordingly
+function changeHeadingLevels(document: Document, rootEl: HTMLElement, topHeadingLevel: number) {
+ const headingLevels = [ 1, 2, 3, 4, 5, 6 ];
+ const currentTopHeading = headingLevels.find((level) => rootEl.querySelectorAll(`h${level}`)?.length);
+
+ if (!currentTopHeading || topHeadingLevel === currentTopHeading) {
+ return;
+ }
+
+ const differenceInLevels = topHeadingLevel - currentTopHeading;
+
+ headingLevels.forEach((level) => {
+ const origTagName = `h${level}`;
+ const tags = rootEl.querySelectorAll(origTagName);
+
+ if (tags.length > 0) {
+ const newTagName = `h${level + differenceInLevels}`;
+
+ tags.forEach((tag) => {
+ const contents = tag.innerHTML;
+ const newTagEl = document.createElement(newTagName);
+
+ Array.from(tag.attributes).forEach((attr) => {
+ newTagEl.setAttribute(attr.name, attr.value);
+ });
+
+ newTagEl.innerHTML = contents;
+
+ tag.replaceWith(newTagEl);
+ });
+ }
+ });
+}
+
// Wrap title and content elements in header and section elements, respectively
function wrapElements(document: Document, rootEl: HTMLElement) {
rootEl.querySelectorAll(`.example, .exercise, .note, .abstract,