diff options
Diffstat (limited to 'design-documents/054-dynamic-form.rst')
-rw-r--r-- | design-documents/054-dynamic-form.rst | 201 |
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 +===== |