Skip to content

Commit b56dea1

Browse files
committed
CMS: Update popular topics component and validation
1 parent fdb88c3 commit b56dea1

File tree

6 files changed

+275
-1
lines changed

6 files changed

+275
-1
lines changed

cms/dynamic_content/blocks.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@
1313

1414
MINIMUM_ROWS_NUMBER_BLOCK_COUNT: int = 1
1515
MAXIMUM_ROWS_NUMBER_BLOCK_COUNT: int = 2
16+
17+
POPULAR_TOPICS_BOTTOM_RIGHT_COLUMN_COUNT: int = 2
18+
POPULAR_TOPICS_HEADLINE_NUMBER_BLOCK_COUNT: int = 2
19+
1620
METRIC_NUMBER_BLOCK_DATE_PREFIX_DEFAULT_TEXT = "Up to"
1721

1822

@@ -45,6 +49,46 @@ class Meta:
4549
icon = "table"
4650

4751

52+
class PopularTopicsHeadlineNumberBlockTypes(blocks.StreamBlock):
53+
headline_number = HeadlineNumberComponent(help_text=help_texts.HEADLINE_BLOCK_FIELD)
54+
trend_number = TrendNumberComponent(help_text=help_texts.TREND_BLOCK_FIELD)
55+
56+
class Meta:
57+
icon = "bars"
58+
59+
60+
class PopularTopicsMetricNumberBlockTypes(blocks.StructBlock):
61+
title = blocks.TextBlock(required=True, help_text=help_texts.TITLE_FIELD)
62+
date_prefix = blocks.TextBlock(
63+
required=True,
64+
default=METRIC_NUMBER_BLOCK_DATE_PREFIX_DEFAULT_TEXT,
65+
help_text=help_texts.HEADLINE_DATE_PREFIX,
66+
)
67+
headline_metrics = PopularTopicsHeadlineNumberBlockTypes(
68+
required=True,
69+
min_num=POPULAR_TOPICS_HEADLINE_NUMBER_BLOCK_COUNT,
70+
max_num=POPULAR_TOPICS_HEADLINE_NUMBER_BLOCK_COUNT,
71+
help_text=help_texts.POPULAR_TOPICS_METRIC_CARDS.format(
72+
POPULAR_TOPICS_HEADLINE_NUMBER_BLOCK_COUNT
73+
),
74+
)
75+
76+
class Meta:
77+
icon = "table"
78+
79+
80+
class PopularTopicsRightColumnBottomRowBlockTypes(blocks.StreamBlock):
81+
headline_metric_card = PopularTopicsMetricNumberBlockTypes(
82+
required=True,
83+
min_num=POPULAR_TOPICS_BOTTOM_RIGHT_COLUMN_COUNT,
84+
max_num=POPULAR_TOPICS_BOTTOM_RIGHT_COLUMN_COUNT,
85+
help_text=help_texts.POPULAR_TOPICS_HEADLINE_METRIC_CARDS,
86+
)
87+
88+
class Meta:
89+
icon = "table"
90+
91+
4892
class MetricNumberBlock(blocks.StreamBlock):
4993
column = MetricNumberBlockTypes()
5094

cms/dynamic_content/cards.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
HeadlineNumberBlockTypes,
1313
MetricNumberBlock,
1414
PageLinkChooserBlock,
15+
PopularTopicsRightColumnBottomRowBlockTypes,
1516
RelatedLinkBlock,
1617
SourceLinkBlock,
1718
)
@@ -46,6 +47,9 @@
4647

4748
MINIMUM_SEGMENTS_COUNT: int = 1
4849

50+
POPULAR_TOPICS_SEGMENT_COUNT: int = 1
51+
POPULAR_TOPICS_RIGHT_COLUMN_BOTTOM_ROW_SEGMENT_COUNT: int = 2
52+
4953
DEFAULT_SIMPLE_CHART_X_AXIS = "date"
5054
DEFAULT_SIMPLE_CHART_Y_AXIS = "metric"
5155

@@ -575,6 +579,44 @@ class ChartRowBlockTypes(blocks.StreamBlock):
575579
dual_category_chart_card = DualCategoryChartCard()
576580

577581

582+
class PopularTopicsLeftColumnBlockTypes(blocks.StreamBlock):
583+
weather_health_alert_card = WeatherHealthAlertsCard()
584+
chart_card_with_description = ChartWithDescriptionCard()
585+
586+
class Meta:
587+
icon = "standalone_chart"
588+
589+
590+
class PopularTopicsRightColumnTopRowBlockTypes(blocks.StreamBlock):
591+
chart_card = SimplifiedChartWithLink()
592+
593+
class Meta:
594+
icon = "standalone_chart"
595+
596+
597+
class PopularTopicsCard(blocks.StructBlock):
598+
left_column = PopularTopicsLeftColumnBlockTypes(
599+
min_num=POPULAR_TOPICS_SEGMENT_COUNT,
600+
max_num=POPULAR_TOPICS_SEGMENT_COUNT,
601+
help_text=help_texts.POPULAR_TOPICS_LEFT_COLUMN,
602+
)
603+
right_column_top_row = PopularTopicsRightColumnTopRowBlockTypes(
604+
min_num=POPULAR_TOPICS_SEGMENT_COUNT,
605+
max_num=POPULAR_TOPICS_SEGMENT_COUNT,
606+
help_text=help_texts.POPULAR_TOPICS_RIGHT_COLUMN_TOP_ROW,
607+
)
608+
right_column_bottom_row = PopularTopicsRightColumnBottomRowBlockTypes(
609+
min_num=POPULAR_TOPICS_RIGHT_COLUMN_BOTTOM_ROW_SEGMENT_COUNT,
610+
max_num=POPULAR_TOPICS_RIGHT_COLUMN_BOTTOM_ROW_SEGMENT_COUNT,
611+
help_text=help_texts.POPULAR_TOPICS_RIGHT_COLUMN_BOTTOM_ROW.format(
612+
POPULAR_TOPICS_RIGHT_COLUMN_BOTTOM_ROW_SEGMENT_COUNT
613+
),
614+
)
615+
616+
class Meta:
617+
icon = "standalone_chart"
618+
619+
578620
class ChartRowCard(blocks.StructBlock):
579621
columns = ChartRowBlockTypes(
580622
min_num=MINIMUM_COLUMNS_CHART_COLUMNS_COUNT,

cms/dynamic_content/help_texts.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,31 @@
44
So by moving 1 column component above the other, that component will be rendered in the column left of the other.
55
"""
66

7+
POPULAR_TOPICS_LEFT_COLUMN: str = """
8+
This will be used to display a full height card on the left column.
9+
Choose either a weather health alerts card or a chart card with description.
10+
"""
11+
12+
POPULAR_TOPICS_RIGHT_COLUMN_TOP_ROW: str = """
13+
This will be used to display a chart card in the top row of the second (right) column.
14+
"""
15+
16+
POPULAR_TOPICS_RIGHT_COLUMN_BOTTOM_ROW: str = """
17+
This will require {} headline metrics cards which will be displayed from left to right
18+
with each card occupying and sharing half of the bottom row right column of the
19+
popular topics component.
20+
"""
21+
22+
POPULAR_TOPICS_METRIC_CARDS: str = """
23+
This block only allows {} headline number blocks to be added.
24+
It can be used to add headline number and trend number.
25+
"""
26+
27+
POPULAR_TOPICS_HEADLINE_METRIC_CARDS: str = """
28+
Each card will be displayed from left to right and will share and occupy half
29+
of the bottom row right column of the popular topics component.
30+
"""
31+
732
HEADLINE_COLUMNS_IN_CHART_CARD: str = """
833
Add up to {} headline or trend number column components within this space.
934
Note that these figures will be displayed within the card, and above the chart itself.

cms/dynamic_content/sections.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class ContentCardsSectionWithLink(StreamBlock):
4040
chart_card_section = cards.ChartCardSection()
4141
headline_numbers_row_card = cards.HeadlineNumbersRowCard()
4242
weather_health_alert_card = cards.WeatherHealthAlertsCard()
43+
popular_topics_card = cards.PopularTopicsCard()
4344

4445

4546
class Section(StructBlock):

metrics/domain/common/utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def selectable_simplified_chart_choices(cls) -> tuple[tuple[str, str], ...]:
6767
Examples:
6868
(("line_single_simplified", "line_single_simplified"), ...)
6969
"""
70-
selectable = (cls.line_single_simplified,)
70+
selectable = (cls.line_single_simplified, cls.line_multi_coloured)
7171
return tuple((chart_type.value, chart_type.value) for chart_type in selectable)
7272

7373
@classmethod
Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
import pytest
2+
3+
from cms.dynamic_content.blocks import (
4+
POPULAR_TOPICS_BOTTOM_RIGHT_COLUMN_COUNT,
5+
POPULAR_TOPICS_HEADLINE_NUMBER_BLOCK_COUNT,
6+
PopularTopicsMetricNumberBlockTypes,
7+
PopularTopicsRightColumnBottomRowBlockTypes,
8+
)
9+
from cms.dynamic_content.cards import (
10+
POPULAR_TOPICS_RIGHT_COLUMN_BOTTOM_ROW_SEGMENT_COUNT,
11+
PopularTopicsCard,
12+
PopularTopicsLeftColumnBlockTypes,
13+
PopularTopicsRightColumnTopRowBlockTypes,
14+
SimplifiedChartWithLink,
15+
)
16+
17+
18+
class TestPopularTopicsFirstColumnBlockTypes:
19+
@pytest.mark.parametrize(
20+
"expected_field_name",
21+
(
22+
"weather_health_alert_card",
23+
"chart_card_with_description",
24+
),
25+
)
26+
def test_has_expected_child_blocks(self, expected_field_name: str) -> None:
27+
"""
28+
Given an instance of `PopularTopicsLeftColumnBlockTypes`
29+
When inspecting child blocks
30+
Then the expected field(s) are available
31+
"""
32+
# Given
33+
first_column_block_types = PopularTopicsLeftColumnBlockTypes()
34+
35+
# When
36+
selected_field = first_column_block_types.child_blocks.get(expected_field_name)
37+
38+
# Then
39+
assert selected_field is not None
40+
41+
42+
class TestPopularTopicsSecondColumnTopRightBlockTypes:
43+
def test_has_expected_chart_card_block(self) -> None:
44+
"""
45+
Given an instance of `PopularTopicsRightColumnTopRowBlockTypes`
46+
When inspecting child blocks
47+
Then the chart card field is available and correctly typed
48+
"""
49+
# Given
50+
top_right_block_types = PopularTopicsRightColumnTopRowBlockTypes()
51+
52+
# When
53+
selected_field = top_right_block_types.child_blocks.get("chart_card")
54+
55+
# Then
56+
assert isinstance(selected_field, SimplifiedChartWithLink)
57+
58+
59+
class TestPopularTopicsSecondColumnBottomRowBlockTypes:
60+
def test_has_expected_headline_metric_card_block(self) -> None:
61+
"""
62+
Given an instance of `PopularTopicsRightColumnBottomRowBlockTypes`
63+
When inspecting child blocks
64+
Then the headline metric card field is available and correctly typed
65+
"""
66+
# Given
67+
bottom_row_block_types = PopularTopicsRightColumnBottomRowBlockTypes()
68+
69+
# When
70+
selected_field = bottom_row_block_types.child_blocks.get("headline_metric_card")
71+
72+
# Then
73+
assert isinstance(selected_field, PopularTopicsMetricNumberBlockTypes)
74+
assert selected_field.meta.min_num == POPULAR_TOPICS_BOTTOM_RIGHT_COLUMN_COUNT
75+
assert selected_field.meta.max_num == POPULAR_TOPICS_BOTTOM_RIGHT_COLUMN_COUNT
76+
77+
78+
class TestPopularTopicsCard:
79+
@pytest.mark.parametrize(
80+
"field_name, expected_block_type",
81+
(
82+
("left_column", PopularTopicsLeftColumnBlockTypes),
83+
("right_column_top_row", PopularTopicsRightColumnTopRowBlockTypes),
84+
("right_column_bottom_row", PopularTopicsRightColumnBottomRowBlockTypes),
85+
),
86+
)
87+
def test_has_expected_child_blocks(
88+
self,
89+
field_name: str,
90+
expected_block_type: type,
91+
) -> None:
92+
"""
93+
Given an instance of `PopularTopicsCard`
94+
When inspecting child blocks
95+
Then the expected field(s) use the correct block type
96+
"""
97+
# Given
98+
popular_topics_card = PopularTopicsCard()
99+
100+
# When
101+
selected_field = popular_topics_card.child_blocks.get(field_name)
102+
103+
# Then
104+
assert isinstance(selected_field, expected_block_type)
105+
106+
@pytest.mark.parametrize(
107+
"field_name, expected_min_num, expected_max_num",
108+
(
109+
("left_column", 1, 1),
110+
("right_column_top_row", 1, 1),
111+
(
112+
"right_column_bottom_row",
113+
POPULAR_TOPICS_RIGHT_COLUMN_BOTTOM_ROW_SEGMENT_COUNT,
114+
POPULAR_TOPICS_RIGHT_COLUMN_BOTTOM_ROW_SEGMENT_COUNT,
115+
),
116+
),
117+
)
118+
def test_card_columns_have_expected_required_counts(
119+
self,
120+
field_name: str,
121+
expected_min_num: int,
122+
expected_max_num: int,
123+
) -> None:
124+
"""
125+
Given an instance of `PopularTopicsCard`
126+
When inspecting stream count constraints for each card area
127+
Then each field has the expected min and max values
128+
"""
129+
# Given
130+
popular_topics_card = PopularTopicsCard()
131+
132+
# When
133+
selected_field = popular_topics_card.child_blocks[field_name]
134+
135+
# Then
136+
assert selected_field.meta.min_num == expected_min_num
137+
assert selected_field.meta.max_num == expected_max_num
138+
139+
140+
class TestPopularTopicsMetricNumberBlockTypes:
141+
def test_headline_metrics_are_required_and_allow_exactly_two_entries(self) -> None:
142+
"""
143+
Given an instance of `PopularTopicsMetricNumberBlockTypes`
144+
When inspecting the headline_metrics field configuration
145+
Then headline_metrics are required and constrained to exactly two entries
146+
"""
147+
# Given
148+
metric_number_block = PopularTopicsMetricNumberBlockTypes()
149+
150+
# When
151+
headline_metrics_block = metric_number_block.child_blocks["headline_metrics"]
152+
153+
# Then
154+
assert headline_metrics_block.meta.required is True
155+
assert (
156+
headline_metrics_block.meta.min_num
157+
== POPULAR_TOPICS_HEADLINE_NUMBER_BLOCK_COUNT
158+
)
159+
assert (
160+
headline_metrics_block.meta.max_num
161+
== POPULAR_TOPICS_HEADLINE_NUMBER_BLOCK_COUNT
162+
)

0 commit comments

Comments
 (0)