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 lives
  • childrenPaths (optional): Paths of children pages (within the same bubble) that are using the shared form. The original templatePath must be applied on the index page
  • isModal (optional): Whether the form is static or dynamic/modal, false by default
  • modalId (optional): Only required for modals. Set it to contact-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 the aria-controls for CTA is set to the same ID as the modalID.
  • formData (required): General form data
    • title (required): Title of form
    • introText (optional): Form description
    • formId (required): Marketo ID for the form
    • returnUrl (required): Return URL on form submission, attach #contact-form-success to use the standardized form success banner
    • product: 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 fieldset
    • id (required): Fieldset id
    • isRequired (optional): Whether the fieldset is required or not, False by default
    • noCommentsFromLead (optional): Whether to append this fieldset to Comments_from_lead__c parameter in the payload. This is usually set as True for contact fields, False by default
    • inputType (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 remove name attribute before submitting to payload. Attaches js-remove-radio-names class to fieldset
  • checkbox: Include for required checkbox fields. Attaches js-required-checkbox class to fieldset
  • checkbox-visibility: Include for checkbox fields where “Other” options are greyed out when non-other options are selected and vice versa. Attaches js-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
  • Email
  • 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 field
  • id (required): Input field ID
  • label (optional): Text label
  • isRequired (optional): Whether the field is required, false by default.

Country

"fieldsets": [
    ...
    "fields": [
        {
            "type": "country",
            "id" : "",
            "label": "",
            "isRequired": true
        }
    ]
]
  • type (required): Type of field
  • id (required): Input field ID, set as empty
  • label (optional): Text label
  • isRequired (optional): Whether the field is required, false by default.

Email

"fieldsets": [
    ...
    "fields": [
        {
            "type": "email",
            "id" : "email",
            "label": "Email address",
            "isRequired": true
        }
    ]
]
  • type (required): Type of field
  • id (required): Input field ID
  • label (optional): Field label
  • isRequired (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 field
  • id (required): Input field ID
  • label (optional): Field label

Long text

"fieldsets": [
    ...
    "fields": [
        {
            "type": "long-text",
            "id" : "long-text-id",
            "placeholder": "Example placeholder text on textbox"
        },
    ]
]
  • type (required): Type of field
  • id (required): Input field ID
  • placeholder (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 field
  • id (required): Input field ID
  • label (required): Input field label
  • options (required): Dropdown options
    • value (required): Value of dropdown selection
    • label (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 field
  • id (required): Input field ID
  • value (required): Input field value
  • label (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 field
  • id (required): Input field ID
  • value (required): Input field value
  • label (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 section
  • options (required): List of checkbox selection in an array
    • type (required): Input type
    • id (required): Input field ID
    • value (required): Input field value
    • label (required): Input field label

Last updated 2 days ago.