Repository: wagtail/wagtail

Issue:
Support a `caption` with the Tables UI compnoent
### Is your proposal related to a problem?

While reviewing https://github.com/wagtail/wagtail/pull/11250 I found that the current `ui.tables.Table` component has no simple way to add a table `caption`. We kept the existing behaviour of using aria-description but a caption would be more suitable.

### Describe the solution you'd like


* `wagtail/admin/ui/tables/__init__.py` should have a default property of `caption` as an empty string.
* There should be a new method `get_caption` that returns the property, but this can be overridden if needed.
* This `caption` value should be available on the table instance passed into context.
* The base table template `wagtail/admin/templates/wagtailadmin/tables/table.html` should render the caption if it's truthy with the class `w-sr-only` by default.
* Maybe it's worth having a way to override this class at the `get_caption` or `@property` method and a way to override the class, this may make sense as a standalone template partial. `wagtail/admin/templates/wagtailadmin/tables/caption.html`
* Unit tests will be a must - they can be added to `wagtail/admin/tests/ui/test_tables.py`
* By default we should add some styling, even if it's for screen readers only, to make the caption show at the bottom https://developer.mozilla.org/en-US/docs/Web/CSS/caption-side -> this can probably be added to client/scss/generic/_normalize.scss
* You can validate the global styling in the styleguide also as we have a table caption here wagtail/contrib/styleguide/templates/wagtailstyleguide/base.html

Once the above is done, either on the same PR or a different PR, replace the use of `aria-description` with the new `caption` for when the page listing is in sortable mode.

* See wagtail/admin/views/pages/listing.py
* ...`"Focus on the drag button`
* It should only set the caption if `show_ordering_column` is True.


### Describe alternatives you've considered

* Do nothing or keep using the aria-description even though this is not the most appropriate way to inform the user in screen readers.

### Additional context

* For work done in the ordering of page listing see #11250
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/caption

### Working on this

Anyone can contribute to this. View our [contributing guidelines](https://docs.wagtail.org/en/latest/contributing/index.html), add a comment to the issue once you’re ready to start.

* Would be good to have a basic understanding of the HTML caption element and Django templates + Django unit tests.



Discussion:
I can pick it up?
All yours @salty-ivy if you are keen. Feel free to do in two parts if that's easier (first, add support for caption, second use the caption in the page listing ordering mode).

Or one PR will also be fine but try to split to two commits at least.

Resolving patch:
diff --git a/wagtail/admin/ui/tables/__init__.py b/wagtail/admin/ui/tables/__init__.py
--- a/wagtail/admin/ui/tables/__init__.py
+++ b/wagtail/admin/ui/tables/__init__.py
@@ -413,8 +413,10 @@ def __init__(
         ordering=None,
         classname=None,
         attrs=None,
+        caption=None,
     ):
         self.columns = OrderedDict([(column.name, column) for column in columns])
+        self.caption = caption
         self.data = data
         if template_name:
             self.template_name = template_name
@@ -424,6 +426,9 @@ def __init__(
             self.classname = classname
         self.base_attrs = attrs or {}
 
+    def get_caption(self):
+        return self.caption
+
     def get_context_data(self, parent_context):
         context = super().get_context_data(parent_context)
         context["table"] = self
diff --git a/wagtail/admin/views/pages/listing.py b/wagtail/admin/views/pages/listing.py
--- a/wagtail/admin/views/pages/listing.py
+++ b/wagtail/admin/views/pages/listing.py
@@ -347,10 +347,10 @@ def get_table_kwargs(self):
         kwargs["actions_next_url"] = self.index_url
 
         if self.show_ordering_column:
+            kwargs["caption"] = _(
+                "Focus on the drag button and press up or down arrows to move the item, then press enter to submit the change."
+            )
             kwargs["attrs"] = {
-                "aria-description": _(
-                    "Focus on the drag button and press up or down arrows to move the item, then press enter to submit the change."
-                ),
                 "data-controller": "w-orderable",
                 "data-w-orderable-active-class": "w-orderable--active",
                 "data-w-orderable-chosen-class": "w-orderable__item--active",


Test patch:
diff --git a/wagtail/admin/tests/ui/test_tables.py b/wagtail/admin/tests/ui/test_tables.py
--- a/wagtail/admin/tests/ui/test_tables.py
+++ b/wagtail/admin/tests/ui/test_tables.py
@@ -47,6 +47,40 @@ def test_table_render(self):
         """,
         )
 
+    def test_table_render_with_caption(self):
+        data = [
+            {"first_name": "Paul", "last_name": "Simon"},
+            {"first_name": "Art", "last_name": "Garfunkel"},
+        ]
+
+        caption = "Test table"
+
+        table = Table(
+            columns=[
+                Column("first_name"),
+                Column("last_name"),
+            ],
+            data=data,
+            caption=caption,
+        )
+
+        html = self.render_component(table)
+        self.assertHTMLEqual(
+            html,
+            """
+            <table class="listing">
+                <caption class="w-sr-only">Test table</caption>
+                <thead>
+                    <tr><th>First name</th><th>Last name</th></tr>
+                </thead>
+                <tbody>
+                    <tr><td>Paul</td><td>Simon</td></tr>
+                    <tr><td>Art</td><td>Garfunkel</td></tr>
+                </tbody>
+            </table>
+        """,
+        )
+
     def test_table_render_with_width(self):
         data = [
             {"first_name": "Paul", "last_name": "Simon"},