Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Topic page enhancements #96

Merged
merged 19 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions cms/articles/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from django.http import HttpRequest
from django.http.response import HttpResponseRedirect
from django.template.response import TemplateResponse
from django.utils.functional import Promise
from wagtail.admin.panels import Panel


Expand Down Expand Up @@ -201,6 +202,10 @@ def get_admin_display_title(self) -> str:
"""Changes the admin display title to include the parent title."""
return f"{self.get_parent().title}: {self.draft_title or self.title}"

@property
def label(self) -> "Promise":
return _("Article")

@property
def display_title(self) -> str:
"""Returns the page display title. If the news headline is set, it takes precedence over the series+title."""
Expand Down
4 changes: 2 additions & 2 deletions cms/articles/tests/factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class HeadlineFigureBlockFactory(wagtail_factories.StructBlockFactory):
trend = wagtail_factories.CharBlockFactory()


class ArticleSeriesFactory(wagtail_factories.PageFactory):
class ArticleSeriesPageFactory(wagtail_factories.PageFactory):
"""Factory for ArticleSeriesPage."""

class Meta:
Expand All @@ -36,7 +36,7 @@ class Meta:
django_get_or_create: ClassVar[list[str]] = ["slug", "parent"]

title = factory.Faker("sentence", nb_words=4)
parent = factory.SubFactory(ArticleSeriesFactory)
parent = factory.SubFactory(ArticleSeriesPageFactory)

summary = factory.Faker("text", max_nb_chars=100)
news_headline = factory.Faker("text", max_nb_chars=50)
Expand Down
4 changes: 2 additions & 2 deletions cms/articles/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from django.utils.formats import date_format
from wagtail.test.utils import WagtailTestUtils

from cms.articles.tests.factories import ArticleSeriesFactory, StatisticalArticlePageFactory
from cms.articles.tests.factories import ArticleSeriesPageFactory, StatisticalArticlePageFactory
from cms.core.tests.factories import ContactDetailsFactory


Expand All @@ -16,7 +16,7 @@ class ArticleSeriesTestCase(WagtailTestUtils, TestCase):

@classmethod
def setUpTestData(cls):
cls.series = ArticleSeriesFactory()
cls.series = ArticleSeriesPageFactory()

def test_index_redirect_404_with_no_subpages(self):
"""Test index path redirects to latest."""
Expand Down
6 changes: 6 additions & 0 deletions cms/core/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from django.conf import settings
from django.utils.decorators import method_decorator
from django.utils.functional import cached_property
from django.utils.translation import gettext_lazy as _
from wagtail.models import Page
from wagtail.query import PageQuerySet

Expand All @@ -13,6 +14,7 @@

if TYPE_CHECKING:
from django.db import models
from django.utils.functional import Promise
from wagtail.admin.panels import FieldPanel
from wagtail.contrib.settings.models import (
BaseGenericSetting as _WagtailBaseGenericSetting,
Expand Down Expand Up @@ -58,6 +60,10 @@ class Meta:
*SocialFieldsMixin.promote_panels,
]

@property
def label(self) -> "Promise":
return _("Page")

@cached_property
def related_pages(self) -> PageQuerySet:
"""Return a `PageQuerySet` of items related to this page via the
Expand Down
134 changes: 134 additions & 0 deletions cms/core/tests/test_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
from datetime import datetime

from django.test import TestCase
from django.utils.formats import date_format

from cms.core.models.base import BasePage
from cms.core.utils import get_formatted_pages_list


# DummyPage mimics the minimum attributes and methods of a Wagtail Page.
class DummyPage(BasePage):
def __init__(self, title, summary="", listing_summary="", url="https://ons.gov.uk", **kwargs): # pylint: disable=super-init-not-called
# this just set attributes manually.
self.title = title
self.summary = summary
self.listing_summary = listing_summary
self._url = url
self._release_date = kwargs.get("release_date")

def get_url(self, request=None, current_site=None):
return self._url

@property
def release_date(self):
return self._release_date

class Meta:
abstract = True


class DummyPageWithNoReleaseDate(DummyPage):
@property
def label(self):
return "Dummy Page"

class Meta:
abstract = True


class GetFormattedPagesListTests(TestCase):
def test_without_release_date_and_listing_summary(self):
# When no listing_summary and release_date, should use summary for description,
# and use the default label.
page = DummyPage(title="Test Page", summary="Test summary", listing_summary="")
result = get_formatted_pages_list([page])
expected = {
"title": {"text": "Test Page", "url": "https://ons.gov.uk"},
"metadata": {"object": {"text": "Page"}},
"description": "Test summary",
}
self.assertEqual(len(result), 1)
self.assertDictEqual(result[0], expected)

def test_with_listing_summary_overrides_summary(self):
# When listing_summary is provided, that should be used as description.
page = DummyPage(title="Test Page", summary="Test summary", listing_summary="Listing summary")
result = get_formatted_pages_list([page])
expected = {
"title": {"text": "Test Page", "url": "https://ons.gov.uk"},
"metadata": {"object": {"text": "Page"}},
"description": "Listing summary",
}
self.assertEqual(len(result), 1)
self.assertDictEqual(result[0], expected)

def test_with_custom_label(self):
# When a custom label is defined, it should be used in metadata.
page = DummyPageWithNoReleaseDate(title="Test Page", summary="Test summary", listing_summary="")
result = get_formatted_pages_list([page])
expected = {
"title": {"text": "Test Page", "url": "https://ons.gov.uk"},
"metadata": {"object": {"text": "Dummy Page"}},
"description": "Test summary",
}
self.assertEqual(len(result), 1)
self.assertDictEqual(result[0], expected)

def test_with_release_date(self):
# When release_date is provided, metadata should include date formatting.
test_date = datetime(2024, 1, 1, 12, 30)
page = DummyPage(title="Test Page", summary="Test summary", listing_summary="", release_date=test_date)
result = get_formatted_pages_list([page])

expected_iso = date_format(test_date, "c")
expected_short = date_format(test_date, "DATE_FORMAT")

expected = {
"title": {"text": "Test Page", "url": "https://ons.gov.uk"},
"metadata": {
"object": {"text": "Page"},
"date": {
"prefix": "Released",
"showPrefix": True,
"iso": expected_iso,
"short": expected_short,
},
},
"description": "Test summary",
}
self.assertEqual(len(result), 1)
self.assertDictEqual(result[0], expected)

def test_multiple_pages(self):
# Test processing multiple dummy pages
test_date = datetime(2024, 1, 1, 12, 30)
page1 = DummyPage(title="Page One", summary="Summary One", listing_summary="", release_date=test_date)
page2 = DummyPageWithNoReleaseDate(title="Page Two", summary="Summary Two", listing_summary="Listing Two")
pages = [page1, page2]
result = get_formatted_pages_list(pages)

expected_iso = date_format(test_date, "c")
expected_short = date_format(test_date, "DATE_FORMAT")

expected_page1 = {
"title": {"text": "Page One", "url": "https://ons.gov.uk"},
"metadata": {
"object": {"text": "Page"},
"date": {
"prefix": "Released",
"showPrefix": True,
"iso": expected_iso,
"short": expected_short,
},
},
"description": "Summary One",
}
expected_page2 = {
"title": {"text": "Page Two", "url": "https://ons.gov.uk"},
"metadata": {"object": {"text": "Dummy Page"}},
"description": "Listing Two",
}
self.assertEqual(len(result), 2)
self.assertDictEqual(result[0], expected_page1)
self.assertDictEqual(result[1], expected_page2)
45 changes: 45 additions & 0 deletions cms/core/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from typing import TYPE_CHECKING, Any, Optional, TypedDict

from django.db.models import QuerySet
from django.utils.formats import date_format
from django.utils.translation import gettext_lazy as _

if TYPE_CHECKING:
from django.http import HttpRequest
from wagtail.models import Page


class DocumentListItem(TypedDict):
title: dict[str, str]
metadata: dict[str, Any]
description: str


def get_formatted_pages_list(
pages: list["Page"] | QuerySet["Page"], request: Optional["HttpRequest"] = None
) -> list[DocumentListItem]:
"""Returns a formatted list of page data for the documentList DS macro.
See the search results section in https://service-manual.ons.gov.uk/design-system/components/document-list.
"""
data = []
for page in pages:
datum: DocumentListItem = {
"title": {
"text": getattr(page, "display_title", page.title),
"url": page.get_url(request=request),
},
"metadata": {
"object": {"text": getattr(page, "label", _("Page"))},
},
"description": getattr(page, "listing_summary", "") or getattr(page, "summary", ""),
}
if release_date := page.release_date:
datum["metadata"]["date"] = {
"prefix": _("Released"),
"showPrefix": True,
"iso": date_format(release_date, "c"),
"short": date_format(release_date, "DATE_FORMAT"),
}
data.append(datum)
return data
26 changes: 26 additions & 0 deletions cms/jinja2/templates/components/featured/featured-article.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<div class="featured-article">
<h3 class="ons-u-fs-m ons-u-mt-no ons-u-mb-2xs">
<a href="{{ pageurl(article) }}">{{ article.display_title }}</a>
</h3>
<ul class="featured-article__metadata">
<li class="featured-article__attribute ons-u-fs-s">
<span class="ons-u-fw-b">{{ _("Released") }}:</span>
<time datetime="{{ article.release_date|date("c") }}">{{ article.release_date|date("DATE_FORMAT") }}</time>
</li>
<li class="featured-article__attribute ons-u-fs-s">
<span class="ons-u-fw-b">{{ _("Article") }}</span>
</li>
</ul>

{% if article.listing_image %}
<div class="featured-article__embed-container">
{{ image(article.listing_image, "width-1000") }}
</div>
{% endif %}

{% if article.main_points_summary %}
<div class="featured-article__description">
{{ article.main_points_summary|richtext }}
</div>
{% endif %}
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
{% from "components/document-list/_macro.njk" import onsDocumentList %}
{{ onsDocumentList({"documents": formatted_items}) }}
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ <h1 class="ons-u-fs-3xl common-header__heading">
</div>
<div class="ons-grid__col ons-col-8@m ons-col-12@s ons-u-p-no@2xs@m">
<section id="summary" class="spacing">
<h2>Summary</h2>
<h2>{{ _("Summary") }}</h2>
{{ page.summary|richtext() }}
</section>

Expand Down
55 changes: 52 additions & 3 deletions cms/jinja2/templates/pages/topic_page.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
{% extends "templates/base_page.html" %}
{% from "components/table-of-contents/_macro.njk" import onsTableOfContents %}
{% from "components/document-list/_macro.njk" import onsDocumentList %}

{% block header_area %}
<div class="topic-header">
Expand All @@ -21,9 +23,56 @@ <h1 class="ons-u-fs-3xl common-header__heading">
</div>
</div>
</div>
{% endblock %}

{% block main %}
<div class="ons-grid ons-grid-flex-gap ons-grid-flex-gap--32 ons-js-toc-container">
<div class="ons-grid__col ons-grid__col--sticky@m ons-col-4@m">
{% with toc_title=_("Contents"), toc_aria_label=_("Sections in this page") %}
{# fmt:off #}
{{-
onsTableOfContents({
"title": toc_title,
"ariaLabel": toc_aria_label,
"itemsList": table_of_contents
})
}}
{# fmt:on #}
{% endwith %}
</div>

<div class="ons-grid__col ons-col-8@m">
{% if featured_item %}
<section id="featured" class="spacing">
<h2>{{ _("Featured") }}</h2>

{% with article=featured_item %}
{% include "templates/components/featured/featured-article.html" %}
{% endwith %}
</section>
{% endif %}

{% if page.headline_figures %}
{% include_block page.headline_figures %}
{% endif %}
{% if formatted_articles %}
<section id="related-articles" class="spacing">
<h2>{{ _("Related articles") }}</h2>
{{ onsDocumentList({"documents": formatted_articles}) }}
</section>
{% endif %}

{% if formatted_methodologies %}
<section id="related-methods" class="spacing">
<h2>{{ _("Methods and quality information") }}</h2>
{{ onsDocumentList({"documents": formatted_methodologies}) }}
</section>
{% endif %}

{% if page.explore_more %}
<section id="explore-more">
<h2>{{ _("Explore more") }}</h2>

{% include_block page.explore_more %}
</section>
{% endif %}
</div>
</div>
{% endblock %}
11 changes: 11 additions & 0 deletions cms/methodology/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@
from cms.core.query import order_by_pk_position

if TYPE_CHECKING:
import datetime

from django.utils.functional import Promise
from wagtail.admin.panels import Panel
from wagtail.query import PageQuerySet

Expand Down Expand Up @@ -97,6 +100,14 @@ def get_context(self, request: HttpRequest, *args: Any, **kwargs: Any) -> dict:
context["related_publications"] = self.get_formatted_related_publications_list(request=request)
return context

@property
def label(self) -> "Promise":
return _("Methodology")

@property
def release_date(self) -> "datetime.date":
return self.publication_date

@cached_property
def related_publications(self) -> "PageQuerySet":
"""Return a `PageQuerySet` of the StatisticalArticlePage page model via the
Expand Down
Loading
Loading