[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-docs] branch master updated: fix #8256
From: |
gnunet |
Subject: |
[taler-docs] branch master updated: fix #8256 |
Date: |
Thu, 09 May 2024 23:02:58 +0200 |
This is an automated email from the git hooks/post-receive script.
sebasjm pushed a commit to branch master
in repository docs.
The following commit(s) were added to refs/heads/master by this push:
new 8cff4a00 fix #8256
8cff4a00 is described below
commit 8cff4a00e5bac4397b58b65ceb57b01c815122ef
Author: Sebastian <sebasjm@gmail.com>
AuthorDate: Thu May 9 18:02:53 2024 -0300
fix #8256
---
design-documents/054-dynamic-form.rst | 472 ++++++++++++++++++++++++++--------
1 file changed, 360 insertions(+), 112 deletions(-)
diff --git a/design-documents/054-dynamic-form.rst
b/design-documents/054-dynamic-form.rst
index d93b3684..a4108851 100644
--- a/design-documents/054-dynamic-form.rst
+++ b/design-documents/054-dynamic-form.rst
@@ -1,10 +1,13 @@
-DD 54: Dynamic Web Form
+DD 54: Dynamic Form
#######################
Summary
=======
-This document outlines the approach for implementing a dynamic web form
feature.
+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.
+
Motivation
==========
@@ -12,12 +15,16 @@ 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.
Requirements
============
-A form consist of a layout and a set of fields.
+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.
Layout requirements
-------------------
@@ -41,159 +48,400 @@ Fields requirements
complex composite structure.
+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.
+
Proposed Solutions
==================
-Forms are initialized using a flexible structure defined by the
-TypeScript interface FormType<T>. This interface comprises properties
-such as value (current form data), initial (initial form data for resetting),
-readOnly (flag to disable input), onUpdate (callback on form data update),
-and computeFormState (function to derive the form state based on current data).
+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.
+
+Form metadata
+-------------
+
+This is the root object of the configuration.
+
+.. code-block:: typescript
+
+ type FormMetadata = {
+ label: string;
+ id: string;
+ version: number;
+ config: FormConfiguration;
+ };
+
+
+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.
.. code-block:: typescript
- interface FormType<T extends object> {
- value: Partial<T>;
- initial?: Partial<T>;
- readOnly?: boolean;
- onUpdate?: (v: Partial<T>) => void;
- computeFormState?: (v: Partial<T>) => FormState<T>;
+ type FormConfiguration = DoubleColumnForm;
+
+ type DoubleColumnForm = {
+ type: "double-column";
+ design: DoubleColumnFormSection[];
}
+ type DoubleColumnFormSection = {
+ title: string;
+ description?: string;
+ fields: UIFormElementConfig[];
+ };
+
+
+Form fields
+-----------
-``T``: is the type of the result object
-``value``: is a reference to the current value of the result
-``initial``: data for resetting
-``readOnly``: when true, fields won't allow input
-``onUpdate``: notification of the result update
-``computeFormState``: compute a new state of the form based on the current
value
+A form can have two type of element: decorative/informative or input.
-Form state have the same shape of ``T`` but every field type is
``FieldUIOptions``.
+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.
-Fields type can be:
- * strings
- * numbers
- * boolean
- * arrays
- * object
+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 field type ``AmountJson`` and ``AbsoluteTime`` are opaque since field is
used as a whole.
+The ``UIFormElementConfig`` is an enumerated type with all type of fields
supported
-The form can be instanciated using
+.. code-block:: typescript
+
+ 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.
.. code-block:: typescript
- import { FormProvider } from "@gnu-taler/web-util/browser";
+ 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``.
-Then the field component can access all the properties by the
``useField(name)`` hook,
-which will return
.. code-block:: typescript
- interface InputFieldHandler<Type> {
- value: Type;
- onChange: (s: Type) => void;
- state: FieldUIOptions;
- isDirty: boolean;
- }
+ type UIFormFieldBaseConfig = UIFieldElementDescription & {
+ /* example to be shown inside the field */
+ placeholder?: string;
+ /* show a mark as required */
+ required?: boolean;
-``value``: the current value of the field
-``onChange``: a function to call anytime the user want to change the value
-``state``: the state of the field (hidden, error, etc..)
-``isDirty``: if the user already tried to change the value
+ /* readonly and dim */
+ disabled?: boolean;
-A set of common form field exist in ``@gnu-taler/web-util``:
+ /* conversion id to convert the string into the value type
+ the id should be known to the ui impl
+ */
+ converterId?: string;
- * InputAbsoluteTime
- * InputAmount
- * InputArray
- * InputFile
- * InputText
- * InputToggle
+ /* property id of the form */
+ id: UIHandlerId;
+ };
-and should be used inside a ``Form`` context.
+ /**
+ * string which defined a json path
+ *
+ */
+ type UIHandlerId = string
-.. code-block:: none
- function MyFormComponent():VNode {
- return <FormProvider>
- <InputAmount name="amount" />
- <InputText name="subject" />
- <button type="submit"> Confirm </button>
- </FormProvider>
- }
+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``
+.. code-block:: ini
-Example
---------
+ dot-selector = "." dot-member-name
+ dot-member-name = name-first *name-char
+ name-first = ALPHA / "_"
+ name-char = DIGIT / name-first
-Consider a form shape represented by the TypeScript type:
+ 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.
+
+
+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``.
+
+Decorative elements
+``````````````````
+
+To show some additional text
.. code-block:: typescript
- type TheFormType = {
- name: string,
- age: number,
- savings: AmountJson,
- nextBirthday: AbsoluteTime,
- pets: string[],
- addres: {
- street: string,
- city: string,
- }
- }
+ type UIFormElementCaption = { type: "caption" } & UIFieldElementDescription;
-An example instance of this form could be:
+To group fields in the UI and maybe show a collapsable handler.
.. code-block:: typescript
- const theFormValue: TheFormType = {
- name: "Sebastian",
- age: 15,
- pets: ["dog","cat"],
- address: {
- street: "long",
- city: "big",
+ type UIFormElementGroup = {
+ type: "group";
+ fields: UIFormElementConfig[];
+ } & UIFieldElementDescription;
+
+Example
+'''''''
+
+.. code-block:: json
+
+ {
+ "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"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ }
}
- }
+.. image:: ../images/dynamic-forms.decorative-elements.png
+ :width: 800
-For such a form, a valid state can be computed using a function like
-``computeFormStateBasedOnFormValues``, returning an object indicating
-the state of each field, including properties such as ``hidden``,
-``disabled``, and ``required``.
-
-
-.. code-block:: javascript
-
- function computeFormStateBasedOnFormValues(formValues): {
- //returning fixed state as an example
- //the return state will be commonly be computed from the values of the form
- return {
- age: {
- hidden: true,
- },
- pets: {
- disabled: true,
- elements: [{
- disabled: false,
- }],
- },
- address: {
- street: {
- required: true,
- error: "the street name was not found",
- },
- city: {
- required: true,
- },
- },
- }
+
+Time input
+``````````
+
+This may be rendered as a calendar
+
+.. code-block:: typescript
+
+ type UIFormFieldAbsoluteTime = {
+ type: "absoluteTime";
+ max?: TalerProtocolTimestamp;
+ min?: TalerProtocolTimestamp;
+ pattern: string;
+ } & UIFormFieldBaseConfig;
+
+Amount input
+````````````
+
+Money input.
+
+.. code-block:: typescript
+
+ type UIFormFieldAmount = {
+ type: "amount";
+ max?: Integer;
+ min?: Integer;
+ currency: string;
+ } & UIFormFieldBaseConfig;
+
+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``.
+
+.. code-block:: typescript
+
+ type UIFormFieldArray = {
+ type: "array";
+ // id of the field shown when the array is collapsed
+ labelFieldId: UIHandlerId;
+ fields: UIFormElementConfig[];
+ } & UIFormFieldBaseConfig;
+
+Choice input
+````````````
+
+To be used when the user need to choose on predefined values
+
+.. code-block:: typescript
+
+ interface SelectUiChoice {
+ label: string;
+ description?: string;
+ value: string;
}
+A set of buttons next to each other
+
+.. code-block:: typescript
+
+ type UIFormFieldChoiseHorizontal = {
+ type: "choiceHorizontal";
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+
+A set of buttons next on top of each other
+
+.. code-block:: typescript
+
+ type UIFormFieldChoiseStacked = {
+ type: "choiceStacked";
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+A drop down list to select one of the elements
+
+.. code-block:: typescript
+
+ 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.
+
+.. code-block:: typescript
+
+ type UIFormFieldSelectMultiple = {
+ type: "selectMultiple";
+ max?: Integer;
+ min?: Integer;
+ unique?: boolean;
+ choices: Array<SelectUiChoice>;
+ } & UIFormFieldBaseConfig;
+
+
+File input
+``````````
+
+.. code-block:: typescript
+
+ 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;
+
+Number input
+````````````
+
+
+
+.. code-block:: typescript
+
+ type UIFormFieldInteger = {
+ type: "integer";
+ max?: Integer;
+ min?: Integer;
+ } & UIFormFieldBaseConfig;
+
+Text input
+````````````
+
+A simple line of text
+
+.. code-block:: typescript
+
+ type UIFormFieldText = { type: "text" } & UIFormFieldBaseConfig;
+
+A bigger multi-line of text
+
+.. code-block:: typescript
+
+ type UIFormFieldTextArea = { type: "textArea" } & UIFormFieldBaseConfig;
+
+Boolean input
+`````````````
+
+.. code-block:: typescript
+
+ type UIFormFieldToggle = { type: "toggle" } & UIFormFieldBaseConfig;
+
+
+Examples
+========
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [taler-docs] branch master updated: fix #8256,
gnunet <=