Torchbox Forms
A Torchbox-flavoured template pack for django-crispy-forms, adapted from crispy-forms-gds.
Out of the box, forms created with tbxforms will look like the
GOV.UK Design System, though many
variables can be customised.
Requirements
- python
>=3.8.1,<4.0 - Django
>=2.2 - django-crispy-forms
>=1.13.0,<2.0 - wagtail
>=2.15if usingWagtailBaseForm - sass
>=1.33.0if building the sass yourself
Installation
You must install both the Python package and the NPM package.
Install the Python package
Install using pip:
pip install tbxforms
Add django-crispy-forms and tbxforms to your installed apps:
INSTALLED_APPS = [
# ...
'crispy_forms', # django-crispy-forms
'tbxforms',
]
Now add the following settings to tell django-crispy-forms to use tbxforms:
CRISPY_ALLOWED_TEMPLATE_PACKS = ["tbx"]
CRISPY_TEMPLATE_PACK = "tbx"
Install the NPM package
Install using NPM:
npm install tbxforms
Note: This package uses the Element.closest, NodeList.forEach, and
Array.includes APIs. You will need to install and configure polyfills for
legacy browser support if you need to.
Instantiate your forms:
import TbxForms from 'tbxforms';
document.addEventListener('DOMContentLoaded', () => {
for (const form of document.querySelectorAll(TbxForms.selector())) {
new TbxForms(form);
}
});
Import the styles into your project...
...Either as CSS without any customisations:
@use 'node_modules/tbxforms/dist/style.css';
...Or as Sass to customise variables:
@use 'node_modules/tbxforms/tbxforms.scss' with (
$tbxforms-error-colour: #f00,
$tbxforms-text-colour: #000,
);
Alternatively, variables can be defined in a centralised variables SCSS such as tbxforms/static/sass/abstracts/_variables.scss.
Add button styles
tbxforms provides out-of-the-box GOV.UK Design System styles for everything
except buttons, as styles for these probably exist within your project.
You will need to write button styles for the following classes:
.tbxforms-button.tbxforms-button.tbxforms-button--primary.tbxforms-button.tbxforms-button--secondary.tbxforms-button.tbxforms-button--warning
Usage
tbxforms can be used for coded Django forms and editor-controlled Wagtail forms.
Django forms
All forms must inherit the TbxFormsMixin mixin, as well as specifying a Django base form class (e.g. forms.Form or forms.ModelForm)
from django import forms
from tbxforms.forms import TbxFormsMixin
class ExampleForm(TbxFormsMixin, forms.Form):
...
class ExampleModelForm(TbxFormsMixin, forms.ModelForm):
...
Wagtail forms
Create or update a Wagtail form
Wagtail forms must inherit from TbxFormsMixin and WagtailBaseForm.
from wagtail.contrib.forms.forms import BaseForm as WagtailBaseForm
from tbxforms.forms import TbxFormsMixin
class ExampleWagtailForm(TbxFormsMixin, WagtailBaseForm):
...
Instruct a Wagtail Page model to use your form
In your form definitions (e.g. forms.py):
from tbxforms.forms import BaseWagtailFormBuilder as TbxFormsBaseWagtailFormBuilder
from path.to.your.forms import ExampleWagtailForm
class WagtailFormBuilder(TbxFormsBaseWagtailFormBuilder):
def get_form_class(self):
return type(str("WagtailForm"), (ExampleWagtailForm,), self.formfields)
And in your form page models (e.g. models.py):
from path.to.your.forms import WagtailFormBuilder
class ExampleFormPage(...):
...
form_builder = WagtailFormBuilder
...
Render a form
Just like Django Crispy Forms, you need to pass your form object to the
{% crispy ... %} template tag, e.g.:
{% load crispy_forms_tags %}
<html>
<body>
{% crispy your_sexy_form %}
</body>
</html>
FormHelpers
A FormHelper allows you to alter the rendering behaviour of forms.
Every form that inherits from TbxFormsMixin (i.e. every form within tbxforms)
will have a FormHelper with the following default attributes:
html5_required = Truelabel_size = Size.MEDIUMlegend_size = Size.MEDIUMform_error_title = _("There is a problem with your submission")- Plus everything from django-crispy-forms' default attributes.
These can be changed during instantiation or on the go - examples below.
Add a submit button
Submit buttons are not automatically added to forms. To add one, you can extend
the form.helper.layout (examples below).
Extend during instantiation:
from django import forms
from tbxforms.forms import TbxFormsMixin
from tbxforms.layout import Button
class YourSexyForm(TbxFormsMixin, forms.Form):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper.layout.extend([
Button.primary(
name="submit",
type="submit",
value="Submit",
)
])
Or afterwards:
from tbxforms.layout import Button
form = YourSexyForm()
form.helper.layout.extend([
Button.primary(
name="submit",
type="submit",
value="Submit",
)
])
Conditionally-required fields
tbxforms can show/hide parts of the layout depending on a given value. For
example, you could show (and require) an email address field only when the user
chooses to sign up to a newsletter (examples below).
You can apply this logic to field, div, and fieldset elements.
Note: any field names included within the
conditional_fields_to_show_as_required() method will appear on the frontend
as required, though will technically be required=False.
Field example:
from django import forms
from django.core.exceptions import ValidationError
from tbxforms.choices import Choice
from tbxforms.forms import TbxFormsMixin
from tbxforms.layout import Field, Layout
class ExampleForm(TbxFormsMixin, forms.Form):
NEWSLETTER_CHOICES = (
Choice("yes", "Yes please", hint="Receive occasional email newsletters."),
Choice("no", "No thanks"),
)
newsletter_signup = forms.ChoiceField(
choices=NEWSLETTER_CHOICES
)
email = forms.EmailField(
widget=forms.EmailInput(required=False)
)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper.layout = Layout(
# Add our newsletter sign-up field.
Field.text("newsletter_signup"),
# Add our email field and define the conditional logic.
Field.text(
"email",
data_conditional={
"field_name": "newsletter_signup", # Field to inspect.
"values": ["yes"], # Value(s) to cause this field to show.
},
),
)
@staticmethod
def conditional_fields_to_show_as_required() -> [str]:
# Non-required fields that should show as required to the user.
return [
"email",
]
def clean(self):
cleaned_data = super().clean()
newsletter_signup = cleaned_data.get("newsletter_signup")
email = cleaned_data.get("email")
# Fields included within `conditional_fields_to_show_as_required()` will
# be shown as required but not enforced - i.e. they will not have the
# HTML5 `required` attribute set.
# Thus we need to write our own check to enforce the value exists.
if newsletter_signup == "yes" and not email:
raise ValidationError(
{
"email": "This field is required.",
}
)
# The tbxforms JS will attempt to clear any redundant data upon submission,
# though it is recommended to also handle this in your clean() method.
elif newsletter_signup == "no" and email:
del cleaned_data['email']
return cleaned_data
Container example:
When you have multiple fields/elements that you want to show/hide together, you
can use the exact same data_conditional definition as above but on a div or
fieldset element, e.g.:
from tbxforms.layout import HTML, Div, Field, Layout
Layout(
Div(
HTML("<p>Some relevant text.</p>"),
Field.text("some_other_field"),
Field.text("email"),
data_conditional={
"field_name": "newsletter_signup",
"values": ["yes"],
},
),
)
Customising behaviour
Change the default label and legend classes
Possible values for the label_size and legend_size:
SMALLMEDIUM(default)LARGEEXTRA_LARGE
Further reading
- Download the PyPI package
- Download the NPM package
- Learn more about Django Crispy Forms
- Learn more about Crispy Forms GDS
- Learn more about GOV.UK Design System
Metadata
Release files for tbxforms 2.0.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tbxforms-2.0.0.tar.gz | 38.6 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tbxforms-2.0.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 91.1 kB
Release files / tbxforms-2.0.0.tar.gz
| Download URL | tbxforms-2.0.0.tar.gz |
|---|---|
| Size | 38.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
dd349d5e4fb2602e98451900b16193d4f605f29b085a4a3bc21ea8256a192032
|
|
BLAKE2b-256 checksum How to use checksums |
e5a171ed8780b387552f2170b53535914d8abe1c138e9767af64ae45158bc807
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.7.1 CPython/3.11.6 Darwin/23.2.0
|
Release files / tbxforms-2.0.0-py3-none-any.whl
| Download URL | tbxforms-2.0.0-py3-none-any.whl |
|---|---|
| Size | 52.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
f2c3f870df0684b455fe444e25410df2bd03c8db0625c1ead1e8472380162762
|
|
BLAKE2b-256 checksum How to use checksums |
0524cb653d6ec8fd6e1d82a8215fc862fb9016c31c021b095ae5b38885dbd683
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/1.7.1 CPython/3.11.6 Darwin/23.2.0
|