-
Notifications
You must be signed in to change notification settings - Fork 1
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
TWE-7 - BE - Division page #324
base: integration/2024-evolution
Are you sure you want to change the base?
Changes from all commits
e4abc32
84a7048
7144e99
643ff22
a38b9d1
aa3dfd6
6663d23
bc24e99
685f6cc
7f5d684
aa12999
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,6 +10,7 @@ | |
from django.utils import timezone | ||
from django.utils.functional import cached_property | ||
from django.utils.safestring import mark_safe | ||
from django.utils.translation import gettext as _ | ||
|
||
from tbx.images.models import CustomImage | ||
from wagtail import blocks | ||
|
@@ -262,6 +263,85 @@ class Meta: | |
template = "patterns/molecules/streamfield/blocks/contact_call_to_action.html" | ||
|
||
|
||
class DynamicHeroBlock(blocks.StructBlock): | ||
""" | ||
This block displays text that will be cycled through. | ||
""" | ||
|
||
static_text = blocks.CharBlock(required=False) | ||
dynamic_text = blocks.ListBlock( | ||
blocks.CharBlock(), | ||
help_text=_( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. question: are we ever going to translate the site? Since the other blocks don't use gettext(_lazy) we should keep it to plain strings for consistency |
||
"The hero will cycle through these texts on larger screen sizes " | ||
"and only show the first text on smaller screen sizes." | ||
), | ||
required=False, | ||
) | ||
|
||
class Meta: | ||
icon = "title" | ||
template = "patterns/molecules/streamfield/blocks/dynamic_hero_block.html" | ||
|
||
|
||
class FourPhotoCollageBlock(blocks.StructBlock): | ||
"""Accepts 4 photos shown as a collage + text below. Used on the division page.""" | ||
|
||
images = blocks.ListBlock( | ||
ImageWithAltTextBlock(label="Photo"), | ||
min_num=4, | ||
max_num=4, | ||
label="Photos", | ||
help_text=_("Exactly four required."), | ||
default=[{"image": None, "alt_text": ""}] * 4, | ||
) | ||
caption = blocks.RichTextBlock( | ||
features=settings.PARAGRAPH_RICH_TEXT_FEATURES, required=False | ||
) | ||
small_caption = blocks.RichTextBlock( | ||
features=settings.PARAGRAPH_RICH_TEXT_FEATURES, required=False | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. note to self: see example with caption / small caption. question: would these not be better as |
||
) | ||
|
||
class Meta: | ||
group = "Custom" | ||
icon = "image" | ||
template = "patterns/molecules/streamfield/blocks/four_photo_collage_block.html" | ||
|
||
|
||
class IntroductionWithImagesBlock(blocks.StructBlock): | ||
"""Used on the division page.""" | ||
|
||
introduction = blocks.RichTextBlock(features=settings.PARAGRAPH_RICH_TEXT_FEATURES) | ||
description = blocks.RichTextBlock( | ||
blank=True, features=settings.NO_HEADING_RICH_TEXT_FEATURES | ||
) | ||
images = blocks.ListBlock( | ||
ImageWithAltTextBlock(label="Photo"), | ||
min_num=2, | ||
max_num=2, | ||
label="Photos", | ||
help_text=_("Exactly two required."), | ||
default=[{"image": None, "alt_text": ""}] * 2, | ||
) | ||
|
||
class Meta: | ||
group = "Custom" | ||
icon = "pilcrow" | ||
template = ( | ||
"patterns/molecules/streamfield/blocks/introduction_with_images_block.html" | ||
) | ||
|
||
|
||
class PartnersBlock(blocks.StructBlock): | ||
title = blocks.CharBlock(max_length=255, required=False) | ||
partner_logos = blocks.ListBlock(CustomImageChooserBlock(), label="Logos") | ||
|
||
class Meta: | ||
icon = "openquote" | ||
label = "Partner logos" | ||
template = "patterns/molecules/streamfield/blocks/partners_block.html" | ||
group = "Custom" | ||
|
||
|
||
class ShowcaseBlock(blocks.StructBlock): | ||
""" | ||
This block is a standard ShowcaseBlock, available on the home page and | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,13 +1,30 @@ | ||
import factory | ||
import wagtail_factories | ||
from tbx.core.blocks import StoryBlock | ||
from faker import Faker | ||
from tbx.core.blocks import DynamicHeroBlock, StoryBlock | ||
from tbx.core.models import HomePage, StandardPage | ||
from wagtail.blocks import RichTextBlock | ||
from wagtail import blocks | ||
|
||
fake = Faker() | ||
|
||
|
||
class DynamicHeroBlockFactory(wagtail_factories.StructBlockFactory): | ||
class Meta: | ||
model = DynamicHeroBlock | ||
|
||
static_text = fake.sentence() | ||
|
||
@factory.post_generation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. praise: nice use of factory boy features |
||
def dynamic_text(obj, create, extracted, **kwargs): | ||
values = extracted or fake.sentences(nb=5) | ||
obj["dynamic_text"] = blocks.list_block.ListValue( | ||
blocks.ListBlock(blocks.CharBlock()), values | ||
) | ||
|
||
|
||
class RichTextBlockFactory(wagtail_factories.blocks.BlockFactory): | ||
class Meta: | ||
model = RichTextBlock | ||
model = blocks.RichTextBlock | ||
|
||
|
||
class StoryBlockFactory(wagtail_factories.StreamBlockFactory): | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from tbx.core.blocks import ( | ||
FourPhotoCollageBlock, | ||
IntroductionWithImagesBlock, | ||
PartnersBlock, | ||
StoryBlock, | ||
) | ||
|
||
|
||
class DivisionStoryBlock(StoryBlock): | ||
four_photo_collage = FourPhotoCollageBlock() | ||
introduction_with_images = IntroductionWithImagesBlock() | ||
partners_block = PartnersBlock() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import factory | ||
import wagtail_factories | ||
from tbx.core.blocks import DynamicHeroBlock | ||
from tbx.core.factories import DynamicHeroBlockFactory, StoryBlockFactory | ||
from wagtail import blocks | ||
|
||
from .models import DivisionPage | ||
|
||
|
||
class DynamicHeroStreamBlock(blocks.StreamBlock): | ||
hero = DynamicHeroBlock() | ||
|
||
|
||
class DynamicHeroStreamBlockFactory(wagtail_factories.StreamBlockFactory): | ||
class Meta: | ||
model = DynamicHeroStreamBlock | ||
|
||
hero = factory.SubFactory(DynamicHeroBlockFactory) | ||
|
||
|
||
class DivisionPageFactory(wagtail_factories.PageFactory): | ||
class Meta: | ||
model = DivisionPage | ||
|
||
title = "Charity" | ||
label = "Charity" | ||
|
||
@factory.post_generation | ||
def hero(obj, create, extracted, **kwargs): | ||
blocks = kwargs or {"0": "hero"} | ||
obj.hero = DynamicHeroStreamBlockFactory(**blocks) | ||
|
||
@factory.post_generation | ||
def body(obj, create, extracted, **kwargs): | ||
blocks = kwargs or {"0": "paragraph"} | ||
obj.body = StoryBlockFactory(**blocks) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
# Generated by Django 4.2.16 on 2024-12-18 08:18 | ||
|
||
from django.db import migrations, models | ||
import django.db.models.deletion | ||
import tbx.core.utils.fields | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
initial = True | ||
|
||
dependencies = [ | ||
("wagtailcore", "0094_alter_page_locale"), | ||
("people", "0011_update_theme_colour_choices"), | ||
("images", "0001_initial"), | ||
] | ||
|
||
operations = [ | ||
migrations.CreateModel( | ||
name="DivisionPage", | ||
fields=[ | ||
( | ||
"page_ptr", | ||
models.OneToOneField( | ||
auto_created=True, | ||
on_delete=django.db.models.deletion.CASCADE, | ||
parent_link=True, | ||
primary_key=True, | ||
serialize=False, | ||
to="wagtailcore.page", | ||
), | ||
), | ||
( | ||
"navigation_text", | ||
models.CharField( | ||
blank=True, | ||
help_text="\n Text entered here will appear instead of the page title in the navigation menu.\n For top-level menu items do this in the navigaiton settings instead.\n ", | ||
max_length=255, | ||
), | ||
), | ||
("social_text", models.CharField(blank=True, max_length=255)), | ||
( | ||
"theme", | ||
models.CharField( | ||
blank=True, | ||
choices=[ | ||
("", "None"), | ||
("theme-coral", "Coral"), | ||
("theme-nebuline", "Nebuline"), | ||
("theme-lagoon", "Lagoon"), | ||
("theme-green", "Green"), | ||
], | ||
max_length=25, | ||
), | ||
), | ||
("label", models.CharField(blank=True, max_length=50)), | ||
("hero", tbx.core.utils.fields.StreamField(block_lookup={})), | ||
( | ||
"body", | ||
tbx.core.utils.fields.StreamField(blank=True, block_lookup={}), | ||
), | ||
( | ||
"contact", | ||
models.ForeignKey( | ||
blank=True, | ||
help_text="The contact will be applied to this page's footer and all of its descendants.\nIf no contact is selected, it will be derived from this page's ancestors, eventually falling back to the default contact.", | ||
null=True, | ||
on_delete=django.db.models.deletion.SET_NULL, | ||
related_name="+", | ||
to="people.contact", | ||
), | ||
), | ||
( | ||
"social_image", | ||
models.ForeignKey( | ||
blank=True, | ||
null=True, | ||
on_delete=django.db.models.deletion.SET_NULL, | ||
related_name="+", | ||
to="images.customimage", | ||
), | ||
), | ||
], | ||
options={ | ||
"abstract": False, | ||
}, | ||
bases=("wagtailcore.page", models.Model), | ||
), | ||
] |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
from django.db import models | ||
|
||
from tbx.core.blocks import DynamicHeroBlock | ||
from tbx.core.utils.fields import StreamField | ||
from tbx.core.utils.models import ( | ||
ColourThemeMixin, | ||
NavigationFields, | ||
SocialFields, | ||
) | ||
from tbx.people.models import ContactMixin | ||
from wagtail.admin.panels import FieldPanel, MultiFieldPanel | ||
from wagtail.models import Page | ||
|
||
from .blocks import DivisionStoryBlock | ||
|
||
|
||
class DivisionPage( | ||
ColourThemeMixin, ContactMixin, SocialFields, NavigationFields, Page | ||
): | ||
template = "patterns/pages/divisions/division_page.html" | ||
|
||
label = models.CharField(blank=True, max_length=50) | ||
|
||
hero = StreamField([("hero", DynamicHeroBlock())], max_num=1, min_num=1) | ||
body = StreamField(DivisionStoryBlock(), blank=True) | ||
|
||
content_panels = Page.content_panels + [ | ||
FieldPanel( | ||
"label", | ||
heading="Division label", | ||
help_text=( | ||
"Label displayed beside the logo for this page and any other pages" | ||
" under this division. (e.g. Charity)" | ||
), | ||
), | ||
FieldPanel("hero"), | ||
FieldPanel("body"), | ||
] | ||
|
||
promote_panels = ( | ||
[ | ||
MultiFieldPanel(Page.promote_panels, "Common page configuration"), | ||
] | ||
+ NavigationFields.promote_panels | ||
+ ColourThemeMixin.promote_panels | ||
+ ContactMixin.promote_panels | ||
+ [ | ||
MultiFieldPanel(SocialFields.promote_panels, "Social fields"), | ||
] | ||
) |
Original file line number | Diff line number | Diff line change | ||
---|---|---|---|---|
@@ -0,0 +1,2 @@ | ||||
|
||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||
{% include "patterns/molecules/streamfield/blocks/introduction_with_images_block.html" %} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
tags: | ||
srcset_image: | ||
item.image format-webp loading="lazy" fill-{100x100} alt=item.image.alt_text: | ||
raw: | | ||
<img alt="" width="100" height="100" loading="lazy" sizes="100px" src="https://picsum.photos/100/100.webp" srcset="https://picsum.photos/100/100.webp 100w"> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added as explicit dependencies since we use them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
factory-boy is a wagtail-factories dependency, and faker is a factory-boy dependency
Normally we don't make them explicit like this, but happy with this