Skip to content

Commit

Permalink
Merge pull request #257 from nhsuk/frontend-v9
Browse files Browse the repository at this point in the history
Updating nhsuk frontend library to v9
  • Loading branch information
pflynny authored Feb 13, 2025
2 parents 9cd8b97 + 7519b19 commit 24b2a99
Show file tree
Hide file tree
Showing 19 changed files with 372 additions and 95 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@
- Upgrade for Wagtail 5.0+ compatibility
- Drop tests for Wagtail < 5.2 due to EOL
- Drop tests for Django < 4.2
- Use the latest version of the NHS.UK frontend library ([v9.1.0](https://github.com/nhsuk/nhsuk-frontend/blob/main/CHANGELOG.md#910---4-november-2024))
- Header update inline with the frontend library
- Add column-based footer functionality with customisable columns and links
- Update footer template to support column-based layout
- Add FooterSettingsColumns and FooterColumnLinks models for footer customisation


## 1.5.3

Expand Down
92 changes: 92 additions & 0 deletions docs/components/footer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Footer

## Wagtail Site Settings

wagtail-nhs-style comes with a wagtail
[site settings](http://docs.wagtail.io/en/v2.4/reference/contrib/settings.html)
model. If you want to allow CMS users to configure the footer in the wagtail
interface, use of the site setting is recommended.

Add the `wagtailnhsukfrontend.settings` module to your `INSTALLED_APPS` config.

```python
INSTALLED_APPS = [
...
'wagtailnhsukfrontend',
'wagtailnhsukfrontend.settings',
...
]
```

This will create a new option under `Settings > Footer settings` in the
wagtail interface.

To include the footer in your template, use the `header` templatetag.

```python
{% load nhsukfrontendsettings_tags %}

...

<body>
...
{% footer %}
</body>
```
---

The NHS.UK footer component can be configured in two styles:

---

## Column-based Footer

The column-based footer allows content to be organised into up to 4 columns. Each column can contain multiple links to either internal Wagtail pages or external URLs.

### Enabling Column-based Footer

1. In Wagtail Admin, navigate to **Settings > Footer Column Settings**
2. Tick **"Enable column-based footer"**
3. Add footer links using the **"Footer Column Links"** panel
4. For each link:
- Select the column number (1–4)
- Enter the link label
- Choose either:
- An internal page using **"Link page"**
- An external URL using **"Link URL"**

### Template Usage

```django
{% include 'wagtailnhsukfrontend/footer.html' %}
```

The template automatically detects which footer style to use based on your settings.

## Standard Footer
The standard footer provides a single column of links and can be configured through Settings > Footer Settings.

### Configuration
1. In Wagtail Admin, navigate to Settings > Footer Settings
2. Add footer links using the "Footer Links" panel
3. For each link:
- Enter the URL
- Enter the link label

### Template Usage
```django
{% include 'wagtailnhsukfrontend/footer.html' %}
```

### Customisation
Both footer styles include:

- NHS copyright notice
- Semantic HTML with proper ARIA roles
- Responsive design following NHS.UK frontend standards


## Reference

* [Service Manual](https://service-manual.nhs.uk/design-system/components/footer)
* [Frontend Library](https://github.com/nhsuk/nhsuk-frontend/tree/master/packages/components/footer)
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

setup(
name="wagtail-nhsuk-frontend",
version="1.5.3",
version="1.6.0",
description="NHSUK Frontend Styles for Wagtail",
author="Brad Morton",
author_email="<[email protected]>",
Expand Down
19 changes: 19 additions & 0 deletions testapp/home/migrations/0026_alter_hubspage_body.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.18 on 2025-01-31 09:07

from django.db import migrations
import wagtail.fields


class Migration(migrations.Migration):

dependencies = [
('home', '0025_remove_callout_tickbox'),
]

operations = [
migrations.AlterField(
model_name='hubspage',
name='body',
field=wagtail.fields.StreamField([('card_basic', 4), ('primary_card', 7), ('card_clickable', 7), ('card_image', 11), ('card_feature', 13), ('card_group', 16)], block_lookup={0: ('wagtail.blocks.CharBlock', (), {'required': True}), 1: ('wagtail.blocks.IntegerBlock', (), {'default': 3, 'help_text': 'The heading level affects users with screen readers. Ignore this if there is no label. Default=3, Min=2, Max=6.', 'max_value': 6, 'min_value': 2}), 2: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('', 'Default'), ('small', 'Small'), ('medium', 'Medium'), ('large', 'Large')], 'help_text': "The heading size affects the visual size, this follows the front-end library's sizing.", 'required': False}), 3: ('wagtail.blocks.RichTextBlock', (), {'required': False}), 4: ('wagtail.blocks.StructBlock', [[('heading', 0), ('heading_level', 1), ('heading_size', 2), ('body', 3)]], {}), 5: ('wagtail.blocks.PageChooserBlock', (), {'help_text': 'Interal Page Link for the card', 'label': 'Internal Page', 'required': False}), 6: ('wagtail.blocks.URLBlock', (), {'help_text': 'External Link for the card', 'label': 'URL', 'required': False}), 7: ('wagtail.blocks.StructBlock', [[('heading', 0), ('heading_level', 1), ('heading_size', 2), ('body', 3), ('internal_page', 5), ('url', 6)]], {}), 8: ('wagtail.images.blocks.ImageChooserBlock', (), {'label': 'Image', 'required': True}), 9: ('wagtail.blocks.URLBlock', (), {'help_text': 'Optional, if there is a link the entire card will be clickable.', 'label': 'URL', 'required': False}), 10: ('wagtail.blocks.PageChooserBlock', (), {'help_text': 'Optional, if there is a link the entire card will be clickable.', 'label': 'Internal Page', 'required': False}), 11: ('wagtail.blocks.StructBlock', [[('heading', 0), ('heading_level', 1), ('heading_size', 2), ('body', 3), ('content_image', 8), ('alt_text', 0), ('url', 9), ('internal_page', 10)]], {}), 12: ('wagtail.blocks.RichTextBlock', (), {'required': True}), 13: ('wagtail.blocks.StructBlock', [[('feature_heading', 0), ('heading_level', 1), ('heading_size', 2), ('body', 12)]], {}), 14: ('wagtail.blocks.ChoiceBlock', [], {'choices': [('', 'Full-width'), ('one-half', 'One-half'), ('one-third', 'One-third')], 'required': False}), 15: ('wagtail.blocks.StreamBlock', [[('card_basic', 4), ('card_clickable', 7), ('card_image', 11), ('card_feature', 13)]], {'required': True}), 16: ('wagtail.blocks.StructBlock', [[('column', 14), ('body', 15)]], {})}),
),
]
2 changes: 2 additions & 0 deletions testapp/home/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
ActionLinkBlock,
CardBasicBlock,
CardClickableBlock,
PrimaryCardBlock,
CardFeatureBlock,
CardGroupBlock,
CardImageBlock,
Expand Down Expand Up @@ -69,6 +70,7 @@ class HubsPage(Page):
body = StreamField(
[
("card_basic", CardBasicBlock()),
("primary_card", PrimaryCardBlock()),
("card_clickable", CardClickableBlock()),
("card_image", CardImageBlock()),
("card_feature", CardFeatureBlock()),
Expand Down
14 changes: 9 additions & 5 deletions testapp/testapp/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,24 @@
</head>

<body class="{% block body_class %}{% endblock %}">

<script>document.body.className = ((document.body.className) ? document.body.className + ' js-enabled' : 'js-enabled');</script>

{% wagtailuserbar %}

{% include "wagtailnhsukfrontend/skip_link.html" %}

{% header search_action="https://www.nhs.uk/search/" search_field_name="q" %}

{% block breadcrumb %}
{% breadcrumb %}
{% endblock %}

{% block outer_content%}{% endblock %}

<div class="nhsuk-width-container">
<main id="maincontent" class="nhsuk-main-wrapper">
{% block breadcrumb %}
{% breadcrumb %}
{% endblock %}


<main id="maincontent" class="nhsuk-main-wrapper nhsuk-main-wrapper--s">
{% block main %}
<div class="nhsuk-grid-row">
<div class="nhsuk-grid-column-two-thirds">
Expand Down
6 changes: 2 additions & 4 deletions tox.ini
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tox]
envlist =
py{38,39,310,311}-dj42-wt{52,60,61}
py{310,311,312}-dj50-wt{52,60,61}
py{38,39,310,311}-dj42-wt{52}
py{310,311,312}-dj50-wt{52}
lint

[flake8]
Expand All @@ -28,8 +28,6 @@ deps =
dj42: Django>=4.2,<4.3
dj50: Django>=5.0,<5.1
wt52: wagtail>=5.2,<5.3
wt60: wagtail>=6.0,<6.1
wt61: wagtail>=6.1,<6.2
commands =
python testapp/manage.py makemigrations
python testapp/manage.py migrate
Expand Down
15 changes: 15 additions & 0 deletions wagtailnhsukfrontend/blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,21 @@ def clean(self, value):
return super().clean(value)


class PrimaryCardBlock(CardClickableBlock):
"""A primary card that includes a icon along with clickable functionality."""

class Meta:
label = 'Primary card'
icon = 'doc-full'
template = 'wagtailnhsukfrontend/card.html'
help_text = 'Primary card requires an Internal page selected or a URL entered'

def get_context(self, value, parent_context=None):
context = super().get_context(value, parent_context)
context['primary_card'] = True
return context


class CardImageBlock(CardBasicBlock):

content_image = ImageChooserBlock(label='Image', required=True)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Generated by Django 4.2.18 on 2025-01-30 15:09

from django.db import migrations, models
import django.db.models.deletion
import modelcluster.fields


class Migration(migrations.Migration):

dependencies = [
('wagtailcore', '0094_alter_page_locale'),
('wagtailnhsukfrontendsettings', '0007_organisational_header'),
]

operations = [
migrations.CreateModel(
name='FooterSettingsColumns',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('enable_column_footer', models.BooleanField(default=False, help_text='Enable the column-based footer layout')),
('site', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, to='wagtailcore.site')),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='FooterColumnLinks',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('sort_order', models.IntegerField(blank=True, editable=False, null=True)),
('column', models.IntegerField(choices=[(1, 'Column 1'), (2, 'Column 2'), (3, 'Column 3'), (4, 'Column 4')], default=1)),
('link_label', models.CharField(max_length=250)),
('link_url', models.URLField(blank=True)),
('link_page', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='wagtailcore.page')),
('setting', modelcluster.fields.ParentalKey(on_delete=django.db.models.deletion.CASCADE, related_name='column_links', to='wagtailnhsukfrontendsettings.footersettingscolumns')),
],
options={
'ordering': ['sort_order'],
'abstract': False,
},
),
]
59 changes: 59 additions & 0 deletions wagtailnhsukfrontend/settings/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,62 @@ class FooterLinks(Orderable):
FieldPanel('link_url'),
FieldPanel('link_label'),
]


@register_setting
class FooterSettingsColumns(ClusterableModel, BaseSiteSetting):
COLUMN_CHOICES = [
(1, 'Column 1'),
(2, 'Column 2'),
(3, 'Column 3'),
(4, 'Column 4'),
]

enable_column_footer = models.BooleanField(
default=False,
help_text="Enable the column-based footer layout"
)

panels = [
FieldPanel('enable_column_footer'),
InlinePanel(
'column_links',
label="Footer Column Links",
help_text="Add menu items and assign them to columns",
min_num=1,
)
]


class FooterColumnLinks(Orderable):
setting = ParentalKey(
FooterSettingsColumns,
on_delete=models.CASCADE,
related_name='column_links',
)
column = models.IntegerField(
choices=FooterSettingsColumns.COLUMN_CHOICES,
default=1
)
link_label = models.CharField(max_length=250)
link_page = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
)
link_url = models.URLField(blank=True)

panels = [
FieldPanel('column'),
FieldPanel('link_label'),
FieldPanel('link_page'),
FieldPanel('link_url'),
]

@property
def link(self):
if self.link_page:
return self.link_page.url
return self.link_url
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django import template
from wagtailnhsukfrontend.settings.models import HeaderSettings, FooterSettings
from wagtailnhsukfrontend.settings.models import HeaderSettings, FooterSettings, FooterSettingsColumns

from wagtail.models import Site

Expand Down Expand Up @@ -42,13 +42,25 @@ def footer(context):
request = context['request']
site = Site.find_for_request(request)
footer = FooterSettings.for_site(site)
footer_columns = FooterSettingsColumns.for_site(site)

# Organize column links by column number
column_links = {1: [], 2: [], 3: [], 4: []}
if footer_columns.enable_column_footer:
for link in footer_columns.column_links.all():
column_links[link.column].append({
'label': link.link_label,
'url': link.link_page.url if link.link_page else link.link_url
})

return {
'enable_column_footer': footer_columns.enable_column_footer,
'primary_links': [
{
'label': link.link_label,
'url': link.link_url
}
for link in footer.footer_links.all()
],
'column_links': column_links,
}

Large diffs are not rendered by default.

Loading

0 comments on commit 24b2a99

Please sign in to comment.