Repository: wagtail/wagtail

Issue:
Panel heading attribute ignored when nested in MultiFieldPanel
### Issue Summary

When a panel (`FieldPanel`, `StreamFieldPanel`, etc.) is nested in a `MultiFieldPanel`, the `heading` attribute of the nested panel is ignored.


### Steps to Reproduce

1. (for example) Start a new project with `wagtail start myproject`
2. Edit `home/models.py` as follows...

    ```python
    from django.db import models

    from wagtail.admin import edit_handlers
    from wagtail.core.models import Page


    class HomePage(Page):
        intro = models.CharField(max_length=255, blank=True, null=True)

        content_panels = Page.content_panels +   [
            edit_handlers.MultiFieldPanel(
                [
                    edit_handlers.FieldPanel('intro', heading="Introduction")
                ],
                heading="Multi"
            )
        ]

   ```
     
3. Make and apply migrations

    ```sh
    ./manage.py makemigrations
    ./manage.py migrate
    ```

![image](https://user-images.githubusercontent.com/24797493/109749506-e56aa780-7b7e-11eb-9f57-41a4409b3f47.png)


### Any other relevant information. For example, why do you consider this a bug and what did you expect to happen instead?

* I have confirmed that this issue can be reproduced as described on a fresh Wagtail project: (**yes**/ no)

The expected behavior would be to have the heading "Introduction", but it shows "Intro" instead. 
Setting the `verbose_name` on the model field can be used to change the displayed value. 


### Technical details

* Python version: Run `python --version`: Python 3.9.1
* Django version: Look in your requirements.txt, or run `pip show django | grep Version`: Version: 3.1.7
* Wagtail version: Look at the bottom of the Settings menu in the Wagtail admin, or run `pip show wagtail | grep Version:`: Version: 2.12.2




Discussion:

Resolving patch:
diff --git a/wagtail/admin/edit_handlers.py b/wagtail/admin/edit_handlers.py
--- a/wagtail/admin/edit_handlers.py
+++ b/wagtail/admin/edit_handlers.py
@@ -560,7 +560,10 @@ def db_field(self):
 
     def on_form_bound(self):
         self.bound_field = self.form[self.field_name]
-        self.heading = self.heading or self.bound_field.label
+        if self.heading:
+            self.bound_field.label = self.heading
+        else:
+            self.heading = self.bound_field.label
         self.help_text = self.bound_field.help_text
 
     def __repr__(self):


Test patch:
diff --git a/wagtail/admin/tests/test_edit_handlers.py b/wagtail/admin/tests/test_edit_handlers.py
--- a/wagtail/admin/tests/test_edit_handlers.py
+++ b/wagtail/admin/tests/test_edit_handlers.py
@@ -457,6 +457,7 @@ def test_override_heading(self):
         end_date_panel_with_overridden_heading = (FieldPanel('date_to', classname='full-width', heading="New heading")
                                                   .bind_to(model=EventPage, request=self.request, form=self.EventPageForm()))
         self.assertEqual(end_date_panel_with_overridden_heading.heading, "New heading")
+        self.assertEqual(end_date_panel_with_overridden_heading.bound_field.label, "New heading")
 
     def test_render_as_object(self):
         form = self.EventPageForm(
@@ -557,7 +558,7 @@ def setUp(self):
                                date_from=date(2014, 7, 20), date_to=date(2014, 7, 21))
 
         self.dates_panel = FieldRowPanel([
-            FieldPanel('date_from', classname='col4'),
+            FieldPanel('date_from', classname='col4', heading="Start"),
             FieldPanel('date_to', classname='coltwo'),
         ]).bind_to(model=EventPage, request=self.request)
 
@@ -597,6 +598,9 @@ def test_render_as_field(self):
         self.assertIn('<label for="id_date_to">End date:</label>', result)
         self.assertNotIn('<legend>End date</legend>', result)
 
+        # check that label is overridden with the 'heading' argument
+        self.assertIn('<label for="id_date_from">Start:</label>', result)
+
         # check that help text is included
         self.assertIn('Not required if event is on a single day', result)