Skip to content

Commit

Permalink
Merge branch 'integration/2024-evolution' into feature/twe-21-seconda…
Browse files Browse the repository at this point in the history
…ry-nav-fe
  • Loading branch information
shyusu4 committed Feb 10, 2025
2 parents 5ac1d25 + ee5b592 commit 1ffadb9
Show file tree
Hide file tree
Showing 9 changed files with 201 additions and 8 deletions.
1 change: 1 addition & 0 deletions tbx/core/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ class Meta:

class FeaturedPageCardBlock(blocks.StructBlock):
heading = blocks.CharBlock(required=False)
subheading = blocks.CharBlock(required=False)
description = blocks.RichTextBlock(features=settings.NO_HEADING_RICH_TEXT_FEATURES)
image = ImageChooserBlock()
link_text = blocks.CharBlock()
Expand Down
8 changes: 8 additions & 0 deletions tbx/core/tests/test_division_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,14 @@ def test_division_selected(self):
self.assertEqual(self.service_1.final_division, self.division_1)
self.assertEqual(service_3.final_division, self.division_2)

def test_division_self(self):
"""
For a division page,
final_division should return the page itself.
"""
self.assertEqual(self.division_1.final_division, self.division_1)
self.assertEqual(self.division_2.final_division, self.division_2)

def test_division_selected_on_ancestor(self):
"""
For a page that does not have a division selected
Expand Down
121 changes: 121 additions & 0 deletions tbx/core/tests/test_navigation_mixin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
from wagtail.models import Site
from wagtail.test.utils import WagtailPageTestCase

from tbx.core.factories import HomePageFactory
from tbx.divisions.factories import DivisionPageFactory
from tbx.navigation.factories import NavigationSetFactory
from tbx.services.factories import ServiceAreaPageFactory


class TestNavigationMixin(WagtailPageTestCase):
@classmethod
def setUpTestData(cls):
super().setUpTestData()

# Set up the site & homepage.
site = Site.objects.get(is_default_site=True)
root = site.root_page.specific
cls.home = HomePageFactory(parent=root)

site.root_page = cls.home
site.save()

# Set up a navigation set.
cls.nav_set_1 = NavigationSetFactory(name="Charity nav set")
cls.nav_set_2 = NavigationSetFactory(name="Public sector nav set")

# Set up a division page.
cls.division_1 = DivisionPageFactory(
override_navigation_set=cls.nav_set_1,
parent=cls.home,
)
cls.division_2 = DivisionPageFactory(
title="Public sector",
override_navigation_set=cls.nav_set_2,
parent=cls.home,
)

# Set up a services "index" page.
cls.services = ServiceAreaPageFactory(title="Services", parent=cls.home)
cls.service_1 = ServiceAreaPageFactory(title="Service 1", parent=cls.services)
cls.service_2 = ServiceAreaPageFactory(title="Service 2", parent=cls.service_1)

def test_navigation_set_selected(self):
"""
For a page that has an override navigation set selected,
navigation_set should return the selected navigation set.
"""
self.service_1.override_navigation_set = self.nav_set_1
self.service_1.save()

service_3 = ServiceAreaPageFactory(
override_navigation_set=self.nav_set_2,
parent=self.service_2,
title="Service 3",
)

self.assertEqual(self.service_1.navigation_set, self.nav_set_1)
self.assertEqual(service_3.navigation_set, self.nav_set_2)

def test_navigation_set_selected_on_ancestor(self):
"""
For a page that does not have a navigation set selected
but an ancestor page has a navigation set selected,
navigation_set should return the ancestor's selected navigation set.
"""
self.service_1.override_navigation_set = self.nav_set_1
self.service_1.save()

service_3 = ServiceAreaPageFactory(
parent=self.service_2,
title="Service 3",
)

self.assertEqual(self.service_1.navigation_set, self.nav_set_1)
self.assertEqual(self.service_2.navigation_set, self.nav_set_1)
self.assertEqual(service_3.navigation_set, self.nav_set_1)

def test_division_with_navigation_set_selected_on_ancestor(self):
"""
For a page that does not have a navigation set selected
but an ancestor page has a division with a navigation set selected,
navigation_set should return that ancestor's selected navigation set.
"""
self.service_1.division = self.division_2
self.service_1.save()

service_3 = ServiceAreaPageFactory(
parent=self.service_2,
title="Service 3",
)
service_4 = ServiceAreaPageFactory(
division=self.division_1,
parent=service_3,
title="Service 4",
)
service_5 = ServiceAreaPageFactory(
parent=service_4,
title="Service 5",
)

self.assertEqual(self.service_1.navigation_set, self.nav_set_2)
self.assertEqual(self.service_2.navigation_set, self.nav_set_2)
self.assertEqual(service_3.navigation_set, self.nav_set_2)
self.assertEqual(service_4.navigation_set, self.nav_set_1)
self.assertEqual(service_5.navigation_set, self.nav_set_1)

def test_no_navigation_set(self):
"""
For a page that does not have a navigation set selected
and is not a descendant of a DivisionPage with a navigation set
nor has an ancestor with a selected navigation set,
navigation_set should return None.
"""
service_3 = ServiceAreaPageFactory(
parent=self.service_2,
title="Service 3",
)

self.assertIsNone(self.service_1.navigation_set)
self.assertIsNone(self.service_2.navigation_set)
self.assertIsNone(service_3.navigation_set)
28 changes: 21 additions & 7 deletions tbx/core/utils/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,31 +66,45 @@ def navigation_set(self):
Returns a NavigationSet.
If a navigation set field is set on the current page, use that.
Or if a division with a navigation set is selected, use that.
If not, check the ancestors.
The closest ancestor that fulfills one of the following will be followed:
- the navigation set field is populated, OR
- the ancestor page is a DivisionPage.
- the division field is populated with a DivisionPage that has a navigation set.
"""
from tbx.divisions.models import DivisionPage

if self.override_navigation_set:
return self.override_navigation_set

# If a division page with a navigation set is selected, use that.
if getattr(self, "division", None) and getattr(
self.division, "override_navigation_set", None
):
return self.division.override_navigation_set

try:
division_page = next(
getattr(p, "division", None) or p
page = next(
p
for p in self.get_ancestors()
.filter(depth__gt=2)
.specific()
.defer_streamfields()
.order_by("-depth")
if isinstance(getattr(p, "division", None) or p, DivisionPage)
if (
getattr(p, "override_navigation_set", None)
or (
getattr(p, "division", None)
and getattr(p.division, "override_navigation_set", None)
)
)
)
except StopIteration:
division_page = None
page = None

return division_page and division_page.override_navigation_set
return page and (
page.override_navigation_set or page.division.override_navigation_set
)


# Generic social fields abstract class to add social image/text to any new content type easily.
Expand Down
11 changes: 11 additions & 0 deletions tbx/navigation/factories.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import factory
from factory.django import DjangoModelFactory

from tbx.navigation.models import NavigationSet


class NavigationSetFactory(DjangoModelFactory):
class Meta:
model = NavigationSet

name = factory.Faker("text", max_nb_chars=20)
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
class Migration(migrations.Migration):
dependencies = [
("navigation", "0006_use_custom_streamfield"),
("wagtailcore", "0094_alter_page_locale"),
]

operations = [
Expand All @@ -25,6 +26,21 @@ class Migration(migrations.Migration):
),
("name", models.CharField(max_length=255)),
("navigation", tbx.core.utils.fields.StreamField(block_lookup={})),
(
"latest_revision",
models.ForeignKey(
blank=True,
editable=False,
null=True,
on_delete=models.deletion.SET_NULL,
related_name="+",
to="wagtailcore.revision",
verbose_name="latest revision",
),
),
],
options={
"abstract": False,
},
),
]
13 changes: 12 additions & 1 deletion tbx/navigation/models.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
from django.contrib.contenttypes.fields import GenericRelation
from django.core.cache import cache
from django.core.cache.utils import make_template_fragment_key
from django.db import models

from modelcluster.models import ClusterableModel
from wagtail.admin.panels import FieldPanel
from wagtail.contrib.settings.models import BaseSiteSetting, register_setting
from wagtail.models import RevisionMixin
from wagtail.snippets.models import register_snippet

from tbx.core.utils.fields import StreamField
Expand All @@ -17,7 +19,7 @@


@register_snippet
class NavigationSet(models.Model):
class NavigationSet(RevisionMixin, models.Model):
name = models.CharField(max_length=255)
navigation = StreamField(
[
Expand All @@ -26,9 +28,18 @@ class NavigationSet(models.Model):
],
)

# This will let us do revision.navigation_set
_revisions = GenericRelation(
"wagtailcore.Revision", related_query_name="navigation_set"
)

def __str__(self):
return self.name

@property
def revisions(self):
return self._revisions


@register_setting(icon="list-ul")
class NavigationSettings(BaseSiteSetting, ClusterableModel):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
{% srcset_image card.image format-webp fill-{750x420,650x420} sizes="(max-width: 750px) 100vw, (min-width: 751px) 650px" alt="" %}
<div class="featured-services__text">
<h3 class="featured-services__heading heading heading--two-b">{% firstof card.heading card.page.title %}</h3>
{% if card.subheading %}
<p class="featured-services__subheading">{{ card.subheading }}</p>
{% endif %}
<div class="featured-services__description rich-text">{{ card.description|richtext }}</div>
</div>
{# The title and icon need to be on the same line with no whitespace to prevent the arrow being orphaned on a new line #}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,25 @@ context:
intro: 'Discover how we can help you achieve your goals through our range of services.'
cards:
- heading: Advocacy, Engagement & Income
subheading: ''
description: '<p>Grow your audience, mobilise and increase your income through design, communication and strategy</p>'
link_text: Our services
accessible_link_text: Our Advocacy, Engagement & Income services
- heading: Measure, Analyse & Optimise
subheading: ''
description: '<p>Organise your data to gain the insights and confidence needed to boost your charity&apos;s performance.</p>'
link_text: Our services
accessible_link_text: Our Measure, Analyse & Optimise services
- heading: Scale impact through Technology
subheading: ''
description: '<p>We design technology that&apos;s built for humans. Scale your impact and better deliver your services by leveraging our expertise.</p>'
link_text: Our services
accessible_link_text: Our services on scaling impact through Technology
- heading: Explore
subheading: Discover the whole problem.
description: '<p>Use data-led insights to understand, evaluate and amplify the impact of your services. Grow awareness and ensure your services are reaching the people you serve.</p>'
link_text: Our services
accessible_link_text: Our services on scaling impact through Technology

tags:
srcset_image:
Expand Down

0 comments on commit 1ffadb9

Please sign in to comment.