Wagtailstreamforms Fields Side-by-Side

Viewed 227

Wagtailstreamforms is a great package to build forms very easily, it allows for fields to be stacked, but how do I choose fields to render side-by-side? Below is a picture of my desired outcome.

Side-by-side fields

Notice how the name and email fields and project timespan and budget fields are in the same row and are side-by-side. I want to add functionality where I can do this with any two types of fields, where I simply select the fields in the wagtailstreamforms form builder settings page.

How do I go about doing this?


Edit: Some extra details.

Right now wagtailstreamforms can only build forms with fields stacked one on top of the other. There is no functionality in the form builder page to create a grid system with form fields in multiple columns.

I want to add functionality where I can create a new field via the register decorator in wagtailstreamforms_fields.py, where the field will have two options, left and right, and each option will be a list of the available fields (excluding this one) so the user can create two fields to place side-by-side in two columns.

I have started to implement this by creating the field with the left and right options.

# wagtailstreamforms_fields.py

from django import forms
from wagtail.core import blocks
from wagtail.core.fields import StreamField
from wagtailstreamforms import streamfield
from wagtailstreamforms.fields import BaseField, register

form_field_list_left = streamfield.FormFieldStreamBlock('form_two_columns_left')
form_field_list_right = streamfield.FormFieldStreamBlock('form_two_columns_left')

@register('two_columns')
class TwoColumns(BaseField):
    field_class = forms.MultiValueField
    widget = forms.MultiWidget
    icon = 'list-ul'
    label = 'Two Columns'

    def get_form_block(self):
        return blocks.StructBlock([
            ('left', form_field_list_left),
            ('right', form_field_list_right),
        ], icon=self.icon, label=self.label)

Field with left and right options

The issue now is how do I access the left and right options in the templates?

If the two-column field works, I will be expanding this by creating a few other fields for three-columns and maybe even four-columns.


Another edit: a different way to achieve this grid system.

As I'm using Bootstrap CSS for my project, I can specify an extra option called width for each form, this will be a number from 1 to 12, 12 being full width.

I can then access this option in templates and append it to the CSS class of the container div, col-{{ field.width }}

This method allows more flexibility to the grid system's size, although it isn't as intuitive as having fields with preset column widths. I will be pursuing this approach in another StackOverflow post: Adding additional information into each form field

1 Answers

Custom StreamField Block editing interfaces can be achieved in multiple ways, the simplest is to leverage the form_classname meta attribute and add css to suit.

Step 1 - Define your StructBlock

  • Here is a simple version of the screenshot you shared, the fields do not matter that much for this example.
  • They key change is form_classname = "struct-block column-layout", note that the 'struct-block' must be included and the 'column-layout' is what we will link the custom styles to.
  • Note: You can also add the form_classname via a keyword argument for any StructBlock usage, you do not have to define your own class.

my-app/blocks.py

from wagtail.core.blocks import CharBlock, StructBlock, TextBlock

# ...

class ProjectBlock(StructBlock):
    name = TextBlock()
    email = CharBlock(blank=True, required=False, label="e.g. joe@email.co")
    company = TextBlock()
    timespan = CharBlock(blank=True, required=False, label="e.g. 7 decades")
    budget = CharBlock(blank=True, required=False, label="$$$")

    class Meta:
        form_classname = "struct-block column-layout"
        icon = "fa-file"

Step 2 - Add CSS (styles)

  • These styles use flexbox to show the fields as separate columns, you could also use grid if you wanted.
  • The styles are scoped to devices above 970px, this is so that on smaller devices the rows will 'snap' back to being stacked.
  • The key item to note here is the usage of .column-layout as defined in our StructBlock.
  • There are specific styles for the company field, which is the field we want to show as full width.

static/css/admin.css

@media (min-width: 970px) {
  .struct-block.column-layout {
    display: flex;
    flex-wrap: wrap;
    margin-right: -1.5em;
  }

  .struct-block.column-layout > .field {
    min-width: calc(50% - 1.5em);
    margin-right: 1.5em;
  }

  .struct-block.column-layout > .field + .field {
    padding-top: 0;
    padding-bottom: 0.5em;
  }

  .struct-block.column-layout > .field[data-contentpath="company"] {
    min-width: calc(100% - 1.5em);
  }
}

Step 3 - Add CSS file to admin

  • Ensure that the CSS file is loaded into the admin interface by leveraging the Wagtail hooks system, the code for hooks must be added to a wagtail_hooks.py file.
  • Remember to add 'django.contrib.staticfiles', to INSTALLED_APPS and STATICFILES_DIRS = [os.path.join(PROJECT_DIR, 'static') ] in your settings.py

my-app/wagtail_hooks.py

from django.templatetags.static import static
from django.utils.html import format_html

from wagtail.core import hooks

# ...

@hooks.register("insert_editor_css")
def editor_css():
    return format_html('<link rel="stylesheet" href="{}">', static("css/admin.css"))

Final Layout

  • The final layout should look like below.

multi column StructBlock layout example

Documentation

  • This is a basic approach but should be enough to achieve what is needed, this could also be abstracted by creating a class that extends StructBlock and overrides some methods and default values, the documentation below has details about how to completely customise the form field rendering.
  • Customising the StreamField editing interfact
  • Inserting editor CSS
Related