This commit is contained in:
Jhonatan Lopes 2024-04-18 16:17:54 -03:00
Родитель 881098aa74
Коммит 09068d23c2
38 изменённых файлов: 618 добавлений и 55 удалений

Просмотреть файл

@ -8294,7 +8294,7 @@ a.text-gray-dark:focus, a.text-gray-dark:hover {
padding-bottom: calc( + 22px - 6px / 2); } } padding-bottom: calc( + 22px - 6px / 2); } }
@media (min-width: 768px) { @media (min-width: 768px) {
#primary-nav-container .nav-links a { #primary-nav-container .nav-links a {
padding-bottom: calc( 16px + 9px - 6px / 2); } } padding-bottom: 1rem; } }
#primary-nav-container .nav-links a:focus, #primary-nav-container .nav-links a:active, #primary-nav-container .nav-links a:hover { #primary-nav-container .nav-links a:focus, #primary-nav-container .nav-links a:active, #primary-nav-container .nav-links a:hover {
text-decoration: none; text-decoration: none;
border-bottom-color: #cccccc; } border-bottom-color: #cccccc; }

Просмотреть файл

@ -70,7 +70,7 @@ class NavFeaturedItem(BaseLinkBlock):
value_class = NavItemValue value_class = NavItemValue
label = "Featured Navigation Link" label = "Featured Navigation Link"
icon = "link" icon = "link"
template = "nav/blocks/featured_item_block.html" template = "fragments/blocks/nav/featured-item.html"
register(BaseLinkBlockAdapter(), NavFeaturedItem) register(BaseLinkBlockAdapter(), NavFeaturedItem)
@ -81,7 +81,7 @@ class NavButton(BaseLinkBlock):
value_class = NavItemValue value_class = NavItemValue
label = "Navigation Button" label = "Navigation Button"
icon = "link" icon = "link"
template = "nav/blocks/nav_button_block.html" template = "fragments/blocks/nav/button.html"
register(BaseLinkBlockAdapter(), NavButton) register(BaseLinkBlockAdapter(), NavButton)
@ -93,7 +93,7 @@ class NavColumnValue(blocks.StructValue):
return bool(self.get("button")) return bool(self.get("button"))
@property @property
def button(self) -> NavButton | None: def button_value(self) -> NavButton | None:
button = self.get("button") button = self.get("button")
if button: if button:
return button[0] return button[0]
@ -117,7 +117,7 @@ class NavColumn(blocks.StructBlock):
class Meta: class Meta:
label = "Navigation Column" label = "Navigation Column"
icon = "list-ul" icon = "list-ul"
template = "nav/blocks/nav_column_block.html" template = "fragments/blocks/nav/column.html"
value_class = NavColumnValue value_class = NavColumnValue
@ -129,7 +129,7 @@ class NavFeaturedColumn(blocks.StructBlock):
class Meta: class Meta:
label = "Featured Navigation Column" label = "Featured Navigation Column"
icon = "list-ul" icon = "list-ul"
template = "nav/blocks/featured_column_block.html" template = "fragments/blocks/nav/featured-column.html"
value_class = NavColumnValue value_class = NavColumnValue
@ -140,7 +140,7 @@ class NavOverview(blocks.StructBlock):
class Meta: class Meta:
label = "Navigation Overview" label = "Navigation Overview"
icon = "pilcrow" icon = "pilcrow"
template = "nav/blocks/overview_block.html" template = "fragments/blocks/nav/overview.html"
class NavDropdownValue(blocks.StructValue): class NavDropdownValue(blocks.StructValue):
@ -149,7 +149,7 @@ class NavDropdownValue(blocks.StructValue):
return bool(self.get("overview")) return bool(self.get("overview"))
@property @property
def overview(self) -> NavOverview | None: def overview_value(self) -> NavOverview | None:
overview = self.get("overview") overview = self.get("overview")
if overview: if overview:
return overview[0] return overview[0]
@ -160,7 +160,7 @@ class NavDropdownValue(blocks.StructValue):
return bool(self.get("button")) return bool(self.get("button"))
@property @property
def button(self) -> NavButton | None: def button_value(self) -> NavButton | None:
button = self.get("button") button = self.get("button")
if button: if button:
return button[0] return button[0]
@ -171,12 +171,21 @@ class NavDropdownValue(blocks.StructValue):
return bool(self.get("featured_column")) return bool(self.get("featured_column"))
@property @property
def featured_column(self) -> NavFeaturedColumn | None: def featured_column_value(self) -> NavFeaturedColumn | None:
featured_column = self.get("featured_column") featured_column = self.get("featured_column")
if featured_column: if featured_column:
return featured_column[0] return featured_column[0]
return None return None
@property
def ncols(self) -> int:
ncols = len(self.get("columns"))
if self.has_overview:
ncols += 1
if self.has_featured_column:
ncols += 1
return ncols
class NavDropdown(blocks.StructBlock): class NavDropdown(blocks.StructBlock):
title = blocks.CharBlock(max_length=100, help_text="How the dropdown menu will be labelled in the nav bar") title = blocks.CharBlock(max_length=100, help_text="How the dropdown menu will be labelled in the nav bar")
@ -285,5 +294,5 @@ class NavDropdown(blocks.StructBlock):
class Meta: class Meta:
label = "Navigation Dropdown" label = "Navigation Dropdown"
icon = "bars" icon = "bars"
template = "nav/blocks/nav_dropdown_block.html" template = "fragments/blocks/nav/dropdown.html"
value_class = NavDropdownValue value_class = NavDropdownValue

Просмотреть файл

@ -53,6 +53,8 @@ class NavMenu(
@register_setting @register_setting
class SiteNavMenu(BaseSiteSetting): class SiteNavMenu(BaseSiteSetting):
select_related = ["active_nav_menu"]
active_nav_menu = models.ForeignKey( active_nav_menu = models.ForeignKey(
"nav.NavMenu", "nav.NavMenu",
null=True, null=True,

Просмотреть файл

@ -1,3 +1,4 @@
import wagtail_factories
from django.test import TestCase from django.test import TestCase
from wagtail.blocks import StreamBlockValidationError, StructBlockValidationError from wagtail.blocks import StreamBlockValidationError, StructBlockValidationError
from wagtail.models import Locale, Page from wagtail.models import Locale, Page
@ -138,9 +139,9 @@ class TestNavColumnBlock(TestCase):
for link in block["nav_items"]: for link in block["nav_items"]:
self.assertIsInstance(link.block, nav_blocks.NavItem) self.assertIsInstance(link.block, nav_blocks.NavItem)
self.assertIsInstance(link, nav_blocks.NavItemValue) self.assertIsInstance(link, nav_blocks.NavItemValue)
self.assertIsNotNone(block.button) self.assertIsNotNone(block.button_value)
self.assertEqual(len(block["button"]), 1) self.assertEqual(len(block["button"]), 1)
self.assertEqual(block.button, block["button"][0]) self.assertEqual(block.button_value, block["button"][0])
self.assertTrue(block.has_button) self.assertTrue(block.has_button)
def test_without_button(self): def test_without_button(self):
@ -149,7 +150,7 @@ class TestNavColumnBlock(TestCase):
self.assertEqual(len(block["button"]), 0) self.assertEqual(len(block["button"]), 0)
self.assertFalse(block.has_button) self.assertFalse(block.has_button)
self.assertIsNone(block.button) self.assertIsNone(block.button_value)
def test_with_variable_number_of_links(self): def test_with_variable_number_of_links(self):
"""Create a nav_blocks.NavColumn with links.""" """Create a nav_blocks.NavColumn with links."""
@ -247,7 +248,7 @@ class TestNavDropdownBlock(TestCase):
self.assertEqual(len(block["overview"]), 0) self.assertEqual(len(block["overview"]), 0)
self.assertFalse(block.has_overview) self.assertFalse(block.has_overview)
self.assertIsNone(block.overview) self.assertIsNone(block.overview_value)
self.assertEqual(len(block["columns"]), 4) self.assertEqual(len(block["columns"]), 4)
for column in block["columns"]: for column in block["columns"]:
@ -256,12 +257,14 @@ class TestNavDropdownBlock(TestCase):
self.assertEqual(len(block["featured_column"]), 0) self.assertEqual(len(block["featured_column"]), 0)
self.assertFalse(block.has_featured_column) self.assertFalse(block.has_featured_column)
self.assertIsNone(block.featured_column) self.assertIsNone(block.featured_column_value)
self.assertEqual(len(block["button"]), 1) self.assertEqual(len(block["button"]), 1)
self.assertIsInstance(block["button"][0].block, nav_blocks.NavButton) self.assertIsInstance(block["button"][0].block, nav_blocks.NavButton)
self.assertTrue(block.has_button) self.assertTrue(block.has_button)
self.assertEqual(block.button, block["button"][0]) self.assertEqual(block.button_value, block["button"][0])
self.assertEqual(block.ncols, 4)
def test_block_with_overview(self): def test_block_with_overview(self):
"""Create a nav_blocks.NavDropdown with an overview.""" """Create a nav_blocks.NavDropdown with an overview."""
@ -270,10 +273,12 @@ class TestNavDropdownBlock(TestCase):
self.assertEqual(len(block["overview"]), 1) self.assertEqual(len(block["overview"]), 1)
self.assertTrue(block.has_overview) self.assertTrue(block.has_overview)
self.assertEqual(block.overview, block["overview"][0]) self.assertEqual(block.overview_value, block["overview"][0])
self.assertEqual(len(block["columns"]), 3) self.assertEqual(len(block["columns"]), 3)
self.assertEqual(block.ncols, 4)
def test_block_with_featured_column(self): def test_block_with_featured_column(self):
"""Create a nav_blocks.NavDropdown with a featured column.""" """Create a nav_blocks.NavDropdown with a featured column."""
block = nav_factories.NavDropdownFactory(with_featured_column=True) block = nav_factories.NavDropdownFactory(with_featured_column=True)
@ -281,10 +286,12 @@ class TestNavDropdownBlock(TestCase):
self.assertEqual(len(block["featured_column"]), 1) self.assertEqual(len(block["featured_column"]), 1)
self.assertTrue(block.has_featured_column) self.assertTrue(block.has_featured_column)
self.assertEqual(block.featured_column, block["featured_column"][0]) self.assertEqual(block.featured_column_value, block["featured_column"][0])
self.assertEqual(len(block["columns"]), 3) self.assertEqual(len(block["columns"]), 3)
self.assertEqual(block.ncols, 4)
def test_block_with_overview_and_featured_column(self): def test_block_with_overview_and_featured_column(self):
"""Create a nav_blocks.NavDropdown with an overview and a featured column.""" """Create a nav_blocks.NavDropdown with an overview and a featured column."""
block = nav_factories.NavDropdownFactory(with_overview_and_featured_column=True) block = nav_factories.NavDropdownFactory(with_overview_and_featured_column=True)
@ -292,14 +299,62 @@ class TestNavDropdownBlock(TestCase):
self.assertEqual(len(block["overview"]), 1) self.assertEqual(len(block["overview"]), 1)
self.assertTrue(block.has_overview) self.assertTrue(block.has_overview)
self.assertEqual(block.overview, block["overview"][0]) self.assertEqual(block.overview_value, block["overview"][0])
self.assertEqual(len(block["featured_column"]), 1) self.assertEqual(len(block["featured_column"]), 1)
self.assertTrue(block.has_featured_column) self.assertTrue(block.has_featured_column)
self.assertEqual(block.featured_column, block["featured_column"][0]) self.assertEqual(block.featured_column_value, block["featured_column"][0])
self.assertEqual(len(block["columns"]), 2) self.assertEqual(len(block["columns"]), 2)
self.assertEqual(block.ncols, 4)
def test_number_of_cols_prop(self):
"""Test the number of columns property."""
block = nav_factories.NavDropdownFactory()
nav_blocks.NavDropdown().clean(block)
self.assertEqual(block.ncols, 4)
block = nav_factories.NavDropdownFactory(
columns=wagtail_factories.ListBlockFactory(
nav_factories.NavColumnFactory,
**{
"0__title": "Column 1",
"1__title": "Column 2",
},
),
)
nav_blocks.NavDropdown().clean(block)
self.assertEqual(block.ncols, 2)
block = nav_factories.NavDropdownFactory(
columns=wagtail_factories.ListBlockFactory(
nav_factories.NavColumnFactory,
**{
"0__title": "Column 1",
"1__title": "Column 2",
"2__title": "Column 3",
},
),
)
nav_blocks.NavDropdown().clean(block)
self.assertEqual(block.ncols, 3)
block = nav_factories.NavDropdownFactory(
columns=wagtail_factories.ListBlockFactory(
nav_factories.NavColumnFactory,
**{
"0__title": "Column 1",
"1__title": "Column 2",
"2__title": "Column 3",
"3__title": "Column 4",
},
),
)
nav_blocks.NavDropdown().clean(block)
self.assertEqual(block.ncols, 4)
def test_block_without_button(self): def test_block_without_button(self):
"""Create a nav_blocks.NavDropdown without a button.""" """Create a nav_blocks.NavDropdown without a button."""
block = nav_factories.NavDropdownFactory(no_button=True) block = nav_factories.NavDropdownFactory(no_button=True)
@ -307,7 +362,7 @@ class TestNavDropdownBlock(TestCase):
self.assertEqual(len(block["button"]), 0) self.assertEqual(len(block["button"]), 0)
self.assertFalse(block.has_button) self.assertFalse(block.has_button)
self.assertIsNone(block.button) self.assertIsNone(block.button_value)
def test_needs_at_least_one_column(self): def test_needs_at_least_one_column(self):
with self.assertRaises(StructBlockValidationError): with self.assertRaises(StructBlockValidationError):

Просмотреть файл

@ -0,0 +1,4 @@
from pattern_library.monkey_utils import override_tag
from wagtail.images.templatetags.wagtailimages_tags import register
override_tag(register, name="image", default_html="")

Просмотреть файл

@ -0,0 +1,6 @@
from pattern_library.monkey_utils import override_tag
from wagtail.templatetags.wagtailcore_tags import register
override_tag(register, name="include_block", default_html="")
override_tag(register, name="pageurl", default_html="/")
override_tag(register, name="slugurl", default_html="")

Просмотреть файл

@ -520,6 +520,8 @@ WAGTAILIMAGES_INDEX_PAGE_SIZE = env("WAGTAILIMAGES_INDEX_PAGE_SIZE")
WAGTAIL_USAGE_COUNT_ENABLED = True WAGTAIL_USAGE_COUNT_ENABLED = True
WAGTAIL_I18N_ENABLED = True WAGTAIL_I18N_ENABLED = True
WAGTAILIMAGES_EXTENSIONS = ["avif", "gif", "jpg", "jpeg", "png", "webp", "svg"]
# Wagtail Frontend Cache Invalidator Settings # Wagtail Frontend Cache Invalidator Settings
if env("FRONTEND_CACHE_CLOUDFLARE_BEARER_TOKEN"): if env("FRONTEND_CACHE_CLOUDFLARE_BEARER_TOKEN"):

Просмотреть файл

@ -0,0 +1,13 @@
{% load static wagtailadmin_tags %}
{% if style == "primary" %}
{% fragment as nav_button_style %}tw-btn-primary tw-py-4 tw-px-8{% endfragment %}
{% elif style == "primary-full-width" %}
{% fragment as nav_button_style %}tw-btn-primary tw-min-w-full tw-py-4 tw-px-8{% endfragment %}
{% elif style == "cta" %}
{% fragment as nav_button_style %}tw-leading-[130%] tw-text-blue-80 tw-p-2 hover:tw-underline{% endfragment %}
{% endif %}
{% fragment as base_styles %}link-button tw-font-bold tw-text-lg{% endfragment %}
<a class="{{ nav_button_style }} {{ base_styles }} after:tw-content-['_→'] after:tw-font-['Nunito_Sans']" href="{{ value.url }}" {% if value.open_in_new_window %}target="_blank"{% endif %}>{{ value.label }}</a>

Просмотреть файл

@ -0,0 +1,8 @@
# Navigation Button
A call-to-action to a page, relative URL or external URL.
Takes one of the following `style` options:
* `primary` (for a solid primary button)
* `primary-full-width` (for a solid primary button with 100% width of the container%)
* `cta` (for blue text on white background)

Просмотреть файл

@ -0,0 +1,7 @@
name: Navigation Button
context:
value:
label: "Learn more"
url: https://google.com
open_in_new_window: False
style: primary

Просмотреть файл

@ -0,0 +1,16 @@
{% load wagtailcore_tags wagtailadmin_tags %}
{% fragment as base_styles %}tw-flex tw-flex-col tw-items-start tw-gap-8 tw-py-8{% endfragment %}
{% fragment as desktop_layout %}large:tw-py-22 large:tw-px-8 large:tw-ms-9 large:tw-col-span-1{% endfragment %}
<div class="{{ base_styles }} {{ desktop_layout }}">
<h6 class="tw-self-stretch">{{ value.title }}</h6>
<div class="tw-flex tw-flex-col tw-items-start tw-gap-8 tw-self-stretch">
{% for item in value.nav_items %}
<div class="tw-self-stretch">{% include_block item %}</div>
{% endfor %}
{% if value.has_button %}
<div class="tw-self-stretch">{% include_block value.button_value with style="cta" %}</div>
{% endif %}
</div>
</div>

Просмотреть файл

@ -0,0 +1,34 @@
name: Navigation Column
context:
value:
title: "Research & Analysis"
nav_items:
- value:
label: "Trustworthy AI Progress Report"
description: "Promoting openness, competition, and accountability in AI"
url: /foo/bar
is_external: false
- value:
label: "Internet Health Report"
description: "Issues impacting a healthy internet"
url: /foo/bar
is_external: true
- value:
label: "AI Policy"
description: "Policy recommendations for AI"
url: /foo/bar
is_external: false
has_button: true
button:
value:
label: "See all research"
url: /foo/bar
is_external: false
style: "cta-link"
tags:
include_block:
'item':
template_name: "fragments/blocks/nav/item.html"
'value.button with style="cta-link"':
template_name: "fragments/blocks/nav/variants/button--cta-link.html"

Просмотреть файл

@ -0,0 +1,46 @@
{% load static wagtailcore_tags wagtailadmin_tags %}
{% fragment as title_base_typography %}tw-font-sans tw-font-bold tw-text-xl tw-capitalize tw-text-black{% endfragment %}
{% fragment as content_base_styles %}tw-py-0 tw-px-8 tw-gap-8 tw-overflow-y-auto{% endfragment %}
{% fragment as dropdown_selector_base %}tw-flex tw-flex-row tw-items-center tw-justify-between tw-w-full tw-py-12 tw-px-8 tw-gap-4{% endfragment %}
{% fragment as content_desktop %}large:tw-container large:tw-hidden{% endfragment %}
{% fragment as content_desktop_border %}large:tw-border large:tw-border-gray-20{% endfragment %}
{% comment %} 5rem is the height of the navbar and 1px is the size of the dropdown's border {% endcomment %}
{% fragment as content_desktop_positioning %}large:tw-fixed large:tw-top-[calc(5rem+1px)] large:tw-left-0 large:tw-right-0 large:tw-mx-auto{% endfragment %}
{% fragment as title_desktop_typography %}large:tw-text-lg large:tw-text-black{% endfragment %}
{% comment %} 2.5rem for half the navbar's height, 14px for half the title's line height, 6px for the title bottom border and 1 px for the dropdown border:{% endcomment %}
<div class="tw-flex tw-flex-col large:tw-border-b-6 large:tw-pt-[calc(2.5rem-14px)] large:tw-pb-[calc(2.5rem-14px-6px+1px)] large:tw-border-invisible"
data-{{ style }}-dropdown
>
<div class="{{ dropdown_selector_base }} large:tw-items-center large:tw-py-0 " data-accordion-title>
<h5 class="tw-m-0 {{ title_base_typography }} {{ title_desktop_typography }}">{{ value.title }}</h5>
<img src="{% static "_images/chevron.svg" %}" height="18" width="18" alt="" class="tw-w-8 tw-h-auto tw-rotate-180 tw-transition-transform large:tw-hidden">
</div>
<div class="{{ content_base_styles }} {{ content_desktop_positioning }} {{ content_desktop }}" data-accordion-content>
<div class="tw-flex tw-flex-col tw-p-0 tw-bg-white tw-gap-8 {{ content_desktop_border }} large:tw-grid large:tw-w-{{ value.ncols }}/4 large:tw-grid-cols-{{ value.ncols }}" >
{% if value.has_overview %}
{% include_block value.overview_value with button=value.button_value %}
{% endif %}
{% for column in value.columns %}
{% include_block column %}
{% endfor %}
{% if value.has_featured_column %}
{% include_block value.featured_column_value %}
{% endif %}
{% comment %}
If there is a button but no overview, we want to render the button at the bottom
of the dropdown. Otherwise it will be rendered together with the overview.
{% endcomment %}
{% if value.has_button and not value.has_overview %}
<div class="tw-w-full tw-pb-16 large:tw-col-span-4 large:tw-py-0">
{% include_block value.button_value with style="primary-full-width" %}
</div>
{% endif %}
</div>
</div>
</div>

Просмотреть файл

@ -0,0 +1,3 @@
# Navigation Dropdown
A collection of navigation columns.

Просмотреть файл

@ -0,0 +1,29 @@
name: Navigation Dropdown
context:
value:
title: "Who We Are"
has_overview: True
overview:
title: About Us
description: "Mozilla is a global nonprofit dedicated to keeping the Internet a public resource that is open and accessible to all."
columns:
- column:
- column:
has_featured_column: true
featured_column:
- featured_column:
has_button: true
button:
- button:
style: mobile
tags:
include_block:
'value.overview|first with button=value.button|first':
template_name: "fragments/blocks/nav/overview.html"
'column':
template_name: "fragments/blocks/nav/column.html"
'value.button|first with style="primary-full-width"':
template_name: "fragments/blocks/nav/variants/button--primary-full-width.html"
'value.featured_column|first':
template_name: "fragments/blocks/nav/featured-column.html"

Просмотреть файл

@ -0,0 +1,13 @@
{% load wagtailcore_tags wagtailadmin_tags %}
{% fragment as base_styles %}tw-flex tw-flex-col tw-items-start tw-gap-12 tw-py-8 tw-px-12 tw-bg-gray-02 tw-rounded-2xl{% endfragment %}
{% fragment as desktop_layout %}large:tw-py-22 large:tw-px-8 large:tw-ms-9 large:tw-rounded-none large:tw-col-span-1{% endfragment %}
<div class="{{ base_styles }} {{ desktop_layout }}">
<h6 class="tw-self-stretch">{{ value.title }}</h6>
<div class="tw-flex tw-flex-col tw-items-start tw-gap-8 tw-self-stretch">
{% for item in value.nav_items %}
<div class="tw-self-stretch">{% include_block item %}</div>
{% endfor %}
</div>
</div>

Просмотреть файл

@ -0,0 +1,18 @@
name: Navigation Featured Column
context:
value:
title: "Donate"
nav_items:
- value:
label: "Make a Donation"
url: /foo/bar
is_external: false
- value:
label: "Ways to Give"
url: /foo/bar
is_external: true
tags:
include_block:
'item':
template_name: "fragments/blocks/nav/featured-item.html"

Просмотреть файл

@ -0,0 +1,12 @@
{% load static i18n wagtailadmin_tags wagtailimages_tags %}
{% fragment as font_styles %}tw-text-xl large:tw-text-lg tw-font-semibold tw-text-black{% endfragment %}
{% fragment as hover_styles %}group-hover/item:tw-text-blue-80 group-hover/item:tw-underline{% endfragment %}
<div class="tw-group/item tw-flex tw-flex-row tw-self-stretch tw-gap-6 tw-p-4 hover:tw-bg-blue-03">
{% image value.icon fill-18x18 preserve-svg %}
<a class="{{ font_styles }} {{ hover_styles }}"
href="{{ value.url }}" {% if value.is_external %}target="_blank"{% endif %}>
{{ value.label }}
</a>
</div>

Просмотреть файл

@ -0,0 +1,3 @@
# Navigation Featured Item
A featured item in the navigation menu, holding a title, an icon and a link to a resource.

Просмотреть файл

@ -0,0 +1,13 @@
name: Navigation Featured Item
context:
value:
label: "Make a Donation"
icon:
url: /donate
is_external: True
tags:
image:
value.icon fill-18x18 preserve-svg:
raw:
'<img src="http://localhost:8000/images/AsaYTU0vcnQ3JEbiEoPxhIAbmKI=/9977/original/" width="18" height="18" alt=""/>'

Просмотреть файл

@ -1,11 +1,10 @@
{% load static i18n wagtailadmin_tags %} {% load static i18n wagtailadmin_tags %}
{% fragment as font_styles %}tw-text-xl tw-font-semibold tw-capitalize tw-text-black{% endfragment %} {% fragment as font_styles %}tw-text-xl large:tw-text-lg tw-font-semibold tw-text-black{% endfragment %}
{% fragment as hover_styles %}group-hover:tw-text-blue-80 group-hover:tw-underline{% endfragment %} {% fragment as hover_styles %}group-hover/item:tw-text-blue-80 group-hover/item:tw-underline{% endfragment %}
{% fragment as after_arrow_styles %}after:tw-content-['_↗'] after:tw-font-['Nunito_Sans'] after:tw-text-gray-20 group-hover:after:tw-text-blue-80{% endfragment %}
<div class="tw-group tw-flex tw-flex-col tw-gap-2 tw-p-2 hover:tw-bg-blue-03"> <div class="tw-group/item tw-flex tw-flex-col tw-gap-4 tw-p-4 hover:tw-bg-blue-03 hover:tw-rounded">
<a class="{{ font_styles }} {% if value.is_external %} {{ after_arrow_styles }} {% endif %} {{ hover_styles }}" <a class="{{ font_styles }} {% if value.is_external %} after:tw-content-['_↗'] after:tw-font-['Nunito_Sans'] after:tw-text-gray-20 group-hover/item:after:tw-text-blue-80 {% endif %} {{ hover_styles }}"
href="{{ value.url }}" {% if value.is_external %}target="_blank"{% endif %}> href="{{ value.url }}" {% if value.is_external %}target="_blank"{% endif %}>
{{ value.label }} {{ value.label }}
</a> </a>

Просмотреть файл

@ -0,0 +1,15 @@
{% load wagtailcore_tags wagtailadmin_tags %}
{% fragment as base_styles %}tw-flex tw-flex-col tw-items-start tw-gap-14 tw-pb-16{% endfragment %}
{% fragment as border_styles %}tw-border-gray-20 tw-border-b-[1px] large:tw-border-b-0 large:tw-border-r-[1px]{% endfragment %}
{% fragment as desktop_layout %}large:tw-col-span-1 large:tw-my-22 large:tw-pb-0 large:tw-ms-8 large:tw-ps-12 large:tw-pe-8 large:tw-border-r{% endfragment %}
<div class="{{ base_styles }} {{ border_styles }} {{ desktop_layout }}">
<div class="tw-flex tw-flex-col tw-items-start tw-gap-10">
<p class="tw-font-zilla tw-italic tw-font-medium tw-text-2xl tw-text-black tw-my-0 tw-py-0">{{ value.title }}</p>
<div class="[&_p]:tw-text-gray-80 [&_p]:tw-text-base [&_p]:tw-font-normal [&_p]:tw-font-sans">{{ value.description|richtext }}</div>
</div>
{% if button %}
{% include_block button with style="primary" %}
{% endif %}
</div>

Просмотреть файл

@ -0,0 +1,5 @@
# Navigation Overview
A block with a title and a description for the navigation dropdown menu.
Takes an optional "button" block property, which will render the button block as part of the overview block.

Просмотреть файл

@ -0,0 +1,11 @@
name: Navigation Overview
context:
value:
title: About Us
description: Mozilla is a global nonprofit dedicated to keeping the Internet a public resource that is open and accessible to all.
button: True
tags:
include_block:
'button with style="primary"':
template_name: "fragments/blocks/nav/button.html"

Просмотреть файл

@ -0,0 +1 @@
{% include "fragments/blocks/nav/button.html" with style="cta" %}

Просмотреть файл

@ -0,0 +1 @@
{% include "fragments/blocks/nav/button.html" with style="primary-full-width" %}

Просмотреть файл

@ -0,0 +1,29 @@
{% extends "fragments/primary_nav.html" %}
{% load i18n wagtailcore_tags wagtailadmin_tags %}
{% block menu_container_classes %}
{{ block.super }}
large:tw-py-0 tw-bg-white tw-relative
{% endblock menu_container_classes %}
{% block narrow_screen_menu %}
<div class="tw-bg-white tw-min-h-lvh">
<div class="tw-flex tw-flex-col tw-border-y-[1px] tw-border-gray-20 tw-divide-y-[1px] tw-divide-gray-20 tw-w-full" data-nav-accordion-menu>
{% for dropdown in menu.dropdowns %}
{% include_block dropdown with style="mobile" %}
{% endfor %}
</div>
<div class="tw-flex tw-flex-col tw-items-center tw-p-8">
<button class="tw-btn-secondary btn-newsletter tw-w-full">{% trans "Newsletter" %}</button>
</div>
</div>
{% endblock narrow_screen_menu %}
{% block wide_screen_menu %}
<div class="tw-hidden large:tw-flex large:tw-flex-row large:tw-items-center large:tw-h-full" data-nav-accordion-menu>
{% for dropdown in menu.dropdowns %}
{% include_block dropdown with style="desktop" %}
{% endfor %}
</div>
{% endblock wide_screen_menu %}

Просмотреть файл

@ -0,0 +1,12 @@
name: Navigation Menu
context:
menu:
dropdowns:
- dropdown
- dropdown
- dropdown
tags:
include_block:
'dropdown':
template_name: "fragments/blocks/nav/dropdown.html"

Просмотреть файл

@ -4,15 +4,15 @@
{% else %}<div class="primary-nav-container"> {% else %}<div class="primary-nav-container">
{% endif %} {% endif %}
<div class="wrapper-burger {% block wrapper_classes %}{% endblock %}"> <div class="wrapper-burger {% block wrapper_classes %}{% endblock %}">
<div class="menu-container {% block menu_container_classes %}{% endblock %}"> <div class="menu-container large:tw-h-40 {% block menu_container_classes %}{% endblock %}">
{% block menu_content %} {% block menu_content %}
<div class="narrow-screen-menu hidden d-lg-none"> <div class="narrow-screen-menu tw-bg-white hidden d-lg-none tw-overflow-y-auto">
{% block narrow_screen_menu %} {% block narrow_screen_menu %}
<div class="narrow-screen-menu-background tw-dark"> <div class="narrow-screen-menu-background tw-dark">
<div class="narrow-screen-menu-container"> <div class="narrow-screen-menu-container ">
<div class="container" role="navigation"> <div class="tw-container" role="navigation">
<div class="row"> <div class="tw-row">
<div class="col"> <div class="tw-flex-grow tw-max-w-full tw-flex-1">
<div class="nav-links pt-3"> <div class="nav-links pt-3">
{% block narrow_screen_nav_links %} {% block narrow_screen_nav_links %}
<div><a class="{% primary_active_nav request menu_root.full_url menu_root.full_url %}" href="{{ menu_root.url }}">{% trans "Home" %}</a></div> <div><a class="{% primary_active_nav request menu_root.full_url menu_root.full_url %}" href="{{ menu_root.url }}">{% trans "Home" %}</a></div>
@ -28,27 +28,27 @@
{% endblock %} {% endblock %}
</div> </div>
{% endblock %} {% endblock %}
<nav class="container wide-screen-menu-container" title="{% trans "main site navigation" context "Tooltip on menu items" %}"> <nav class="tw-container wide-screen-menu-container tw-relative large:tw-h-full tw-bg-white" title="{% trans "main site navigation" context "Tooltip on menu items" %}">
<div class="row"> <div class="tw-row tw-h-full tw-justify-between large:tw-items-center">
<div class="col"> <div class="col">
<div class="d-flex flex-row justify-content-between"> <div class="tw-flex tw-flex-row tw-justify-between large:tw-items-center">
<div id="primary-nav-links" class="tw-w-full large:tw-w-auto"> <div id="primary-nav-links" class="tw-w-full large:tw-w-auto large:tw-py-0 large:tw-items-center">
<div class="d-flex align-items-center flex-wrap"> <div class="tw-flex tw-items-center tw-flex-wrap large:tw-h-24">
<button class="burger {% if page.zen_nav is not True %} d-lg-none ml-md-0 {% endif %}" aria-label="{% trans 'Open menu' %}"> <button class="burger tw-z-50 tw-bg-white {% if page.zen_nav is not True %} large:tw-hidden small:tw-ml-0 {% endif %}" aria-label="{% trans 'Open menu' %}">
<span class="burger-bar burger-bar-top"></span> <span class="burger-bar burger-bar-top"></span>
<span class="burger-bar burger-bar-middle"></span> <span class="burger-bar burger-bar-middle"></span>
<span class="burger-bar burger-bar-bottom"></span> <span class="burger-bar burger-bar-bottom"></span>
</button> </button>
{% block nav_logo %} {% block nav_logo %}
<a class="logo text-hide" href="/" aria-label="{% trans "Mozilla Foundation Homepage" %}">{% trans "Mozilla Foundation" %}</a> <a class="logo text-hide tw-z-50" href="/" aria-label="{% trans "Mozilla Foundation Homepage" %}">{% trans "Mozilla Foundation" %}</a>
{% endblock %} {% endblock %}
{% if page.zen_nav == True %} {% if page.zen_nav == True %}
<div class="wide-screen-menu hidden"> <div class="wide-screen-menu hidden">
{% else %} {% else %}
<div class="wide-screen-menu"> <div class="wide-screen-menu large:tw-h-full">
{% endif %} {% endif %}
{% block wide_screen_menu %} {% block wide_screen_menu %}
@ -79,9 +79,9 @@
<div id="nav-newsletter-form-wrapper" class="d-none"> <div id="nav-newsletter-form-wrapper" class="d-none">
<div class="container tw-dark"> <div class="tw-container tw-dark">
<div class="row"> <div class="tw-row">
<div class="col-12"> <div class="tw-relative tw-px-8 tw-w-full">
<div class="newsletter-signup-module on-nav tw-pt-24" <div class="newsletter-signup-module on-nav tw-pt-24"
data-module-type="default" data-module-type="default"
data-form-style="header:2-col" data-form-style="header:2-col"

Просмотреть файл

@ -107,7 +107,13 @@
<div class="wrapper"> <div class="wrapper">
<div class="{% block sticky_top_class %} sticky-top {% endblock %} d-print-none"> <div class="{% block sticky_top_class %} sticky-top {% endblock %} d-print-none">
{% block primary_nav %} {% block primary_nav %}
{% include "fragments/primary_nav.html" with background="simple-background" %} {% with nav_menu=settings.nav.SiteNavMenu.active_nav_menu %}
{% if nav_menu %}
{% include "fragments/nav/menu.html" with menu=nav_menu.localized %}
{% else %}
{% include "fragments/primary_nav.html" with background="simple-background" %}
{% endif %}
{% endwith %}
{% endblock %} {% endblock %}
</div> </div>

Просмотреть файл

@ -23,6 +23,8 @@
<div class="tw-row"> <div class="tw-row">
{% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-white" name="White ($white)" hex="#ffffff" %} {% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-white" name="White ($white)" hex="#ffffff" %}
{% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-gray-02" name="Gray 02" hex="#fafafa" %}
{% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-gray-05" name="Gray 05 ($gray-05)" hex="#f2f2f2" %} {% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-gray-05" name="Gray 05 ($gray-05)" hex="#f2f2f2" %}
{% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-gray-20" name="Gray 20 ($gray-20)" hex="#cccccc" %} {% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-gray-20" name="Gray 20 ($gray-20)" hex="#cccccc" %}
@ -53,6 +55,8 @@
</div> </div>
<div class="tw-row"> <div class="tw-row">
{% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-blue-03" name="Blue 03" hex="#f5f5fd" %}
{% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-blue-05" name="Blue 05" hex="#e7e7fc" %} {% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-blue-05" name="Blue 05" hex="#e7e7fc" %}
{% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-blue-10" name="Blue 10" hex="#d3d5fc" %} {% include "wagtailpages/fragments/styleguide_color_block.html" with classname="tw-bg-blue-10" name="Blue 10" hex="#d3d5fc" %}

Просмотреть файл

@ -0,0 +1,54 @@
class Accordion {
static selector() {
return "[data-accordion]";
}
constructor(node) {
this.accordion = node;
this.title = this.accordion.querySelector("[data-accordion-title]");
this.content = this.accordion.querySelector("[data-accordion-content]");
this.chevron = this.title.querySelector("img");
this.titleText = this.title.querySelector("h5");
this.close();
this.bindEvents();
}
bindEvents() {
this.title.addEventListener("click", (e) => {
e.preventDefault();
let open = !this.content.classList.contains("tw-hidden");
if (open) {
this.close();
open = false;
} else {
this.open();
open = true;
}
});
this.title.addEventListener("focus", () => {
this.title.setAttribute("aria-selected", "true");
});
this.title.addEventListener("blur", () => {
this.title.setAttribute("aria-selected", "false");
});
}
open() {
this.content.classList.remove("tw-hidden");
this.chevron.classList.remove("tw-rotate-180");
this.title.setAttribute("aria-expanded", "true");
this.content.setAttribute("aria-hidden", "false");
}
close() {
this.content.classList.add("tw-hidden");
this.chevron.classList.add("tw-rotate-180");
this.title.setAttribute("aria-expanded", "false");
this.content.setAttribute("aria-hidden", "true");
}
}
export default Accordion;

Просмотреть файл

@ -0,0 +1,65 @@
import Accordion from "../accordion/accordion";
class NavDesktopDropdown extends Accordion {
static selector() {
return "[data-desktop-dropdown]";
}
constructor(node) {
super(node);
}
getSiblings() {
let siblings = document.querySelectorAll(NavDesktopDropdown.selector());
return Array.from(siblings).filter((sibling) => sibling !== this.accordion);
}
bindEvents() {
this.accordion.addEventListener("focus", () => {
this.open();
});
this.accordion.addEventListener("pointerenter", () => {
this.open();
});
this.accordion.addEventListener("blur", () => {
this.close();
});
this.accordion.addEventListener("pointerleave", () => {
this.close();
});
}
open() {
super.open();
this.titleText.classList.add("large:tw-text-black");
this.accordion.classList.add("large:tw-border-black");
this.accordion.classList.remove("large:tw-border-transparent");
this.accordion.setAttribute("aria-selected", "true");
this.content.classList.add("large:tw-grid");
this.content.classList.remove("large:tw-hidden");
this.siblings = this.getSiblings();
this.siblings.forEach((sibling) => {
const titleText = sibling.querySelector("[data-accordion-title] h5");
titleText.classList.remove("large:tw-text-black");
titleText.classList.add("large:tw-text-gray-40");
});
}
close() {
super.close();
this.titleText.classList.remove("large:tw-text-black");
this.accordion.classList.remove("large:tw-border-black");
this.accordion.classList.add("large:tw-border-transparent");
this.accordion.setAttribute("aria-selected", "false");
this.content.classList.remove("large:tw-grid");
this.content.classList.add("large:tw-hidden");
this.siblings = this.getSiblings();
this.siblings.forEach((sibling) => {
const titleText = sibling.querySelector("[data-accordion-title] h5");
titleText.classList.add("large:tw-text-black");
titleText.classList.remove("large:tw-text-gray-40");
});
}
}
export default NavDesktopDropdown;

Просмотреть файл

@ -0,0 +1,39 @@
import Accordion from "../accordion/accordion";
class NavMobileDropdown extends Accordion {
static selector() {
return "[data-mobile-dropdown]";
}
constructor(node) {
super(node);
}
bindEvents() {
super.bindEvents();
this.accordion.addEventListener("focus", () => {
this.addHoverEffects();
});
this.accordion.addEventListener("pointerenter", () => {
this.addHoverEffects();
});
this.accordion.addEventListener("blur", () => {
this.removeHoverEffects();
});
this.accordion.addEventListener("pointerleave", () => {
this.removeHoverEffects();
});
}
addHoverEffects() {
this.accordion.classList.add("tw-bg-blue-03");
this.titleText.classList.add("tw-underline");
}
removeHoverEffects() {
this.accordion.classList.remove("tw-bg-blue-03");
this.titleText.classList.remove("tw-underline");
}
}
export default NavMobileDropdown;

Просмотреть файл

@ -38,6 +38,9 @@ import { initYoutubeRegretsAccordions } from "./foundation/pages/youtube-regrets
import { initYouTubeRegretsRecommendationsPieChart } from "./foundation/pages/youtube-regrets/recommendations-pie-chart"; import { initYouTubeRegretsRecommendationsPieChart } from "./foundation/pages/youtube-regrets/recommendations-pie-chart";
import { initYoutubeRegretsCarousel } from "./foundation/pages/youtube-regrets/carousel"; import { initYoutubeRegretsCarousel } from "./foundation/pages/youtube-regrets/carousel";
import { initYoutubeRegretsLocomotiveScroll } from "./foundation/pages/youtube-regrets/locomotive-scroll"; import { initYoutubeRegretsLocomotiveScroll } from "./foundation/pages/youtube-regrets/locomotive-scroll";
import Accordion from "./components/accordion/accordion.js";
import NavDesktopDropdown from "./components/nav/desktop-dropdown.js";
import NavMobileDropdown from "./components/nav/mobile-dropdown.js";
// Initializing component a11y browser console logging // Initializing component a11y browser console logging
if (process.env.NODE_ENV === "development") { if (process.env.NODE_ENV === "development") {
@ -53,6 +56,11 @@ let env, networkSiteURL;
// until all the React stuff is _actually_ done. // until all the React stuff is _actually_ done.
const apps = []; const apps = [];
function initComponent(ComponentClass) {
const items = document.querySelectorAll(ComponentClass.selector());
items.forEach((item) => new ComponentClass(item));
}
let main = { let main = {
init() { init() {
injectMultipageNav(); injectMultipageNav();
@ -62,6 +70,10 @@ let main = {
Dropdowns.init(); Dropdowns.init();
FoundationCarousels.init(); FoundationCarousels.init();
initComponent(Accordion);
initComponent(NavDesktopDropdown);
initComponent(NavMobileDropdown);
this.fetchEnv((envData) => { this.fetchEnv((envData) => {
env = envData; env = envData;
networkSiteURL = env.NETWORK_SITE_URL; networkSiteURL = env.NETWORK_SITE_URL;

Просмотреть файл

@ -54,11 +54,23 @@ let primaryNav = {
} }
} }
function setBodyHeight(openMenu) {
// set body height and overflow to prevent scrolling on the body when mobile nav is open
if (openMenu) {
document.body.style.height = `100vh`;
document.body.style.overflow = `hidden`;
} else {
document.body.style.height = `auto`;
document.body.style.overflow = `auto`;
}
}
function setMenuState(openMenu) { function setMenuState(openMenu) {
setWideMenuState(openMenu); setWideMenuState(openMenu);
setNarrowMenuState(openMenu); setNarrowMenuState(openMenu);
setBurgerState(openMenu); setBurgerState(openMenu);
trackMenuState(openMenu); trackMenuState(openMenu);
setBodyHeight(openMenu);
} }
document.addEventListener(`keyup`, (e) => { document.addEventListener(`keyup`, (e) => {

Просмотреть файл

@ -237,17 +237,12 @@
} }
.narrow-screen-menu { .narrow-screen-menu {
position: absolute; position: fixed;
@apply tw-pt-32; top: 65px;
bottom: 0;
@media (min-width: $nav-full-logo-breakpoint) {
padding-top: 72px;
}
height: 100vh;
top: 0;
left: 0; left: 0;
width: 100%; right: 0;
overflow-y: auto;
transition: transition:
opacity 0.2s, opacity 0.2s,
height 0s; height 0s;

Просмотреть файл

@ -17,6 +17,13 @@ module.exports = {
// eventually we have to extract what bootstrap base/reset styles we need // eventually we have to extract what bootstrap base/reset styles we need
preflight: false, preflight: false,
}, },
safelist: [
{
// Handle dynamic grid column sizing on templates (see fragments/blocks/nav/dropdown.html)
pattern: /w-(1|2|3|4)\/4/,
variants: ['large', 'hover', 'focus', 'large:hover', 'large:focus'],
},
],
plugins: [ plugins: [
plugin(function ({ addUtilities }) { plugin(function ({ addUtilities }) {
// Adding Column Count to Tailwind CSS // Adding Column Count to Tailwind CSS
@ -80,6 +87,9 @@ module.exports = {
gridAutoRows: { gridAutoRows: {
"1fr": "1fr", "1fr": "1fr",
}, },
borderWidth: {
'6': '6px',
}
}, },
// Overriding default spacing // Overriding default spacing
spacing: { spacing: {