12.54. DD 54: Dynamic Forms¶
12.54.1. Summary¶
This document outlines the design of forms defined in the backend in a JSON file which will be rendered by a client for asking information to a person.
12.54.2. Motivation¶
Currently, creating a new form for a web app involves coding a new page with HTML, CSS, and JS. Exchange AML requires multiple forms, and different instances may have distinct forms based on jurisdiction. Being able to define forms in a JSON file that a client software (not just web SPA) could use to ask the information helps to change it without requiring a new upgrade of the client app.
12.54.3. Requirements¶
A form consist of a layout, a set of fields and metadata required to recognice which form configuration was used to produce the saved value.
12.54.3.1. Layout requirements¶
editable by system admin: System admins should be able to create new forms or edit current one shipped with the source.
accesibility: Forms should meet accessibility level AA.
responsive: Forms should be responsive and function on all devices.
metadata: Generated form information should contain enough data to handle multiple form versions.
12.54.3.2. Fields requirements¶
validations: Each field may require custom validation
custom data type: A field may consist of a list, string, number, or a complex composite structure.
12.54.3.3. Metadata requirements¶
identification: the form configuration instance should have an unique non reusable id.
label: the form should have a name recognizable for the user
version: the same form, with the same id, could be updated. This will increase the version number. A newer form should support older forms.
12.54.4. Proposed Solutions¶
The propose solution defines the structure of a form, the fields and additional type form-configuration which links a form with a set of fields.
12.54.4.1. Form metadata¶
This is the root object of the configuration.
type FormMetadata = {
label: string;
id: string;
version: number;
config: FormConfiguration;
};
12.54.4.2. Form configuration¶
Defies a basic structure and the set of fields the form is going to have.
The FormConfiguration
is an enumerated type which list can be extended in the
future.
type FormConfiguration = DoubleColumnForm;
type DoubleColumnForm = {
type: "double-column";
design: DoubleColumnFormSection[];
}
type DoubleColumnFormSection = {
title: string;
description?: string;
fields: UIFormElementConfig[];
};
12.54.4.3. Form fields¶
A form can have two type of element: decorative/informative or input.
An example of a decorative element is a grouping element which make all the fields inside the group look into the same section. This element will not allow the user to enter information and won’t produce any value in the resulting JSON.
An example of an input field is a text field which allows the user to enter text.
This element should have an id
which will point into the location in which the
value will be stored in the resulting JSON. Note that two field in the same form
with the same id
will result in undefined behavior.
The UIFormElementConfig
is an enumerated type with all type of fields supported
type UIFormElementConfig =
| UIFormElementGroup
| UIFormElementCaption
| UIFormFieldAbsoluteTime
| UIFormFieldAmount
| UIFormFieldArray
| UIFormFieldChoiseHorizontal
| UIFormFieldChoiseStacked
| UIFormFieldFile
| UIFormFieldInteger
| UIFormFieldSelectMultiple
| UIFormFieldSelectOne
| UIFormFieldText
| UIFormFieldTextArea
| UIFormFieldToggle;
All form elements should extend from UIFieldElementDescription
which defines a base
configuration to show a field.
type UIFieldElementDescription = {
/* label if the field, visible for the user */
label: string;
/* long text to be shown on user demand */
tooltip?: string;
/* short text to be shown close to the field, usually below and dimmer*/
help?: string;
/* name of the field, useful for a11y */
name: string;
/* if the field should be initially hidden */
hidden?: boolean;
};
That will be enough for a decorative form element (like group element or
a text element) but if it defines an input field then it should extend
from UIFormFieldBaseConfig
which add more information to the previously
defined UIFieldElementDescription
.
type UIFormFieldBaseConfig = UIFieldElementDescription & {
/* example to be shown inside the field */
placeholder?: string;
/* show a mark as required */
required?: boolean;
/* readonly and dim */
disabled?: boolean;
/* conversion id to convert the string into the value type
the id should be known to the ui impl
*/
converterId?: string;
/* property id of the form */
id: UIHandlerId;
};
/**
* string which defined a json path
*
*/
type UIHandlerId = string
The id
property defines the location in which this information is going
to be saved in the JSON result. Formally formally, it should be a dot-selector
dot-selector = "." dot-member-name
dot-member-name = name-first *name-char
name-first = ALPHA / "_"
name-char = DIGIT / name-first
DIGIT = %x30-39 ; 0-9
ALPHA = %x41-5A / %x61-7A ; A-Z / a-z
All the input fields will create a string value located where the id
points, unless a convertedId
is specified. The convertedId
is a reference
to a converter that the client software implements. For example, an input field
with convertedId = "Taler.Amount"
will transform the value the user
entered into a AmountString with the currency in the configuration.
12.54.4.4. Description of supported fields¶
All of this fields defines an UI handler which help the user to input
the value with as handy as possible. The type of the field doesn’t define
the type of the value in the resulting JSON, that’s defined by the converterId
.
12.54.4.4.1. Decorative elements¶
To show some additional text
type UIFormElementCaption = { type: "caption" } & UIFieldElementDescription;
To group fields in the UI and maybe show a collapsable handler.
type UIFormElementGroup = {
type: "group";
fields: UIFormElementConfig[];
} & UIFieldElementDescription;
12.54.4.4.1.1. Example¶
{
"label": "Example form",
"id": "example",
"version": 1,
"config": {
"type": "double-column",
"design": [
{
"title": "Decorative elements",
"fields": [
{
"type": "caption",
"name": "cap",
"label": "This is a caption"
},
{
"type": "group",
"name": "group",
"label": "The first name and last name are in a group",
"fields": [
{
"type": "text",
"name": "firstName",
"id": ".person.name",
"label": "First name"
},
{
"type": "text",
"name": "lastName",
"id": ".person.lastName",
"label": "Last name"
}
]
}
]
}
]
}
}
12.54.4.4.2. Time input¶
This may be rendered as a calendar
type UIFormFieldAbsoluteTime = {
type: "absoluteTimeText";
max?: TalerProtocolTimestamp;
min?: TalerProtocolTimestamp;
pattern: string;
} & UIFormFieldBaseConfig;
{
"label": "Example form",
"id": "example",
"version": 1,
"config": {
"type": "double-column",
"design": [
{
"title": "Time inputs",
"fields": [
{
"type": "absoluteTime",
"name": "thedate",
"id": ".birthdate",
"converterId": "Taler.AbsoluteTime",
"help": "the day you born",
"pattern":"dd/MM/yyyy",
"label": "Birthdate"
}
]
}
]
}
}
12.54.4.4.3. Amount input¶
Money input.
type UIFormFieldAmount = {
type: "amount";
max?: Integer;
min?: Integer;
currency: string;
} & UIFormFieldBaseConfig;
{
"label": "Example form",
"id": "example",
"version": 1,
"config": {
"type": "double-column",
"design": [
{
"title": "Amount inputs",
"fields": [
{
"type": "amount",
"name": "thedate",
"id": ".amount",
"converterId": "Taler.Amount",
"help": "how much do you have?",
"currency":"EUR",
"label": "Amount"
}
]
}
]
}
}
12.54.4.4.4. List input¶
This input allows to enter more than element in the same field, and the
resulting JSON will have a json list. The UI should show the elements
already present in the list, and for that it will use labelFieldId
.
type UIFormFieldArray = {
type: "array";
// id of the field shown when the array is collapsed
labelFieldId: UIHandlerId;
fields: UIFormElementConfig[];
} & UIFormFieldBaseConfig;
{
"label": "Example form",
"id": "example",
"version": 1,
"config": {
"type": "double-column",
"design": [
{
"title": "Amount inputs",
"fields": [
{
"type": "array",
"name": "people",
"id": ".people",
"help": "who is coming to the party?",
"labelFieldId": ".name",
"fields": [{
"type": "text",
"name": "firstName",
"id": ".name",
"label": "First name"
},
{
"type": "text",
"name": "lastName",
"id": ".lastName",
"label": "Last name"
}],
}
]
}
]
}
}
12.54.4.4.5. Choice input¶
To be used when the user need to choose on predefined values
interface SelectUiChoice {
label: string;
description?: string;
value: string;
}
A set of buttons next to each other
type UIFormFieldChoiseHorizontal = {
type: "choiceHorizontal";
choices: Array<SelectUiChoice>;
} & UIFormFieldBaseConfig;
A set of buttons next on top of each other
type UIFormFieldChoiseStacked = {
type: "choiceStacked";
choices: Array<SelectUiChoice>;
} & UIFormFieldBaseConfig;
A drop down list to select one of the elements
type UIFormFieldSelectOne = {
type: "selectOne";
choices: Array<SelectUiChoice>;
} & UIFormFieldBaseConfig;
A drop down list to select multiple of the element, which will produce a list of values in the resulting JSON.
type UIFormFieldSelectMultiple = {
type: "selectMultiple";
max?: Integer;
min?: Integer;
unique?: boolean;
choices: Array<SelectUiChoice>;
} & UIFormFieldBaseConfig;
{
"label": "Example form",
"id": "example",
"version": 1,
"config": {
"type": "double-column",
"design": [
{
"title": "Choice inputs",
"fields": [
{
"type": "choiceHorizontal",
"name": "food",
"label": "Food",
"id": ".food",
"choices": [
{
"value": "meat",
"label": "Meat"
},
{
"value": "sushi",
"label": "Sushi"
},
{
"value": "taco",
"label": "Taco"
},
{
"value": "salad",
"label": "Salad"
}
]
}
]
}
]
}
}
12.54.4.4.6. File input¶
type UIFormFieldFile = {
type: "file";
maxBytes?: Integer;
minBytes?: Integer;
// comma-separated list of one or more file types
// https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/accept#unique_file_type_specifiers
accept?: string;
} & UIFormFieldBaseConfig;
{
"label": "Example form",
"id": "example",
"version": 1,
"config": {
"type": "double-column",
"design": [
{
"title": "File inputs",
"fields": [
{
"type": "file",
"name": "photo",
"id": ".photo",
"label": "Photo",
"accept": "*.png"
}
]
}
]
}
}
12.54.4.4.7. Number input¶
type UIFormFieldInteger = {
type: "integer";
max?: Integer;
min?: Integer;
} & UIFormFieldBaseConfig;
12.54.4.4.8. Text input¶
A simple line of text
type UIFormFieldText = { type: "text" } & UIFormFieldBaseConfig;
A bigger multi-line of text
type UIFormFieldTextArea = { type: "textArea" } & UIFormFieldBaseConfig;
12.54.4.4.9. Boolean input¶
type UIFormFieldToggle = { type: "toggle" } & UIFormFieldBaseConfig;
{
"label": "Example form",
"id": "example",
"version": 1,
"config": {
"type": "double-column",
"design": [
{
"title": "Boolean inputs",
"fields": [
{
"type": "toggle",
"name": "the_question",
"id": ".the_question",
"label": "Yes or no?"
}
]
}
]
}
}