gnunet-svn
[Top][All Lists]
Advanced

[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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]