Automated form builder
Forms on canonical.com and ubuntu.com have been restructured to use a common template.
The form generator uses three files: form-fields.html
, form-fieldset.html
and forms-data.json
.
The form builder reads form data in forms-data.json
. This is where all the forms data live. It includes all necessary data that needs to be rendered on the page. All forms will be rendered through one template with params passed from that data storage file.
In order to add a new form, we will have to import the form template on the relevant files and update the relevant information on forms-data.json
. This documentation will guide users on how to use the form builder.
Importing form builder
ubuntu.com
Static forms
Include template on the form page
{% include "/shared/forms/form-template.html" %}
Dynamic forms (modals)
We have to wrap the template in a contact-form-container
and also include parameters in the container. The parameters are: data-form-location, data-form-id, data-lp-id, data-return-url, data-lp-url. This data is read from the formData variable on forms-data.json
.
CTA buttons and links need to include class="js-invoke-modal" id="contact-modal"
to allow modal to be invoked on click.
<script defer src="{{ versioned_static('js/modals.js') }}"></script>
<div class="u-hide"
id="contact-form-container"
data-form-location="/shared/forms/form-template"
data-form-id="{{ formData.formId }}"
data-lp-id=""
data-return-url="{{ formData.returnUrl }}"
data-lp-url="https://ubuntu.com/aws/contact-us">
{% include "/shared/forms/form-template.html" %}
</div>
canonical.com
Static forms
Include the template in the necessary place on the page. No wrappers needed.
{% include "/shared/forms/form-template.html" %}
Dynamic forms
Import the script for modals
<script defer src="{{ versioned_static('js/modals.js') }}"></script>
CTA buttons and links need to include class="js-invoke-modal"
so that the modal can be invoked on click.
The modalId on forms-data.json should also be applied on the CTA as id="<modalId>
.
Using the template
In order to add new forms or update existing one, we can update forms-data.json
with the new form data.
General forms-data.json
structure
This is where form data lives. The form generator parses data from this file and builds it with the template. For this example, we are using an example use case for the form on https://canonical.com/data/postgresql#get-in-touch.
"forms": {
"/data/postgresql": { // Page url
"templatePath": "/data/postgresql/index.html",
"childrenPaths": ["/data/postgres/child1", "/data/postgres/child2"],
"isModal": true, // False by default for static forms
"modalId": "data-relational-dbs-modal", // Only for modals
"formData": {
"title": "Talk to our relational databases' experts",
"introText": "Intro text", // optional
"formId": "1266",
"returnUrl": "/data/postgresql#contact-form-success",
"product": ""
},
"fieldsets": [{
"title": "Tell us more about your use case",
"id": "comments",
"isRequired": false,
"noCommentsFromLead": false,
"fields": [
{
"type": "long-text",
"id": "comments",
"placeholder": "Anything you'd like to communicate about your needs or interests?"
"isRequired": false
}
],
}],
...
}
}
}
templatePath
(required): Path of page where the form liveschildrenPaths
(optional): Paths of children pages (within the same bubble) that are using the shared form. The originaltemplatePath
must be applied on the index pageisModal
(optional): Whether the form is static or dynamic/modal, false by defaultmodalId
(optional): Only required for modals. Set it tocontact-modal
for u.com and an appropriate ID (e.g data-relational-dbs-modal for c.com). This is because modals are invoked differently on both sides. For c.com, make sure that thearia-controls
for CTA is set to the same ID as the modalID.formData
(required): General form datatitle
(required): Title of formintroText
(optional): Form descriptionformId
(required): Marketo ID for the formreturnUrl
(required): Return URL on form submission, attach#contact-form-success
to use the standardized form success bannerproduct
: Which product this form is for, can be left as an empty string if unknown.
fieldsets
(required): Can be a single fieldset with one field, or a group fieldset with a list of fields. If it is a group, we can initiate an array for the group of fields. More example use cases are shown below.title
(required): Title of fieldsetid
(required): Fieldset idisRequired
(optional): Whether the fieldset is required or not, False by defaultnoCommentsFromLead
(optional): Whether to append this fieldset toComments_from_lead__c
parameter in the payload. This is usually set as True for contact fields, False by defaultinputType
(optional): radio/checkbox/checkbox-visibility. Only needed for fieldsets with JS functionality
Fieldsets
Fieldset is a group/section of data in the form. They can have a single field or a group of fields. If there is a list of fields, we have to initiate an array to store the different fields. We have JS code for fieldsets with special behaviours such as radio and toggling checkbox visibility, this can be applied using inputType
parameter.
inputType
values and explanation
radio
: Must include for radio fieldset to removename
attribute before submitting to payload. Attachesjs-remove-radio-names
class to fieldsetcheckbox
: Include for required checkbox fields. Attachesjs-required-checkbox
class to fieldsetcheckbox-visibility
: Include for checkbox fields where “Other” options are greyed out when non-other options are selected and vice versa. Attachesjs-toggle-checkbox-visibility
class to fieldset
Fieldsets can have different fields
"fieldsets": [
...
"fields": [
{
...
},
{
...
}
]
]
Setting required fields
To set a field as required, we can add isRequired
to the field and set to True. If there are multiple field types such as for radio or checkbox, then we also have to change the parent fieldset
, add inputType
and set to the appropriate type (i.e radio
, checkbox
). This adds JS functionality to the fieldset and fields to ensure that it is selected before submission.
Fields
The general list of field types used for the forms are:
- Text field
- Country
- Mobile
- Long text
- Dropdown select
- Radio
- Checkbox
Text
"fieldsets": [
...
"fields": [
{
"type": "text",
"id" : "text-id",
"label": "Example text label",
"isRequired": true
}
]
]
type
(required): Type of fieldid
(required): Input field IDlabel
(optional): Text labelisRequired
(optional): Whether the field is required, false by default.
Country
"fieldsets": [
...
"fields": [
{
"type": "country",
"id" : "",
"label": "",
"isRequired": true
}
]
]
type
(required): Type of fieldid
(required): Input field ID, set as emptylabel
(optional): Text labelisRequired
(optional): Whether the field is required, false by default.
"fieldsets": [
...
"fields": [
{
"type": "email",
"id" : "email",
"label": "Email address",
"isRequired": true
}
]
]
type
(required): Type of fieldid
(required): Input field IDlabel
(optional): Field labelisRequired
(optional): Whether the field is required, false by default.
Mobile
"fieldsets": [
...
"fields": [
{
"type": "tel",
"id" : "phone",
"label": "Mobile/cell phone number"
},
]
]
type
(required): Type of fieldid
(required): Input field IDlabel
(optional): Field label
Long text
"fieldsets": [
...
"fields": [
{
"type": "long-text",
"id" : "long-text-id",
"placeholder": "Example placeholder text on textbox"
},
]
]
type
(required): Type of fieldid
(required): Input field IDplaceholder
(optional): Placeholder text shown on textbox
Dropdown select
Instantiate an array of fields for the different dropdown selection
"fieldsets": [
...
"fields": [
{
"type": "select",
"id" : "select-id",
"label": "Example dropdown",
"options": [
{
"value": "dropdown-value-1",
"label": "Example value 1"
},
{
"value": "dropdown-value-2",
"label": "Example value 2"
},
]
},
]
]
type
(required): Type of fieldid
(required): Input field IDlabel
(required): Input field labeloptions
(required): Dropdown optionsvalue
(required): Value of dropdown selectionlabel
(required): Label of dropdown selection
Radio
Instantiate an array of fields for the different radio selection.
Add "inputType": "radio"
to fieldsets to concatenate value to payload string.
"fieldsets": [
...
"inputType": "radio",
"fields": [
{
"type": "radio",
"id": "radio-id-1",
"value": "value-1",
"label": "Example first selection"
},
{
"type": "radio",
"id": "radio-id-2",
"value": "value-2",
"label": "Example second selection"
},
{
"type": "radio",
"id": "radio-id-3",
"value": "value-3",
"label": "Example third selection"
},
]
]
type
(required): Type of fieldid
(required): Input field IDvalue
(required): Input field valuelabel
(required): Input field label
Checkbox
There are 2 types of checkboxes, normal required/non-required checkboxes and visibility checkboxes.
Normal checkbox fields
"fieldsets": [
...
"fields": [
{
"type": "checkbox",
"id": "checkbox-id-1",
"value": "value-1",
"label": "Example first selection"
},
{
"type": "checkbox",
"id": "checkbox-id-2",
"value": "checkbox-2",
"label": "Example second selection"
}
]
]
type
(required): Type of fieldid
(required): Input field IDvalue
(required): Input field valuelabel
(required): Input field label
Visibility checkbox fields
This means that there are a few divided fields, and one Other
group. If a checkbox within Other
is selected, then the fields in other field groups will be greyed out and vice versa. This is achieved by attaching inputType
as checkbox-visibility
to fieldset.
"fieldsets": [
...
"inputType": "checkbox-visibility",
"fields": [
{
“fieldTitle”: “Example section 1”,
"options": [
{
"type": "checkbox",
"id": "1-1",
"value": "1-1",
"label": "1-1"
},
{
"type": "checkbox",
"id": "1-2",
"value": "1-2",
"label": "1-2"
},
],
},
{
“fieldTitle”: “Example section 2”,
"options": [
{
"type": "checkbox",
"id": "2-1",
"value": "2-1",
"label": "2-1"
},
{
"type": "checkbox",
"id": "2-2",
"value": "2-2",
"label": "2-2"
},
],
},
]
]
fieldTitle
(required): Title of the checkbox field sectionoptions
(required): List of checkbox selection in an arraytype
(required): Input typeid
(required): Input field IDvalue
(required): Input field valuelabel
(required): Input field label
Last updated 2 days ago.