Skip to content

Commit 560df22

Browse files
dchukhinlucianoratamerokkellydesigndependabot[bot]maureenlholland
authored
WT-334 - Build CMS Components For Advertising Pages - Part 3 (#16815)
* Stylelint * WIP: add new solutions page * Stylelint * Move base styles from the home-solutions to the advertising and main-dark-section css files * Add base advertising template for next pages * Remove unused code * Prevent specific issues with white lines below the sticky subnav * Add base why mozilla view and template * Update ads notification bar to make arrow clickable * Add why mozilla, contact, notification, and highlight text to the why mozilla page * Wrap why mozilla content on #mozads-main * Add sample gallery section * Redirect formats page to the WIP solutions page * solutions template fix * First step toward refactoring typography to align advertising, protocol, and m24 sizing. * finished refactoring font sizes * Move shared css from principles page to a generic two-column css file * Add new contact sales page * Change url for the contact page * Add link to the contact sales page * Wrap contact and principles pages with #mozads-main * Add required field indicators to the contact sales form fields * Add base static content to the ads solutions page * Add placeholder modal implementation to ads solutions page * Add accordion component using the details and summary html tags * add an AdvertisingIndexPage to the project, with template identical to mozorg/advertising/landing.html * make sure that AdvertisingIndexPage is included in db export script * turn advertising index page hero into a block * turn advertising section heading into a block * Migrate ads solutions to an es6 file * Add skeleton for carousels in modals on ads solutions page * turn advertising statistics sections into a block * turn advertising feature list section into a block * turn advertising contact banner into a snippet * turn advertising notification into a page field * add an AdvertisingPrinciplesPage to the project * make AdvertisingPrinciplesPage fields editable in Wagtail * make code DRYer by using include template in in feature_list_item_block.html * implement sub-navigation for advertising pages * Bump the frontend group with 3 updates (#16769) Bumps the frontend group with 3 updates: [@sentry/browser](https://github.com/getsentry/sentry-javascript), [caniuse-lite](https://github.com/browserslist/caniuse-lite) and [webpack](https://github.com/webpack/webpack). Updates `@sentry/browser` from 10.12.0 to 10.17.0 - [Release notes](https://github.com/getsentry/sentry-javascript/releases) - [Changelog](https://github.com/getsentry/sentry-javascript/blob/develop/CHANGELOG.md) - [Commits](getsentry/sentry-javascript@10.12.0...10.17.0) Updates `caniuse-lite` from 1.0.30001743 to 1.0.30001746 - [Commits](browserslist/caniuse-lite@1.0.30001743...1.0.30001746) Updates `webpack` from 5.101.3 to 5.102.0 - [Release notes](https://github.com/webpack/webpack/releases) - [Commits](webpack/webpack@v5.101.3...v5.102.0) --- updated-dependencies: - dependency-name: "@sentry/browser" dependency-version: 10.17.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend - dependency-name: caniuse-lite dependency-version: 1.0.30001746 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: frontend - dependency-name: webpack dependency-version: 5.102.0 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: frontend ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> * Add mozfest block to homepage (fixes #16792) (#16802) * Add mozfest block to homepage * Add responsive images * Add alt for image * Update l10n/en/mozorg/home-m24.ftl --------- Co-authored-by: Stephanie Hobson <stephaniehobson@users.noreply.github.com> * l10n request: fix dashes (#16811) * add an AdvertisingPrinciplesPage to the project * make AdvertisingPrinciplesPage fields editable in Wagtail * make code DRYer by using include template in in feature_list_item_block.html * implement sub-navigation for AdvertisingPrinciplesPage * reorder migrations after merge * allow subnavigation links to look like buttons by selecting a checkbox * add AdvertisingPrinciplesPage to the pages which are editable in Wagtail * add AdvertisingPrinciplesPage to the pages in export db script * add AdvertisingPrinciplesPage to the pages which are editable in Wagtail * add AdvertisingPrinciplesPage to the pages in export db script * avoid confusion by making sure that AdvertisingPrinciplesPage can only be a child of AdvertisingIndexPage * avoid confusion by making sure that AdvertisingPrinciplesPage can only be a child of AdvertisingIndexPage * be more specific about rendered image dimensions * be more specific about rendered image dimensions * be more specific about rendered image dimensions * add a unit test for the AdvertisingIndexPage * design revisions * modal FE finished * solutions PDF, contact link updated * minor FE edits to account for predictable CMS behavior * Revert "Merge branch 'wt-303-ads-solutions-page' into WT-334-cms-components-for-advertising-subpage" This reverts commit ed6183b, reversing changes made to 0759a63. * copy and design revisions * update for design revisions * design revisions * fixing linting * prettier JS updates * carousel caption JS updates * new Impact page design * add ability to add top divider to section heading block * allow image alt text to be set on page, but also have default if page has not alt text * typography revisions * use image description for alt-text simplify user editing experience by limiting the number of editable fields; since the images will likely be used only 1 time, the alt text can be defined at the image level * use wagtail-link-block to create consistent link choosing experience * test * make advertising subpage more generically-named * for clarity, rename list_items field to be second_column * make sure that template variables have a value when using include * do not require elements in two column subpage's second column to be a list * add rich text block to two column subpage's second column * add a unit test for TwoColumnSubpage * update test for AdvertisingIndexPage based on changes to fields * fix typo and add missing '}' * fix lint error * remove extra text from section_anchor help_text * updated type scale * linting fixes * linting fixes * use wagtail-link-block in sub navigation for more consistent link choosing experience * add a unit test for advertising pages sub-navigation * fix formatting error * make page more reusable by putting content into a two-column block in a streamfield * allow FigureWithStatisticBlock to have a link * fix reference to new_window property in link * update CSS classes used to match work in WT-303 * allow FigureWithStatisticBlock to have a link * update blocks and CSS classes to match work in WT-303 * new images, another round of design QA * advertising index page can have multiple links for a notification * new copy added * update tests based on recent changes to TwoColumnSubpage blocks * update migration based on merge * make sure TwoColumnSubpage uses correct file for sub-navigation * fix frontend build issues * MDN ads images added, captions revised * replace hard-coded link in sub-navigation with page URL * merge more changes from WT-303 * remove unused SCSS file * remove unused HTML file * make more updates from WT-303 changes * make more updates from WT-303 changes * add ContentSubpage to the project * update SectionHeaderBlock to support ContentSubpage designs * add FigureWithStatisticBlock to ContentSubpage * Mozilla Ads and Web Docs find/replace * CSS prefix revision to remove "mozads" in favor of "mza" * replacing mozads with mza in template files * Solutions refactor in prep for CMS work * add StatisticCalloutBlock to ContentSubpage * add FeatureListWithModalsBlock to ContentSubpage * allow StatisticBlocks to have a label above the value * add RowTextAndLinkBlock to ContentSubpage * add contact banner and notifications to ContentSubpage * make HTML file more readable * replace mozads with mza in template files * replace mozads with mza in template files * make sure to use specific page, so that navigation correctly appears * replace mozads with mza in template files * rename block field to be more descriptive of its function * do not require a heading text in SectionHeaderBlocks * require SectionHeaderBlocks to have a superheading, heading, or subheading * add FeatureListBlock to ContentSubpage, so it can be used on the 'Impact' page * allow feature list item heading to have line breaks since line breaks are needed on the 'Impact' page * allow feature list item supporting text to be rich text since rich text is needed on the 'Impact' page * use a more-fitting icon for RowTextAndLinkBlock * undo accidental changes to github actions code from merge revert commit * photo update * undo accidental changes from merge revert * undo more accidental changes from merge revert * make sure to refer to correct CTA attribute in template * add rel noopener attribute to links that open in a new tab * add rel noopener attribute to links that open in a new tab * "nearly" removed. Apostrophes fixed. Principles added to nav. * precommit installed and run on all relevant files * homepage CSS revisions * impact and solutions page revisions * header class revisions * prefer Wagtail Pages for each of advertising pages * make CMS Page editing more closely match springfield In order to make the experience of editing pages more-closely match Springfield, we nest blocks inside of a SectionBlock. * move divider and background color settings from blocks to SectionBlock * update tests based on recent page structure changes * update navigation anchor validation logic based on recent changes to page structure * ads work refactor for better markup/sections * fixing line lengths * change notifications block to be a snippet * template revisions * Update bedrock/mozorg/blocks/advertising.py Co-authored-by: Steve Jalim <stevejalim@mozilla.com> * removing advertising/why-mozilla and advertising/contact * add migration for label text change * remove twitter as a social icon choice * reorder migrations after merge * for better usage clarity, rename TwoColumnSubpage to AdvertisingTwoColumnSubpage * reorder migrations after merge * update reference to previously renamed model * remove twitter as field from NotificationSnippet * remove unused notification-related blocks * remove unused field mapping * Merge branch 'main' into WT-334-cms-components-for-advertising-sub-navigation * Merge branch 'main' into WT-334-cms-components-for-advertising-sub-navigation --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: Luciano Ratamero <luciano@ratamero.com> Co-authored-by: Kasey Kelly <kasey@servee.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: maureenlholland <19650432+maureenlholland@users.noreply.github.com> Co-authored-by: Stephanie Hobson <stephaniehobson@users.noreply.github.com> Co-authored-by: Steve Jalim <stevejalim@mozilla.com>
1 parent fa972e0 commit 560df22

46 files changed

Lines changed: 3325 additions & 388 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

bedrock/mozorg/blocks/advertising.py

Lines changed: 173 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
# License, v. 2.0. If a copy of the MPL was not distributed with this
33
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
44

5+
from django.core.exceptions import ValidationError
6+
57
from wagtail import blocks
68
from wagtail.images.blocks import ImageChooserBlock
79
from wagtail_link_block.blocks import LinkBlock
@@ -17,19 +19,14 @@
1719
]
1820

1921

20-
SOCIAL_MEDIA_ICON_CHOICES = [
21-
("linkedin", "LinkedIn"),
22-
("tiktok", "TikTok"),
23-
("spotify", "Spotify"),
24-
("bluesky", "BlueSky"),
25-
("instagram", "Instagram"),
26-
("youtube", "YouTube"),
27-
]
28-
29-
3022
class AdvertisingHeroBlock(blocks.StructBlock):
3123
"""Advertising page hero block."""
3224

25+
anchor_id = blocks.CharBlock(
26+
required=False,
27+
max_length=100,
28+
help_text="Optional: Add an ID to make this section linkable from navigation (e.g., 'hero', 'overview')",
29+
)
3330
heading_text = blocks.CharBlock(char_max_length=255)
3431
primary_cta_text = blocks.CharBlock(char_max_length=255)
3532
primary_cta_link = LinkBlock(label="Primary CTA Link")
@@ -47,17 +44,31 @@ class Meta:
4744
class SectionHeaderBlock(blocks.StructBlock):
4845
"""Section header block."""
4946

50-
has_top_divider = blocks.BooleanBlock(
51-
default=False,
47+
anchor_id = blocks.CharBlock(
5248
required=False,
53-
label="Should the section have a divider line on top?",
54-
inline_form=True,
49+
max_length=100,
50+
help_text="Optional: Add an ID to make this section linkable from navigation (e.g., 'solutions', 'why-mozilla')",
5551
)
5652
superheading_text = blocks.CharBlock(char_max_length=255, required=False)
57-
heading_text = blocks.CharBlock(char_max_length=255)
53+
heading_text = blocks.CharBlock(char_max_length=255, required=False)
5854
subheading_text = blocks.CharBlock(char_max_length=255, required=False)
5955
image = ImageChooserBlock(required=False)
6056

57+
def clean(self, value):
58+
errors = {}
59+
60+
superheading = value.get("superheading_text", "").strip()
61+
heading = value.get("heading_text", "").strip()
62+
subheading = value.get("subheading_text", "").strip()
63+
64+
if not superheading and not heading and not subheading:
65+
errors["heading_text"] = ValidationError("You must provide either superheading, or heading, or subheading.")
66+
67+
if errors:
68+
raise blocks.StructBlockValidationError(errors)
69+
70+
return super().clean(value)
71+
6172
class Meta:
6273
icon = "title"
6374
label = "Section Heading"
@@ -69,8 +80,13 @@ class Meta:
6980
class FigureWithStatisticBlock(blocks.StructBlock):
7081
"""Figure with a statistic block."""
7182

83+
anchor_id = blocks.CharBlock(
84+
required=False,
85+
max_length=100,
86+
help_text="Optional: Add an ID to make this section linkable from navigation",
87+
)
7288
image = ImageChooserBlock(required=False)
73-
image_caption = blocks.RichTextBlock(char_max_length=255)
89+
image_caption = blocks.RichTextBlock(char_max_length=255, required=False)
7490
statistic_value = blocks.CharBlock(char_max_length=255)
7591
statistic_label = blocks.CharBlock(char_max_length=255)
7692
align_image_on_right = blocks.BooleanBlock(
@@ -79,8 +95,6 @@ class FigureWithStatisticBlock(blocks.StructBlock):
7995
label="Position the image to the right of the statistic",
8096
inline_form=True,
8197
)
82-
cta_text = blocks.CharBlock(char_max_length=255, required=False)
83-
cta_link = LinkBlock(label="Link", required=False)
8498

8599
class Meta:
86100
icon = "decimal"
@@ -93,8 +107,8 @@ class Meta:
93107
class FeatureListItemBlock(blocks.StructBlock):
94108
"""Feature list item block."""
95109

96-
heading_text = blocks.CharBlock(char_max_length=255)
97-
supporting_text = blocks.TextBlock()
110+
heading_text = blocks.TextBlock()
111+
supporting_text = blocks.RichTextBlock(features=["bold", "italic", "link"])
98112

99113
class Meta:
100114
label = "Feature Item"
@@ -106,6 +120,11 @@ class Meta:
106120
class FeatureListBlock(blocks.StructBlock):
107121
"""Feature list block."""
108122

123+
anchor_id = blocks.CharBlock(
124+
required=False,
125+
max_length=100,
126+
help_text="Optional: Add an ID to make this section linkable from navigation",
127+
)
109128
feature_list_items = blocks.ListBlock(FeatureListItemBlock(), min_num=1)
110129

111130
class Meta:
@@ -144,6 +163,103 @@ class Meta:
144163
form_classname = "compact-form struct-block"
145164

146165

166+
class StatisticBlock(blocks.StructBlock):
167+
"""Statistic block."""
168+
169+
statistic_superlabel = blocks.CharBlock(char_max_length=255, required=False)
170+
statistic_value = blocks.CharBlock(char_max_length=255)
171+
statistic_label = blocks.CharBlock(char_max_length=255)
172+
173+
class Meta:
174+
icon = "decimal"
175+
label = "Statistic Block"
176+
label_format = "{statistic_value} {statistic_label}"
177+
template = "mozorg/cms/advertising/blocks/statistic_block.html"
178+
form_classname = "compact-form struct-block"
179+
180+
181+
class StatisticCalloutBlock(blocks.StructBlock):
182+
"""Block for statistics with a callout."""
183+
184+
anchor_id = blocks.CharBlock(
185+
required=False,
186+
max_length=100,
187+
help_text="Optional: Add an ID to make this section linkable from navigation",
188+
)
189+
heading_text = blocks.CharBlock(char_max_length=255)
190+
statistics = blocks.ListBlock(StatisticBlock(), min_num=1)
191+
192+
class Meta:
193+
icon = "decimal"
194+
label = "Statistic Callout Block"
195+
label_format = "{heading_text}"
196+
template = "mozorg/cms/advertising/blocks/statistic_callout_block.html"
197+
form_classname = "compact-form struct-block"
198+
199+
200+
class FigureBlock(blocks.StructBlock):
201+
"""Figure block."""
202+
203+
image = ImageChooserBlock(required=False)
204+
image_caption_heading = blocks.TextBlock(char_max_length=255, required=False)
205+
image_caption_text = blocks.TextBlock(char_max_length=255, required=False)
206+
207+
class Meta:
208+
label = "Feature Item"
209+
label_format = "{image_caption}"
210+
form_classname = "compact-form struct-block"
211+
212+
213+
class FeatureItemWithModalBlock(blocks.StructBlock):
214+
"""Block for a feature item with a modal."""
215+
216+
heading_text = blocks.CharBlock(char_max_length=255)
217+
figures = blocks.ListBlock(FigureBlock(), min_num=1)
218+
cta_text = blocks.CharBlock(char_max_length=255, required=False)
219+
cta_link = LinkBlock(label="CTA Link", required=False)
220+
221+
class Meta:
222+
label = "Feature Item"
223+
label_format = "{heading_text}"
224+
template = "mozorg/cms/advertising/blocks/feature_item_with_modal_block.html"
225+
form_classname = "compact-form struct-block"
226+
227+
228+
class FeatureListWithModalsBlock(blocks.StructBlock):
229+
"""A block for a list of features, each with a modal."""
230+
231+
anchor_id = blocks.CharBlock(
232+
required=False,
233+
max_length=100,
234+
help_text="Optional: Add an ID to make this section linkable from navigation",
235+
)
236+
heading_text = blocks.CharBlock(char_max_length=255)
237+
supporting_text = blocks.TextBlock()
238+
feature_list_items = blocks.ListBlock(FeatureItemWithModalBlock(), min_num=1)
239+
240+
class Meta:
241+
icon = "list-ul"
242+
label = "Feature List With Modals Block"
243+
label_format = "{heading_text}"
244+
template = "mozorg/cms/advertising/blocks/feature_list_with_modals_block.html"
245+
form_classname = "compact-form struct-block"
246+
247+
248+
class RowTextAndLinkBlock(blocks.StructBlock):
249+
"""Block for text and a link."""
250+
251+
text = blocks.CharBlock(char_max_length=255, required=False)
252+
link_text = blocks.CharBlock(char_max_length=255)
253+
link = LinkBlock(label="Link")
254+
255+
class Meta:
256+
icon = "doc-full"
257+
label = "Text and Link Block"
258+
label_format = "{text}"
259+
template = "mozorg/cms/advertising/blocks/row_text_and_link_block.html"
260+
form_classname = "compact-form struct-block"
261+
262+
147263
class TwoColumnDetailBlock(blocks.StructBlock):
148264
"""Feature list item block."""
149265

@@ -163,39 +279,49 @@ class Meta:
163279
form_classname = "compact-form struct-block"
164280

165281

166-
class LinkWithIcon(blocks.StructBlock):
167-
"""Link with an icon."""
168-
169-
icon = blocks.ChoiceBlock(required=False, choices=SOCIAL_MEDIA_ICON_CHOICES, inline_form=True)
170-
link = LinkBlock()
282+
class SectionSettings(blocks.StructBlock):
283+
anchor_id = blocks.CharBlock(
284+
required=False,
285+
max_length=100,
286+
help_text="Optional: Add an ID to make this section linkable from navigation (e.g., 'solutions', 'why-mozilla')",
287+
)
288+
has_top_divider = blocks.BooleanBlock(
289+
default=False,
290+
required=False,
291+
label="Should the section have a divider line on top?",
292+
inline_form=True,
293+
)
294+
display_on_dark_background = blocks.BooleanBlock(
295+
default=False,
296+
required=False,
297+
label="Should the section have a dark background?",
298+
inline_form=True,
299+
)
171300

172301
class Meta:
173-
icon = "link"
174-
label = "Link With Icon"
175-
label_format = "Link With Icon"
176-
template = "mozorg/cms/advertising/blocks/link_with_icon_block.html"
302+
icon = "cog"
303+
collapsed = True
304+
label = "Settings"
305+
label_format = "ID: {anchor_id} - Divider: {has_top_divider} - Dark background: {display_on_dark_background}"
177306
form_classname = "compact-form struct-block"
178307

179308

180-
class NotificationBlock(blocks.StructBlock):
181-
notification_text = blocks.RichTextBlock(
182-
char_max_length=255,
183-
features=[
184-
"bold",
185-
"italic",
186-
"superscript",
187-
"subscript",
188-
"strikethrough",
189-
"code",
190-
"link",
191-
],
192-
)
193-
links = blocks.StreamBlock(
309+
class SectionBlock(blocks.StructBlock):
310+
settings = SectionSettings()
311+
312+
header = SectionHeaderBlock()
313+
content = blocks.StreamBlock(
194314
[
195-
("link_with_icon", LinkWithIcon()),
196-
],
197-
required=False,
315+
("section_header_block", SectionHeaderBlock()),
316+
("figure_with_statistic_block", FigureWithStatisticBlock()),
317+
("statistic_callout_block", StatisticCalloutBlock()),
318+
("features_with_modals", FeatureListWithModalsBlock()),
319+
("feature_list_block", FeatureListBlock()),
320+
]
198321
)
322+
call_to_action = blocks.ListBlock(RowTextAndLinkBlock(), min_num=0, max_num=1, default=[], label="Call to Action")
199323

200324
class Meta:
201-
template = "mozorg/cms/advertising/blocks/notification_block.html"
325+
template = "mozorg/cms/advertising/blocks/section.html"
326+
label = "Section"
327+
label_format = "{header}"
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# This Source Code Form is subject to the terms of the Mozilla Public
2+
# License, v. 2.0. If a copy of the MPL was not distributed with this
3+
# file, You can obtain one at https://mozilla.org/MPL/2.0/.
4+
5+
from django.core.exceptions import ValidationError
6+
7+
from wagtail import blocks
8+
from wagtail.blocks import StructValue
9+
from wagtail_link_block.blocks import LinkBlock
10+
11+
12+
class NavigationLinkValue(StructValue):
13+
"""Custom StructValue for NavigationLinkBlock with URL resolution."""
14+
15+
def get_resolved_url(self, anchor_page_url=""):
16+
"""Return the resolved URL for this navigation link."""
17+
link = self.get("link")
18+
if link.get("link_to") == "anchor":
19+
return f"{anchor_page_url}{link.get_url()}"
20+
elif link:
21+
return link.get_url()
22+
return None
23+
24+
25+
class NavigationLinkBlock(blocks.StructBlock):
26+
"""A navigation link for sub-navigation menu."""
27+
28+
link_text = blocks.CharBlock(
29+
max_length=50,
30+
help_text="Text to display for this navigation link",
31+
)
32+
33+
link = LinkBlock()
34+
35+
has_button_appearance = blocks.BooleanBlock(
36+
required=False,
37+
default=False,
38+
help_text="This link should look like a button",
39+
)
40+
41+
def clean(self, value):
42+
errors = {}
43+
link = value.get("link", {})
44+
45+
# Validate that the appropriate field is filled based on link_type
46+
if link.get("link_to") == "anchor":
47+
if not link.get("anchor"):
48+
errors["link"] = ValidationError("Section anchor is required when linking to a section")
49+
50+
if errors:
51+
raise blocks.StructBlockValidationError(errors)
52+
53+
return super().clean(value)
54+
55+
class Meta:
56+
icon = "link"
57+
label = "Navigation Link"
58+
value_class = NavigationLinkValue

0 commit comments

Comments
 (0)