From a1aeefa6ea62e31b48aef8b2f0fa72197836bae7 Mon Sep 17 00:00:00 2001
From: 4the4ryushin <aman2001mi@gmail.com>
Date: Fri, 7 Apr 2023 17:01:55 +0530
Subject: [PATCH] Add initial support for Panels with attrs

- Rename shared/attrs template for reuse in other files
- Add `attrs` to Panel and a `BASE_ATTRS` property for reuse
- Add initial unit tests
---
 wagtail/admin/panels/base.py                  | 13 ++++++++++
 .../{tables => shared}/attrs.html             |  0
 .../templates/wagtailadmin/tables/table.html  |  4 ++--
 .../wagtailadmin/tables/title_cell.html       |  2 +-
 wagtail/admin/tests/test_edit_handlers.py     | 24 +++++++++++++++++--
 .../snippets/tables/title_cell.html           |  2 +-
 6 files changed, 39 insertions(+), 6 deletions(-)
 rename wagtail/admin/templates/wagtailadmin/{tables => shared}/attrs.html (100%)

diff --git a/wagtail/admin/panels/base.py b/wagtail/admin/panels/base.py
index f4ddf8167d..cb576ce528 100644
--- a/wagtail/admin/panels/base.py
+++ b/wagtail/admin/panels/base.py
@@ -67,6 +67,8 @@ class Panel:
     :param icon: The name of the icon to display next to the panel heading.
     """
 
+    BASE_ATTRS = {}
+
     def __init__(
         self,
         heading="",
@@ -74,6 +76,7 @@ class Panel:
         help_text="",
         base_form_class=None,
         icon="",
+        attrs=None,
     ):
         self.heading = heading
         self.classname = classname
@@ -81,6 +84,10 @@ class Panel:
         self.base_form_class = base_form_class
         self.icon = icon
         self.model = None
+        self.attrs = self.BASE_ATTRS.copy()
+
+        if attrs is not None:
+            self.attrs.update(attrs)
 
     def clone(self):
         """
@@ -95,6 +102,7 @@ class Panel:
         """
         return {
             "icon": self.icon,
+            "attrs": self.attrs,
             "heading": self.heading,
             "classname": self.classname,
             "help_text": self.help_text,
@@ -245,6 +253,10 @@ class Panel:
         def classes(self):
             return self.panel.classes()
 
+        @property
+        def attrs(self):
+            return self.panel.attrs
+
         @property
         def icon(self):
             return self.panel.icon
@@ -273,6 +285,7 @@ class Panel:
         def get_context_data(self, parent_context=None):
             context = super().get_context_data(parent_context)
             context["self"] = self
+            context["attrs"] = self.attrs
             return context
 
         def get_comparison(self):
diff --git a/wagtail/admin/templates/wagtailadmin/tables/attrs.html b/wagtail/admin/templates/wagtailadmin/shared/attrs.html
similarity index 100%
rename from wagtail/admin/templates/wagtailadmin/tables/attrs.html
rename to wagtail/admin/templates/wagtailadmin/shared/attrs.html
diff --git a/wagtail/admin/templates/wagtailadmin/tables/table.html b/wagtail/admin/templates/wagtailadmin/tables/table.html
index cc4e8e9075..f205a0b919 100644
--- a/wagtail/admin/templates/wagtailadmin/tables/table.html
+++ b/wagtail/admin/templates/wagtailadmin/tables/table.html
@@ -1,6 +1,6 @@
 {% load wagtailadmin_tags %}
 
-<table{% include "wagtailadmin/tables/attrs.html" with attrs=table.attrs %}>
+<table{% include "wagtailadmin/shared/attrs.html" with attrs=table.attrs %}>
     {% if table.has_column_widths %}
         {% for column in table.columns.values %}
             <col {% if column.width %}width="{{ column.width }}"{% endif %} />
@@ -15,7 +15,7 @@
     </thead>
     <tbody>
         {% for row in table.rows %}
-            <tr{% include "wagtailadmin/tables/attrs.html" with attrs=row.attrs %}>
+            <tr{% include "wagtailadmin/shared/attrs.html" with attrs=row.attrs %}>
                 {% for cell in row.values %}
                     {% component cell %}
                 {% endfor %}
diff --git a/wagtail/admin/templates/wagtailadmin/tables/title_cell.html b/wagtail/admin/templates/wagtailadmin/tables/title_cell.html
index 7ef43bd4f9..b662eec7a5 100644
--- a/wagtail/admin/templates/wagtailadmin/tables/title_cell.html
+++ b/wagtail/admin/templates/wagtailadmin/tables/title_cell.html
@@ -2,7 +2,7 @@
     <div class="title-wrapper">
         {% block title %}
             {% if link_url %}
-                <a {% include "wagtailadmin/tables/attrs.html" with attrs=link_attrs %}>{{ value }}</a>
+                <a {% include "wagtailadmin/shared/attrs.html" with attrs=link_attrs %}>{{ value }}</a>
             {% elif label_id %}
                 <label for="{{ label_id }}">{{ value }}</label>
             {% else %}
diff --git a/wagtail/admin/tests/test_edit_handlers.py b/wagtail/admin/tests/test_edit_handlers.py
index d17e3c3c7e..d070cee3a3 100644
--- a/wagtail/admin/tests/test_edit_handlers.py
+++ b/wagtail/admin/tests/test_edit_handlers.py
@@ -428,7 +428,8 @@ class TestTabbedInterface(WagtailTestUtils, TestCase):
                     permission="tests.other_custom_see_panel_setting",
                     heading="Other Custom Setting",
                 ),
-            ]
+            ],
+            attrs={"data-controller": "my-tabbed-interface"},
         ).bind_to_model(EventPage)
 
     def test_get_form_class(self):
@@ -471,6 +472,9 @@ class TestTabbedInterface(WagtailTestUtils, TestCase):
         # result should contain rendered content from descendants
         self.assertIn("Abergavenny sheepdog trials</textarea>", result)
 
+        # result should contain the data-controller attribute as defined by attrs
+        self.assertIn('data-controller="my-tabbed-interface"', result)
+
         # this result should not include fields that are not covered by the panel definition
         self.assertNotIn("signup_link", result)
 
@@ -619,6 +623,7 @@ class TestObjectList(TestCase):
             ],
             heading="Event details",
             classname="shiny",
+            attrs={"data-controller": "my-object-list"},
         ).bind_to_model(EventPage)
 
     def test_get_form_class(self):
@@ -647,6 +652,9 @@ class TestObjectList(TestCase):
         # result should contain ObjectList furniture
         self.assertIn('<div class="w-panel__header">', result)
 
+        # result should contain the specified attrs
+        self.assertIn('data-controller="my-object-list"', result)
+
         # result should contain labels for children
         self.assertIn(
             '<label for="id_date_from" id="id_date_from-label">',
@@ -810,6 +818,9 @@ class TestFieldPanel(TestCase):
                 # The input should have the expected value
                 self.assertIn(f'value="{expected_input_value}"', result)
 
+                # check that data-field-wrapper is added by default via attrs.
+                self.assertIn("data-field-wrapper", result)
+
                 # help text should rendered
                 self.assertIn("Not required if event is on a single day", result)
 
@@ -1206,7 +1217,10 @@ class TestInlinePanel(WagtailTestUtils, TestCase):
         speaker_object_list = ObjectList(
             [
                 InlinePanel(
-                    "speakers", label="Speakers", classname="classname-for-speakers"
+                    "speakers",
+                    label="Speakers",
+                    classname="classname-for-speakers",
+                    attrs={"data-controller": "test"},
                 )
             ]
         ).bind_to_model(EventPage)
@@ -1268,6 +1282,12 @@ class TestInlinePanel(WagtailTestUtils, TestCase):
         # rendered panel must include the JS initializer
         self.assertIn("var panel = new InlinePanel({", result)
 
+        # rendered panel must have data-contentpath-disabled attribute by default
+        self.assertIn("data-contentpath-disabled", result)
+
+        # check that attr option renders the data-controller attribute
+        self.assertIn('data-controller="test"', result)
+
     def test_render_with_panel_overrides(self):
         """
         Check that inline panel renders the panels listed in the InlinePanel definition
diff --git a/wagtail/snippets/templates/wagtailsnippets/snippets/tables/title_cell.html b/wagtail/snippets/templates/wagtailsnippets/snippets/tables/title_cell.html
index 70bb5658e2..07906a54ce 100644
--- a/wagtail/snippets/templates/wagtailsnippets/snippets/tables/title_cell.html
+++ b/wagtail/snippets/templates/wagtailsnippets/snippets/tables/title_cell.html
@@ -3,7 +3,7 @@
 <td class="{% if column.classname %}{{ column.classname }} {% endif %}title">
     <span class="title-wrapper">
         {% if link_url %}
-            <a {% include "wagtailadmin/tables/attrs.html" with attrs=link_attrs %}>{{ value }}</a>
+            <a {% include "wagtailadmin/shared/attrs.html" with attrs=link_attrs %}>{{ value }}</a>
         {% else %}
             {{ value }}
         {% endif %}