Skip to content

Commit

Permalink
[TP-268][Main Nav] Nav dropdown (#12114)
Browse files Browse the repository at this point in the history
* Better NavColumn block with default values

* Add NavOverview block

* Setup rich text features on overview description

* Define an ExtendedStructBlockFactory

* Use ExtendedStructBlockFactory

* Add NavDropdown block

* Lint

* Fix types
  • Loading branch information
jhonatan-lopes committed Apr 3, 2024
1 parent f255434 commit 49432c3
Show file tree
Hide file tree
Showing 4 changed files with 327 additions and 36 deletions.
128 changes: 126 additions & 2 deletions network-api/networkapi/nav/blocks.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
from collections import OrderedDict

from django.core.exceptions import ValidationError
from django.forms.utils import ErrorList
from wagtail import blocks
from wagtail.telepath import register

Expand Down Expand Up @@ -57,12 +59,134 @@ class Meta:
register(BaseLinkBlockAdapter(), NavButton)


class NavColumnValue(blocks.StructValue):
@property
def has_button(self) -> bool:
return bool(self.get("button"))

@property
def button(self) -> NavButton | None:
button = self.get("button")
if button:
return button[0]
return None


class NavColumn(blocks.StructBlock):
title = blocks.CharBlock(max_length=100)
links = blocks.ListBlock(NavItem, min_num=1, max_num=4)
button = blocks.ListBlock(NavButton, required=False, min_num=0, max_num=1)
# Empty default so that it starts collapsed:
nav_items = blocks.ListBlock(NavItem, min_num=1, max_num=4, label="Items", default=[])
button = blocks.ListBlock(
NavButton,
required=False,
min_num=0,
max_num=1,
default=[],
label="Column Button",
help_text="Adds a CTA button to the bottom of the nav column.",
)

class Meta:
label = "Navigation Column"
icon = "list-ul"
template = "nav/blocks/nav_column_block.html"
value_class = NavColumnValue


class NavOverview(blocks.StructBlock):
title = blocks.CharBlock(max_length=100)
description = blocks.RichTextBlock(features=["bold", "italic"], max_length=200)

class Meta:
label = "Navigation Overview"
icon = "pilcrow"
template = "nav/blocks/overview_block.html"


class NavDropdownValue(blocks.StructValue):
@property
def has_overview(self) -> bool:
return bool(self.get("overview"))

@property
def overview(self) -> NavOverview | None:
overview = self.get("overview")
if overview:
return overview[0]
return None

@property
def has_button(self) -> bool:
return bool(self.get("button"))

@property
def button(self) -> NavButton | None:
button = self.get("button")
if button:
return button[0]
return None


class NavDropdown(blocks.StructBlock):
title = blocks.CharBlock(max_length=100, help_text="How the dropdown menu will be labelled in the nav bar")
overview = blocks.ListBlock(
NavOverview(label="Overview"),
min_num=0,
max_num=1,
label="Overview",
help_text="If added, the overview will take the place of the first column",
default=[],
)
columns = blocks.ListBlock(
NavColumn(label="Column"),
min_num=1,
max_num=4,
label="Columns",
help_text="Add up to 4 columns of navigation links",
)
button = blocks.ListBlock(
NavButton,
required=False,
min_num=0,
max_num=1,
default=[],
label="Dropdown Button",
help_text="Use it to add a CTA to link to the contents of the dropdown menu",
)

def clean(self, value):
result = super().clean(value)
errors = {}

if result["overview"] and len(result["columns"]) > 3:
errors["overview"] = ErrorList(
[
blocks.ListBlockValidationError(
block_errors={},
non_block_errors=ErrorList(
[ValidationError("Overview cannot be used with more than 3 nav columns.")]
),
)
]
)
errors["columns"] = ErrorList(
[
blocks.ListBlockValidationError(
block_errors={},
non_block_errors=ErrorList(
[ValidationError('A maximum of 3 columns can be added together with an "overview".')]
),
)
]
)

if errors:
raise blocks.StructBlockValidationError(block_errors=errors)

return result

class Meta:
label = "Navigation Dropdown"
icon = "bars"
template = "nav/blocks/nav_dropdown_block.html"
value_class = NavDropdownValue
80 changes: 58 additions & 22 deletions network-api/networkapi/nav/factories.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import factory
import wagtail_factories
from wagtail import models as wagtail_models
from wagtail.rich_text import RichText

from networkapi.nav import blocks as nav_blocks
from networkapi.wagtailcustomization.factories.blocks import ExtendedStructBlockFactory


class NavItemFactory(wagtail_factories.StructBlockFactory):
class NavItemFactory(ExtendedStructBlockFactory):
"""Factory for NavLinkBlock.
Use traits to create instances based on the type of link needed:
Expand All @@ -24,14 +26,6 @@ class NavItemFactory(wagtail_factories.StructBlockFactory):
class Meta:
model = nav_blocks.NavItem

@classmethod
def _construct_struct_value(cls, block_class, params):
"""Use NavLinkValue to create the StructValue instance."""
return nav_blocks.NavItemValue(
block_class(),
[(name, value) for name, value in params.items()],
)

class Params:
page_link = factory.Trait(
link_to="page",
Expand All @@ -53,7 +47,7 @@ class Params:
relative_url = ""


class NavButtonFactory(wagtail_factories.StructBlockFactory):
class NavButtonFactory(ExtendedStructBlockFactory):
"""Factory for NavButtonBlock.
Use traits to create instances based on the type of link needed:
Expand All @@ -72,14 +66,6 @@ class NavButtonFactory(wagtail_factories.StructBlockFactory):
class Meta:
model = nav_blocks.NavButton

@classmethod
def _construct_struct_value(cls, block_class, params):
"""Use NavLinkValue to create the StructValue instance."""
return nav_blocks.NavItemValue(
block_class(),
[(name, value) for name, value in params.items()],
)

class Params:
page_link = factory.Trait(
link_to="page",
Expand All @@ -100,13 +86,63 @@ class Params:
relative_url = ""


class NavColumnFactory(wagtail_factories.StructBlockFactory):
class NavColumnFactory(ExtendedStructBlockFactory):
class Meta:
model = nav_blocks.NavColumn

class Params:
with_button = False
no_button = factory.Trait(button=[])

title = factory.Faker("sentence", nb_words=3)
nav_items = wagtail_factories.ListBlockFactory(
NavItemFactory,
**{
"0__external_url_link": True,
"1__external_url_link": True,
"2__external_url_link": True,
"3__external_url_link": True,
},
)
button = wagtail_factories.ListBlockFactory(NavButtonFactory, **{"0__external_url_link": True})


class NavOverviewFactory(wagtail_factories.StructBlockFactory):
class Meta:
model = nav_blocks.NavOverview

title = factory.Faker("sentence", nb_words=3)
links = factory.List([factory.SubFactory(NavItemFactory) for _ in range(4)])
button = factory.LazyAttribute(lambda o: [NavButtonFactory()] if o.with_button else [])
description = RichText(str(factory.Faker("sentence", nb_words=6)))


class NavDropdownFactory(ExtendedStructBlockFactory):
class Meta:
model = nav_blocks.NavDropdown

class Params:
no_overview = factory.Trait(overview=[])
all_columns = factory.Trait(
overview=[],
columns=wagtail_factories.ListBlockFactory(
NavColumnFactory,
**{
"0__title": factory.Faker("sentence", nb_words=3),
"1__title": factory.Faker("sentence", nb_words=3),
"2__title": factory.Faker("sentence", nb_words=3),
"3__title": factory.Faker("sentence", nb_words=3),
},
),
)
no_button = factory.Trait(button=[])

overview = wagtail_factories.ListBlockFactory(
NavOverviewFactory, **{"0__title": factory.Faker("sentence", nb_words=3)}
)
columns = wagtail_factories.ListBlockFactory(
NavColumnFactory,
**{
"0__title": factory.Faker("sentence", nb_words=3),
"1__title": factory.Faker("sentence", nb_words=3),
"2__title": factory.Faker("sentence", nb_words=3),
},
)
button = wagtail_factories.ListBlockFactory(NavButtonFactory, **{"0__external_url_link": True})
Loading

0 comments on commit 49432c3

Please sign in to comment.