summaryrefslogtreecommitdiff
path: root/design-documents/054-dynamic-form.rst
diff options
context:
space:
mode:
Diffstat (limited to 'design-documents/054-dynamic-form.rst')
-rw-r--r--design-documents/054-dynamic-form.rst201
1 files changed, 201 insertions, 0 deletions
diff --git a/design-documents/054-dynamic-form.rst b/design-documents/054-dynamic-form.rst
new file mode 100644
index 00000000..d93b3684
--- /dev/null
+++ b/design-documents/054-dynamic-form.rst
@@ -0,0 +1,201 @@
+DD 54: Dynamic Web Form
+#######################
+
+Summary
+=======
+
+This document outlines the approach for implementing a dynamic web form feature.
+
+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.
+
+
+Requirements
+============
+
+A form consist of a layout and a set of fields.
+
+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.
+
+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.
+
+
+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).
+
+
+.. 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>;
+ }
+
+
+``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
+
+Form state have the same shape of ``T`` but every field type is ``FieldUIOptions``.
+
+Fields type can be:
+ * strings
+ * numbers
+ * boolean
+ * arrays
+ * object
+
+The field type ``AmountJson`` and ``AbsoluteTime`` are opaque since field is used as a whole.
+
+The form can be instanciated using
+
+.. code-block:: typescript
+
+ import { FormProvider } from "@gnu-taler/web-util/browser";
+
+
+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;
+ }
+
+
+``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
+
+A set of common form field exist in ``@gnu-taler/web-util``:
+
+ * InputAbsoluteTime
+ * InputAmount
+ * InputArray
+ * InputFile
+ * InputText
+ * InputToggle
+
+and should be used inside a ``Form`` context.
+
+.. code-block:: none
+
+ function MyFormComponent():VNode {
+ return <FormProvider>
+ <InputAmount name="amount" />
+ <InputText name="subject" />
+ <button type="submit"> Confirm </button>
+ </FormProvider>
+ }
+
+
+Example
+--------
+
+Consider a form shape represented by the TypeScript type:
+
+.. code-block:: typescript
+
+ type TheFormType = {
+ name: string,
+ age: number,
+ savings: AmountJson,
+ nextBirthday: AbsoluteTime,
+ pets: string[],
+ addres: {
+ street: string,
+ city: string,
+ }
+ }
+
+An example instance of this form could be:
+
+.. code-block:: typescript
+
+ const theFormValue: TheFormType = {
+ name: "Sebastian",
+ age: 15,
+ pets: ["dog","cat"],
+ address: {
+ street: "long",
+ city: "big",
+ }
+ }
+
+
+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,
+ },
+ },
+ }
+ }
+
+
+
+
+Q / A
+=====