Add anchor links to headers

This commit is contained in:
Jake Howard 2022-07-01 09:25:57 +01:00
parent 1c7917cb86
commit 03e9da57a1
Signed by: jake
GPG key ID: 57AFB45680EDD477
4 changed files with 33 additions and 5 deletions

View file

@ -1,6 +1,16 @@
section.content { section.content {
font-size: 1.25rem; font-size: 1.25rem;
padding-top: 0.5rem; padding-top: 0.5rem;
h2,
h3,
h4,
h5,
h6 {
.heading-anchor {
margin-right: 0.5rem;
}
}
} }
.content-details { .content-details {

View file

@ -10,7 +10,7 @@ from wagtail.images import get_image_model_string
from wagtail.models import Page from wagtail.models import Page
from .streamfield import get_blocks, get_html, get_word_count, truncate_streamfield from .streamfield import get_blocks, get_html, get_word_count, truncate_streamfield
from .utils import TocEntry, get_table_of_contents from .utils import TocEntry, add_heading_anchors, get_table_of_contents
class BasePage(Page): class BasePage(Page):
@ -68,6 +68,10 @@ class BaseContentMixin(models.Model):
def summary(self) -> str: def summary(self) -> str:
return truncate_streamfield(self.body, 50) return truncate_streamfield(self.body, 50)
@cached_property
def body_html(self):
return add_heading_anchors(str(self.body))
class ContentPage(BasePage, BaseContentMixin): # type: ignore[misc] class ContentPage(BasePage, BaseContentMixin): # type: ignore[misc]
subpage_types: list[Any] = [] subpage_types: list[Any] = []

View file

@ -1,13 +1,13 @@
{% extends "wagtail_base.html" %} {% extends "wagtail_base.html" %}
{% load wagtailcore_tags static %} {% load static %}
{% block content %} {% block content %}
{% include "common/hero.html" %} {% include "common/hero.html" %}
<section class="container content"> <section class="container content">
{% include_block page.body %} {{ page.body_html|safe }}
</section> </section>
{% endblock %} {% endblock %}

View file

@ -5,9 +5,12 @@ from typing import Type
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from django.conf import settings from django.conf import settings
from django.http.request import HttpRequest from django.http.request import HttpRequest
from django.utils.text import slugify
from wagtail.models import Page from wagtail.models import Page
from wagtail.models import get_page_models as get_wagtail_page_models from wagtail.models import get_page_models as get_wagtail_page_models
HEADER_TAGS = ["h2", "h3", "h4", "h5", "h6"]
@dataclass @dataclass
class TocEntry: class TocEntry:
@ -20,10 +23,10 @@ class TocEntry:
def get_table_of_contents(html: str) -> list[TocEntry]: def get_table_of_contents(html: str) -> list[TocEntry]:
soup = BeautifulSoup(html, "lxml") soup = BeautifulSoup(html, "lxml")
headings = soup.find_all(["h2", "h3", "h4", "h5", "h6"]) headings = soup.find_all(HEADER_TAGS)
heading_levels = [ heading_levels = [
TocEntry(tag.text, tag.text, int(tag.name[1]), []) for tag in headings TocEntry(tag.text, slugify(tag.text), int(tag.name[1]), []) for tag in headings
] ]
# Abort if there are no headings # Abort if there are no headings
@ -53,6 +56,17 @@ def get_table_of_contents(html: str) -> list[TocEntry]:
return root.children return root.children
def add_heading_anchors(html: str) -> str:
soup = BeautifulSoup(html, "lxml")
for tag in soup.find_all(HEADER_TAGS):
slug = slugify(tag.text)
anchor = soup.new_tag("a", href="#" + slug, id=slug)
anchor.string = "#"
anchor.attrs["class"] = "heading-anchor"
tag.insert(0, anchor)
return str(soup)
def get_page_models() -> list[Type[Page]]: def get_page_models() -> list[Type[Page]]:
page_models = get_wagtail_page_models().copy() page_models = get_wagtail_page_models().copy()
page_models.remove(Page) page_models.remove(Page)