Skip to content

Table of Content Component

Gabriel Walt edited this page Sep 6, 2021 · 34 revisions

Component that renders a table of content to help navigating the page content.

Tracking

This feature is tracked with issue #1816.

User Story

As an author, I want to insert a table of content to my page, so that visitors can easily navigate to the titles that are within the page.

Requirements

  • Lists all H1-H6 elements that are rendered on the page, regardless of which component renders them (core title or teaser components, custom components, RTE, etc).
  • Lists also those titles that are coming from the page template or from (nested) experience fragments.
  • Links to the corresponding component on the page by using the id attribute that is set on the closest parent from the title element.
  • Creates a nested list of the titles found on the page, rendering them either with UL/LI or OL/LI elements.
  • Authoring capabilities:
    • Capability to choose how to render the list (unordered list - the default, or ordered list).
    • Capability to restrict the start level (default being 1) and the end level (default being 6) of the titles listed.
    • Capability to restrict which title elements are to be listed based on their CSS class names, or of the class name of the elements containing them.
  • Other components that must function with the TOC component:
    • Capability for the Carousel, Tabs and Accordion to automatically show potentially hidden element when navigating to it.
    • Same to function also when loading a page with the hash in the url: automatically show the corresponding Carousel, Tabs and Accordion element.
  • The component must not impact page speed in any substantial ways:
    • It must not rely on JavaScript that might slow down the page in any way.
    • It must avoid any cumulative layout shifts that could be introduced by client-side rendering.
    • It must not slow down the first byte sent to the browser.
    • Any HTML rewriter must not block the output, or at least not until the placeholder of the TOC component is reached on the page.

Dialog

Edit Dialog

  • "List type" label, with info popup "Whether to display the table of content as an unordered list of bullet points, or as an ordered listed of numbered items".
    Drop-down with options "unordered list" and "ordered list", defaulting to "unordered list".
    Option is hidden when the corresponding design configuration is not set to "no restriction".
  • "Title start level" label, with info popup "The minimum title level to report in the table of content.". "
    Drop-down with options "1"-"6", defaulting to "1".
    Option is hidden when the corresponding design configuration is not set to "no restriction".
  • "Title stop level" label, with info popup "The maximum title level to report in the table of content.". "\ Drop-down with options "1"-"6", defaulting to "6".
    Option is hidden when corresponding design configuration is not set to "no restriction".

Design Dialog

  • "Settings" tab
    • "Restrict list type" label, with info popup "Whether the author should be able to choose the list type or not."
      Drop-down with options "no restriction", "unordered list" or "ordered list", defaulting to "no restriction".
    • "Restrict title start level" label, with info popup "Whether the author should be able to choose the the minimum title level to report in the table of content."
      Drop-down with options "no restriction" and "1"-"6", defaulting to "no restriction".
    • "Restrict title stop level" label, with info popup "Whether the author should be able to choose the the maximum title level to report in the table of content."\ Drop-down with options "no restriction", 1-6, defaulting to "no restriction".
    • "Include class names" label, with info popup "If set, only titles with those class names, or contained within elements of the indicated class names will be considered."
      Multi-list of text inputs.
    • "Ignore class names" label, with info popup "If set, titles with those class names, or contained within elements of the indicated class names will be ignored."
      Multi-list of text inputs.
  • "Styles" tab with options as for any other core component.

Include & Ignore Behavior

When both options the include and ignore class names are specified, then the component should also be able to correctly consider nested include and ignore classes.

Let's take an example, assuming the following markup:

<h1 class="cmp-title">Page title</h1>
<div class="main">
    <div class="cmp-title">
        <h2>Main title</h2>
    </div>
    <div class="cmp-teaser">
        <h2 class="cmp-teaser__title">Main teaser</h2>
        <h3 class="cmp-teaser__subtitle">Main teaser subtitle</h3>
    </div>
</div>
<div class="sidebar">
    <div class="cmp-title">
        <h2>Sidebar title</h2>
    </div>
    <div class="cmp-teaser">
        <h2 class="cmp-teaser__title">Sidebar teaser</h2>
        <h3 class="cmp-teaser__subtitle">Sidebar teaser subtitle</h3>
    </div>
</div>

Below are different configurations and how each should render the table of content for that markup.

1. Include=none and Ignore=none:

* Page title
    * Main title
    * Main teaser
        * Main teaser subtitle
    * Sidebar title
    * Sidebar teaser
        * Sidebar teaser subtitle

2. Include=none and Ignore="sidebar":

* Page title
    * Main title
    * Main teaser
        * Main teaser subtitle

3. Include="cmp-title" and Ignore=none:

* Page title
    * Main title
    * Sidebar title

4. Include="cmp-title, cmp-teaser__title" and Ignore=none:

* Page title
    * Main title
    * Main teaser
    * Sidebar title
    * Sidebar teaser

5. Include="cmp-title" and Ignore="sidebar":

* Page title
    * Main title

6. Include="cmp-title, cmp-teaser" and Ignore="sidebar, cmp-teaser__subtitle":

* Page title
    * Main title
    * Main teaser

7. Include="cmp-title, main" and Ignore="cmp-teaser__subtitle":

* Page title
    * Main title
    * Main teaser
    * Sidebar title

Nesting Behavior

Displaying the titles in a nested list can be tricky when authors jump some title levels in their content as it is not desired to have a table of content with empty levels displayed. To correctly display nesting, it helps to think of nesting as a parent to child relationship: an item should only be nested when it also has a corresponding parent. So when an H1 is followed by an H3, then the hierarchy built by the table of content should consider the H3 as a direct child of the H1 component (without any intermediate node displayed). Below are listed a few examples that try to push this logic to its limit, illustrating what the resulting table of content should be. Those can potentially serve as test cases to verify the nesting logic.

1. Simple example:

h1. Bla  |  * Bla
h2. Bli  |      * Bli
h3. Blo  |          * Blo
h4. Blu  |              * Blu

2. Simple example:

h1. Bla  |  * Bla
h2. Bli  |      * Bli
h1. Blo  |  * Blo
h2. Blu  |      * Blu

3. Ignore unused levels in-between:

h1. Bla  |  * Bla
h3. Bli  |      * Bli
h5. Blo  |          * Blo
h6. Blu  |              * Blu

4. Ignore unused levels at start:

h3. Bla  |  * Bla
h4. Bli  |      * Bli
h3. Blo  |  * Blo
h4. Blu  |      * Blu

5. Don't nest items without a parent:

h4. Bla  |  * Bla
h3. Bli  |  * Bli
h2. Blo  |  * Blo
h4. Blu  |      * Blu

6. Don't double-nest child items:

h1. Bla  |  * Bla
h3. Bli  |      * Bli
h2. Blo  |      * Blo
h3. Blu  |          * Blu

7. Combining all complexities:

h3. Bla  |  * Bla
h6. Bli  |      * Bli
h5. Blo  |      * Blo
h2. Blu  |  * Blu
h3. Ble  |      * Ble