Browse Source

Add a (non-functional) 'create' form to generic chooser modal

Matt Westcott 2 years ago
parent
commit
4dd3483617

+ 1 - 1
client/src/includes/chooserModal.js

@@ -169,7 +169,7 @@ class ChooserModalOnloadHandlerFactory {
     this.chosenResponseName = opts?.chosenResponseName || 'chosen';
     this.searchInputDelay = opts?.searchInputDelay || 200;
     this.creationFormSelector =
-      opts?.creationFormSelector || 'form[data-chooser-modal-create]';
+      opts?.creationFormSelector || 'form[data-chooser-modal-creation-form]';
     this.creationFormTabSelector =
       opts?.creationFormTabSelector || '#tab-create';
     this.creationFormFileFieldSelector = opts?.creationFormFileFieldSelector;

+ 1 - 0
docs/extending/generic_views.md

@@ -60,6 +60,7 @@ class PersonChooserViewSet(ChooserViewSet):
     choose_one_text = "Choose a person"
     choose_another_text = "Choose another person"
     edit_item_text = "Edit this person"
+    form_fields = ["first_name", "last_name"]  # fields to show in the "Create" tab
 ```
 
 Again this can be registered with the `register_admin_viewset` hook:

+ 7 - 0
docs/reference/viewsets.md

@@ -57,4 +57,11 @@ Viewsets are Wagtail's mechanism for defining a group of related admin views wit
    .. autoattribute:: chosen_view_class
    .. autoattribute:: widget_class
    .. autoattribute:: register_widget
+   .. autoattribute:: creation_form_class
+   .. autoattribute:: form_fields
+   .. autoattribute:: exclude_form_fields
+   .. autoattribute:: create_action_label
+   .. autoattribute:: create_action_clicked_label
+   .. autoattribute:: creation_tab_label
+   .. autoattribute:: search_tab_label
 ```

+ 41 - 4
wagtail/admin/templates/wagtailadmin/generic/chooser/chooser.html

@@ -1,7 +1,17 @@
-{% load i18n %}
+{% load i18n wagtailadmin_tags %}
 {% include "wagtailadmin/shared/header.html" with title=page_title subtitle=page_subtitle merged=1 icon=header_icon %}
 
 <div class="w-tabs" data-tabs data-tabs-disable-url>
+    {% if creation_form %}
+        <div class="w-tabs__wrapper w-overflow-hidden">
+            {# Using nice-padding and full width class until the modal header is restyled #}
+            <div role="tablist" class="w-tabs__list w-w-full nice-padding">
+                {% include 'wagtailadmin/shared/tabs/tab_nav_link.html' with tab_id='search' title=search_tab_label %}
+                {% include 'wagtailadmin/shared/tabs/tab_nav_link.html' with tab_id='create' title=creation_tab_label %}
+            </div>
+        </div>
+    {% endif %}
+
     <div class="tab-content nice-padding">
         <section
             id="tab-search"
@@ -12,9 +22,7 @@
 
             {% if filter_form.fields %}
                 <form data-chooser-modal-search class="search-bar" action="{{ results_url }}" method="GET" novalidate>
-                    {% if filter_form.hidden_fields %}
-                        {% for field in filter_form.hidden_fields %}{{ field }}{% endfor %}
-                    {% endif %}
+                    {% for field in filter_form.hidden_fields %}{{ field }}{% endfor %}
 
                     {% if filter_form.visible_fields %}
                         <ul class="fields">
@@ -31,5 +39,34 @@
                 {% include view.results_template_name %}
             </div>
         </section>
+
+        {% if creation_form %}
+            <section
+                id="tab-create"
+                class="w-tabs__panel"
+                role="tabpanel"
+                hidden
+                aria-labelledby="tab-label-create"
+            >
+                {% include "wagtailadmin/shared/non_field_errors.html" with form=creation_form %}
+                <form data-chooser-modal-creation-form action="" method="POST" {% if creation_form.is_multipart %}enctype="multipart/form-data"{% endif %} novalidate>
+                    {% csrf_token %}
+                    {% for field in creation_form.hidden_fields %}{{ field }}{% endfor %}
+
+                    <ul class="fields">
+                        {% for field in creation_form.visible_fields %}
+                            {% include "wagtailadmin/shared/field_as_li.html" with field=field %}
+                        {% endfor %}
+                        <li>
+                            {% if create_action_clicked_label %}
+                                <button type="submit" class="button button-longrunning" data-clicked-text="{{ create_action_clicked_label }}">{% icon name="spinner" %}<em>{{ create_action_label }}</em></button>
+                            {% else %}
+                                <button type="submit" class="button">{{ create_action_label }}</button>
+                            {% endif %}
+                        </li>
+                    </ul>
+                </form>
+            </section>
+        {% endif %}
     </div>
 </div>

+ 32 - 1
wagtail/admin/views/generic/chooser.py

@@ -2,6 +2,7 @@ from django import forms
 from django.contrib.admin.utils import unquote
 from django.core.exceptions import ObjectDoesNotExist
 from django.core.paginator import Paginator
+from django.forms.models import modelform_factory
 from django.http import Http404
 from django.template.response import TemplateResponse
 from django.urls import reverse
@@ -134,9 +135,39 @@ class BaseChooseView(ModalPageFurnitureMixin, ContextMixin, View):
 
 
 class ChooseViewMixin:
+    creation_form_class = None
+    form_fields = None
+    exclude_form_fields = None
+    search_tab_label = _("Search")
+    creation_tab_label = None
+    create_action_label = _("Create")
+    create_action_clicked_label = None
+
+    def get_creation_form_class(self):
+        if self.creation_form_class:
+            return self.creation_form_class
+        elif self.form_fields is not None or self.exclude_form_fields is not None:
+            return modelform_factory(
+                self.model, fields=self.form_fields, exclude=self.exclude_form_fields
+            )
+
     def get_context_data(self, **kwargs):
         context = super().get_context_data(**kwargs)
-        context["filter_form"] = self.filter_form
+        context.update(
+            {
+                "filter_form": self.filter_form,
+                "create_action_label": self.create_action_label,
+                "create_action_clicked_label": self.create_action_clicked_label,
+                "search_tab_label": self.search_tab_label,
+                "creation_tab_label": self.creation_tab_label
+                or self.create_action_label,
+            }
+        )
+
+        creation_form_class = self.get_creation_form_class()
+        if creation_form_class:
+            context["creation_form"] = creation_form_class()
+
         return context
 
     # Return the choose view as a ModalWorkflow response

+ 24 - 0
wagtail/admin/viewsets/chooser.py

@@ -40,6 +40,23 @@ class ChooserViewSet(ViewSet):
     #: Defaults to True; if False, the chooser widget will not automatically be registered for use in admin forms.
     register_widget = True
 
+    #: Form class to use for the form in the "Create" tab of the modal.
+    creation_form_class = None
+
+    #: List of model fields that should be included in the creation form, if creation_form_class is not specified.
+    form_fields = None
+
+    #: List of model fields that should be excluded from the creation form, if creation_form_class.
+    #: If none of ``creation_form_class``, ``form_fields`` or ``exclude_form_fields`` are specified, the "Create" tab will be omitted.
+    exclude_form_fields = None
+
+    search_tab_label = _("Search")  #: Label for the 'search' tab in the chooser modal
+    create_action_label = _(
+        "Create"
+    )  #: Label for the submit button on the 'create' form
+    create_action_clicked_label = None  #: Alternative text to display on the submit button after it has been clicked
+    creation_tab_label = None  #: Label for the 'create' tab in the chooser modal (defaults to the same as create_action_label)
+
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
         if self.page_title is None:
@@ -53,6 +70,13 @@ class ChooserViewSet(ViewSet):
             results_url_name=self.get_url_name("choose_results"),
             icon=self.icon,
             page_title=self.page_title,
+            creation_form_class=self.creation_form_class,
+            form_fields=self.form_fields,
+            exclude_form_fields=self.exclude_form_fields,
+            search_tab_label=self.search_tab_label,
+            creation_tab_label=self.creation_tab_label,
+            create_action_label=self.create_action_label,
+            create_action_clicked_label=self.create_action_clicked_label,
         )
 
     @property