diff --git a/docs/extending/forms.md b/docs/extending/forms.md index e55ca34408..24e0926bf7 100644 --- a/docs/extending/forms.md +++ b/docs/extending/forms.md @@ -74,3 +74,5 @@ A view performs the following steps to render a model form through the panels me ** Construct a subclass of the base form class, with the options dict forming the attributes of the inner `Meta` class. * An instance of the form class is created as per a normal Django form view. * The view then calls `get_bound_panel` on the top-level panel, passing `instance`, `form` and `request` as keyword arguments. This returns a `BoundPanel` object, which follows [the template component API](/extending/template_components). Finally, the `BoundPanel` object (and its media definition) is rendered onto the template. + +New panel types can be defined by subclassing `wagtail.admin.panels.Panel` - see [](/reference/panel_api). diff --git a/docs/reference/index.rst b/docs/reference/index.rst index f579456faa..8b53283755 100644 --- a/docs/reference/index.rst +++ b/docs/reference/index.rst @@ -15,3 +15,4 @@ Reference settings project_template jinja2 + panel_api diff --git a/docs/reference/pages/panels.rst b/docs/reference/pages/panels.rst index 3d685a5662..8a0d0a2dbc 100644 --- a/docs/reference/pages/panels.rst +++ b/docs/reference/pages/panels.rst @@ -11,6 +11,7 @@ Django's field types are automatically recognised and provided with an appropria Here are some Wagtail-specific types that you might include as fields in your models. .. module:: wagtail.admin.panels + :noindex: FieldPanel ~~~~~~~~~~ diff --git a/docs/reference/panel_api.md b/docs/reference/panel_api.md new file mode 100644 index 0000000000..83d8f9c0f2 --- /dev/null +++ b/docs/reference/panel_api.md @@ -0,0 +1,28 @@ +# Panel API + +```{eval-rst} +.. module:: wagtail.admin.panels + +Panel +----- + +.. autoclass:: Panel + + .. automethod:: bind_to_model + .. automethod:: on_model_bound + .. automethod:: clone + .. automethod:: clone_kwargs + .. automethod:: get_form_options + .. automethod:: get_form_class + .. automethod:: get_bound_panel + +BoundPanel +---------- + +.. autoclass:: wagtail.admin.panels.Panel.BoundPanel + + In addition to the standard template component functionality (see :ref:`creating_template_components`), this provides the following methods: + + .. automethod:: id_for_label + .. automethod:: is_shown +``` diff --git a/wagtail/admin/panels.py b/wagtail/admin/panels.py index fa97d4ee95..462809fbd4 100644 --- a/wagtail/admin/panels.py +++ b/wagtail/admin/panels.py @@ -117,9 +117,16 @@ class Panel: self.model = None def clone(self): + """ + Create a clone of this panel definition. By default, constructs a new instance, passing the + keyword arguments returned by ``clone_kwargs``. + """ return self.__class__(**self.clone_kwargs()) def clone_kwargs(self): + """ + Return a dictionary of keyword arguments that can be used to create a clone of this panel definition. + """ return { "heading": self.heading, "classname": self.classname, @@ -131,7 +138,7 @@ class Panel: """ Return a dictionary of attributes such as 'fields', 'formsets' and 'widgets' which should be incorporated into the form class definition to generate a form - that this EditHandler can use. + that this panel can use. This will only be called after binding to a model (i.e. self.model is available). """ options = {} @@ -202,6 +209,9 @@ class Panel: ) def bind_to_model(self, model): + """ + Create a clone of this panel definition with a ``model`` attribute pointing to the linked model class. + """ new = self.clone() new.model = model new.on_model_bound() @@ -217,6 +227,10 @@ class Panel: return self.get_bound_panel(instance=instance, request=request, form=form) def get_bound_panel(self, instance=None, request=None, form=None): + """ + Return a ``BoundPanel`` instance that can be rendered onto the template as a component. By default, this creates an instance + of the panel class's inner ``BoundPanel`` class, which must inherit from ``Panel.BoundPanel``. + """ if self.model is None: raise ImproperlyConfigured( "%s.bind_to_model(model) must be called before get_bound_panel" @@ -234,6 +248,10 @@ class Panel: ) def on_model_bound(self): + """ + Called after the panel has been associated with a model class and the ``self.model`` attribute is available; + panels can override this method to perform additional initialisation related to the model. + """ pass def __repr__(self): @@ -267,6 +285,10 @@ class Panel: return "" class BoundPanel(Component): + """ + A template component for a panel that has been associated with a model instance, form, and request. + """ + def __init__(self, panel, instance, request, form): self.panel = panel self.instance = instance @@ -287,9 +309,15 @@ class Panel: return self.panel.field_type() def id_for_label(self): + """ + Returns an HTML ID to be used as the target for any label referencing this panel. + """ return self.panel.id_for_label() def is_shown(self): + """ + Whether this panel should be rendered; if false, it is skipped in the template output. + """ return True def render_as_object(self):