From 5328ce4aac6fab133594256a428266b48df19365 Mon Sep 17 00:00:00 2001 From: Paul Abumov Date: Mon, 8 Jan 2024 19:39:19 -0500 Subject: [PATCH] [Form Builder] Added description of form builder config --- examples/react_form_composer/data/data.json | 87 +++------- mephisto/generators/form_composer/README.md | 162 ++++++++++++++++++ packages/form-composer/README.md | 72 -------- .../src/FormComposer/FormComposer.js | 33 ++-- .../src/FormComposer/fields/SelectField.js | 2 +- 5 files changed, 208 insertions(+), 148 deletions(-) diff --git a/examples/react_form_composer/data/data.json b/examples/react_form_composer/data/data.json index 4dc923bbf..a8941469c 100644 --- a/examples/react_form_composer/data/data.json +++ b/examples/react_form_composer/data/data.json @@ -1,29 +1,27 @@ [ { "form": { - "name": "Form example", + "title": "Form example", "instruction": "Please answer all questions to the best of your ability as part of our study.", "sections": [ { - "name": "About you", + "name": "section_about", + "title": "About you", "instruction": "Please introduce yourself. We would like to know more about your background, personal information, etc.", "fieldsets": [ { - "name": "Personal information", + "title": "Personal information", "instruction": "", "rows": [ { "fields": [ { - "class": "", "help": "", - "icon": "", "id": "id_name_first", "label": "First name", "name": "name_first", "placeholder": "Type first name", - "style": {}, - "title": "Your first name", + "tooltip": "Your first name", "type": "input", "validators": { "required": true, @@ -33,15 +31,12 @@ "value": "" }, { - "class": "", "help": "Optional", - "icon": "", "id": "id_name_last", "label": "Last name", "name": "name_last", "placeholder": "Type last name", - "style": {}, - "title": "Your last name", + "tooltip": "Your last name", "type": "input", "validators": {"required": true}, "value": "" @@ -52,15 +47,12 @@ { "fields": [ { - "class": "", "help": "We may contact you later for additional information", - "icon": "", "id": "id_email", "label": "Email address for Mephisto", "name": "email", "placeholder": "user@mephisto.ai", - "style": {}, - "title": "Email address for Mephisto", + "tooltip": "Email address for Mephisto", "type": "email", "validators": { "required": true, @@ -73,69 +65,63 @@ ] }, { - "name": "Cultural background", + "title": "Cultural background", "instruction": "Please tell us about your cultural affiliations and values that you use in your daily life.", "rows": [ { "fields": [ { - "class": "", "help": "Select country of your residence", - "icon": "", "id": "id_country", "label": "Country", "multiple": false, "name": "country", "options": [ { - "name": "---", + "label": "---", "value": "" }, { - "name": "United States of America", + "label": "United States of America", "value": "USA" }, { - "name": "Canada", + "label": "Canada", "value": "CAN" } ], "placeholder": "", - "style": {}, - "title": "Country", + "tooltip": "Country", "type": "select", "validators": {"required": true}, "value": "" }, { - "class": "", "help": "Select language spoken in your local community", - "icon": "", "id": "id_language", "label": "Language", "multiple": true, "name": "language", "options": [ { - "name": "English", + "label": "English", "value": "en" }, { - "name": "French", + "label": "French", "value": "fr" }, { - "name": "Spanish", + "label": "Spanish", "value": "es" }, { - "name": "Chinese", + "label": "Chinese", "value": "ch" } ], "placeholder": "", - "style": {}, - "title": "Language", + "tooltip": "Language", "type": "select", "validators": { "required": true, @@ -150,21 +136,18 @@ "help": "This information will help us compile study statistics" }, { - "name": "Additional information", + "title": "Additional information", "instruction": "Optional details about you. You can fill out what you are most comfortable with.", "rows": [ { "fields": [ { - "class": "", "help": "", - "icon": "", "id": "id_bio", "label": "Biography", "name": "bio", "placeholder": "", - "style": {}, - "title": "Your bio in a few apragraphs", + "tooltip": "Your bio in a few apragraphs", "type": "textarea", "validators": {"required": false}, "value": "" @@ -174,9 +157,7 @@ { "fields": [ { - "class": "", "help": "", - "icon": "", "id": "id_skills", "label": "Technical Skills", "name": "skills", @@ -202,8 +183,7 @@ "value": "sql" } ], - "style": {}, - "title": "Technical skills you may possess", + "tooltip": "Technical skills you may possess", "type": "checkbox", "validators": { "required": true, @@ -216,9 +196,7 @@ { "fields": [ { - "class": "", "help": "", - "icon": "", "id": "id_kids", "label": "How many children do you have?", "name": "kids", @@ -244,8 +222,7 @@ "value": ">=3" } ], - "style": {}, - "title": "How many children do you have?", + "tooltip": "How many children do you have?", "type": "radio", "validators": {"required": true} } @@ -254,29 +231,23 @@ { "fields": [ { - "class": "", "help": "", - "icon": "", "id": "id_avatar", "label": "Profile Picture", "name": "avatar", "placeholder": "Select a file", - "style": {}, - "title": "Your profile photo", + "tooltip": "Your profile photo", "type": "file", "validators": {"required": true}, "value": "" }, { - "class": "", "help": "", - "icon": "", "id": "id_resume", "label": "Resume", "name": "resume", "placeholder": "Select a file", - "style": {}, - "title": "Your current resume", + "tooltip": "Your current resume", "type": "file", "validators": {"required": false}, "value": "" @@ -289,25 +260,23 @@ ] }, { - "name": "Second section", + "name": "section_second", + "title": "Second section", "instruction": "Example of another section", "fieldsets" : [ { - "name": "Motivation", + "title": "Motivation", "instruction": "", "rows": [ { "fields": [ { - "class": "", "help": "", - "icon": "", "id": "id_motto", "label": "Personal Motto", "name": "motto", "placeholder": "", - "style": {}, - "title": "Your personal motto", + "tooltip": "Your personal motto", "type": "input", "validators": {"required": true}, "value": "" @@ -322,7 +291,7 @@ ], "submit_button": { "text": "Submit", - "title": "Submit form" + "tooltip": "Submit form" } } } diff --git a/mephisto/generators/form_composer/README.md b/mephisto/generators/form_composer/README.md index 82e7ff538..c7ee5fe4c 100644 --- a/mephisto/generators/form_composer/README.md +++ b/mephisto/generators/form_composer/README.md @@ -3,3 +3,165 @@ This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree. --> + +# Form Composer + +This package provides `FormComposer` widget for React-based front-end development for Mephisto tasks. + +--- + +## How to Run + +To create and launch a Form Composer task, create a configuration that fits your needs, +and then the below commands. + +--- + +#### Installation + +```bash +cd /mephisto/packages/form-composer && npm install --save form-composer +``` + +#### Building +```bash +cd /mephisto/packages/form-composer && npm run build +``` + +--- + +## How to configure + +You will need to provide Form Builder with a JSON configuration of your form fields. +The config file name should be `data.json`, and it should contain a single-item JSON array with one key `form`. + +An example is found in `examples/react_form_composer/data/data.json` file. + +--- + +#### Form config hierarchy levels + +Form UI layout consists of the following layers of UI object hierarchy: + +1. Form +2. Section +3. Fieldset +4. Fields Row +5. Field + +--- + +#### Config level: form + +`form` is a top-level config object with the following attributes: + +- `instruction` - Text describing this form; it is located before all contained sections (String, Optional) +- `title` - Header of the form (String) +- `submit_button` - Button to submit the whole form and thus finish a task (Object) + - `text` - Label shown on the button + - `tooltip` - Browser tooltip shown on mouseover +- `sections` - List of containers into which form content is divided, for convenience; each section has its own validation messages, and is collapsible (Array[Object]) + +--- + +#### Config level: section + +Each item of `sections` list is an object with the following attributes: + +- `instruction` - Text describing this section; it is located before all contained fieldsets (String, Optional) +- `title` - Header of the section (String) +- `name` - Unique string that serves as object reference when using dynamic form config (String) +- `fieldsets` - List of containers into which form fields are grouped by meaning (Array[Object]) + +--- + +#### Config level: fieldset + +Each item of `fieldsets` list is an object with the following attributes: + +- `instruction` - Text describing this fieldset; it is located before all contained field rows (String, Optional) +- `title` - Header of the section (String) +- `rows` - List of horizontal lines into which section's form fields are organized (Array[Object]) + +--- + +#### Config level: row + +Each item of `rows` list is an object with the following attributes: + +- `fields` - List of one or more fields that will be line up into one horizontal line + +--- + +#### Config level: field + +Each item of `fields` list is an object that corresponds to the actual form field displayed in the resulting Task UI page. + +Example of a field config: + +```json +{ + "id": "id_name_first", + "label": "First name", + "name": "name_first", + "placeholder": "Type first name", + "title": "First name of a person", + "type": "input", + "validators": { + "required": true, + "minLength": 2, + "maxLength": 20, + "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"] + // or can use this --> "regexp": "^[a-zA-Z0-9._-]+@mephisto\\.ai$" + }, + "value": "" +} +``` + +###### Attributes - all fields + +The most important attributes are: `label`, `name`, `type`, `validators` + +- `help` - Explanation of the field displayed in small font below the field (String, Optional) +- `id` - Unique HTML id of the field, in case we need to refer to it from custom handlers code (String, Optional) +- `label` - Field name displayed above the field (String) +- `name` - Unique name under which this field's data will be sent to the server (String) +- `placeholder` - Text faintly displayed in the field before user provides a value (String, Optional) +- `tooltip` - Text shown in browser tooltip on mouseover (String, Optional) +- `type` - Type of the field (`input`, `email`, `select`, `textarea`, `checkbox`, `radio`, `file`) (String) +- `validators` - Validators preventing incorrect data from being submitted (Object[: String|Boolean|Number], Optional). Supported key-value pairs for the `validators` object: + - `required`: Ensure field is not left empty (Boolean) + - `minLength`: Ensure minimal number of typed characters or selected choices (Number) + - `maxLength`: Ensure maximum number of typed characters or selected choices (Number) + - `regexp`: Ensure provided value confirms to a Javascript regular expression. It can be: + - (String): a regexp string (e.g. `"^[a-zA-Z0-9._-]+$"`) in which case default matching flags are `igm` are used + - (2-item Array[String, String]): a regexp string followed by matching flags (e.g. `["^[a-zA-Z0-9._-]+$", "ig"]`) +- `value` - Initial value of the field (String, Optional) + + +###### Attributes - select field + +- `multiple` - Support selection of multiple provided options, not just one (Boolean. Default: false) +- `options` - list of available options to select from. Each option is an object with these attributes: + - `label`: displayed text (String) + - `value`: value sent to the server (String|Number|Boolean) + + +###### Attributes - checkbox and radio fields + +- `options` - list of available options to select from. Each option is an object with these attributes: + - `label`: displayed text (String) + - `value`: value sent to the server (String|Number|Boolean) + - `checked`: initial state of selection (Boolean, default: false) + +--- + +## Dynamic form config + +TBD + +--- + +## Custom field handlers + +TBD diff --git a/packages/form-composer/README.md b/packages/form-composer/README.md index e463af78b..82e7ff538 100644 --- a/packages/form-composer/README.md +++ b/packages/form-composer/README.md @@ -3,75 +3,3 @@ This source code is licensed under the MIT license found in the LICENSE file in the root directory of this source tree. --> - -# form-composer - -This package provides widget `FormComposer` for React-based front-end development for Mephisto tasks. - -### Installation - -```bash -cd /mephisto/packages/form-composer && npm install --save form-composer -``` - -### Building -```bash -cd /mephisto/packages/form-composer && npm run build -``` - -### How to configure - -All you need to do is provide Form Builder with a JSON configuration of your form fields. - -An example is found in `examples/react_form_composer/data/data.json` file. - ---- - -### How form is composed - -Form Builder supports several layers of hierarchy: - -1. Section -2. Fieldset -3. Fields Row -4. Field - ---- - -### Validators - -Available validators: - - `required` (boolean) - - `minLength` (integer) - - `maxLength` (integer) - - `regexp` (string | array[string, string]) - -`regexp` params: -1. RedExp string (`"^[a-zA-Z0-9._-]+@mephisto\\.ai$"`). Default flags are `igm` -2. Array with RedExp string and flags (`["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"]`) - -Example: - -```json -{ - "fields": [ - { - "id": "id_name_first", - "label": "First name", - "name": "name_first", - "placeholder": "Type first name", - "title": "First name of a person", - "type": "input", - "validators": { - "required": true, - "minLength": 2, - "maxLength": 20, - "regexp": ["^[a-zA-Z0-9._-]+@mephisto\\.ai$", "ig"] - // or just string "regexp": "^[a-zA-Z0-9._-]+@mephisto\\.ai$" - }, - "value": "" - } - ] -} -``` - diff --git a/packages/form-composer/src/FormComposer/FormComposer.js b/packages/form-composer/src/FormComposer/FormComposer.js index 95035690e..8e7bc54af 100644 --- a/packages/form-composer/src/FormComposer/FormComposer.js +++ b/packages/form-composer/src/FormComposer/FormComposer.js @@ -32,7 +32,7 @@ function FormComposer({ data, onSubmit, finalResults }) { const inReviewState = finalResults !== null; - let formName = data.name; + let formTitle = data.title; let formInstruction = data.instruction; let formSections = data.sections; let formSubmitButton = data.submit_button; @@ -137,15 +137,15 @@ function FormComposer({ data, onSubmit, finalResults }) { noValidate={true} onSubmit={onSubmitForm} > - {(formName || formInstruction) && ( + {(formTitle || formInstruction) && (
- {formName && ( + {formTitle && (

- {formName} + {formTitle}

)} - {formName && formInstruction &&
} + {formTitle && formInstruction &&
} {formInstruction && (

@@ -160,7 +160,7 @@ function FormComposer({ data, onSubmit, finalResults }) { {/* Sections */} {formSections.map(( section, sectionIndex ) => { - let sectionName = section.name; + let sectionTitle = section.title; let sectionInstruction = section.instruction; let fieldsets = section.fieldsets; @@ -169,7 +169,7 @@ function FormComposer({ data, onSubmit, finalResults }) { key={`section-${sectionIndex}`} className={`section container`} > - {(sectionName || sectionInstruction) && ( + {(sectionTitle || sectionInstruction) && ( // Section header is clickable for accordion

{/* Section name on the left side */} - {sectionName && ( + {sectionTitle && (

- {sectionName} + {sectionTitle}

)} @@ -197,7 +197,7 @@ function FormComposer({ data, onSubmit, finalResults }) {
- {sectionName && sectionInstruction &&
} + {sectionTitle && sectionInstruction &&
} {sectionInstruction && (

@@ -220,7 +220,7 @@ function FormComposer({ data, onSubmit, finalResults }) { /> {fieldsets.map(( fieldset, fieldsetIndex ) => { - let fieldsetName = fieldset.name; + let fieldsetTitle = fieldset.title; let fieldsetInstruction = fieldset.instruction; let rows = fieldset.rows; @@ -229,15 +229,15 @@ function FormComposer({ data, onSubmit, finalResults }) { key={`fieldset-${fieldsetIndex}`} className={`fieldset container`} > - {(fieldsetName || fieldsetInstruction) && ( + {(fieldsetTitle || fieldsetInstruction) && (

- {fieldsetName && ( + {fieldsetTitle && (
- {fieldsetName} + {fieldsetTitle}
)} - {fieldsetName && fieldsetInstruction &&
} + {fieldsetTitle && fieldsetInstruction &&
} {fieldsetInstruction && (

@@ -269,6 +269,7 @@ function FormComposer({ data, onSubmit, finalResults }) { col ${checkFieldRequiredness(field) ? "required" : ""}` } + title={field.tooltip} > @@ -383,7 +384,7 @@ function FormComposer({ data, onSubmit, finalResults }) { diff --git a/packages/form-composer/src/FormComposer/fields/SelectField.js b/packages/form-composer/src/FormComposer/fields/SelectField.js index db65c0b40..7a43d48ad 100644 --- a/packages/form-composer/src/FormComposer/fields/SelectField.js +++ b/packages/form-composer/src/FormComposer/fields/SelectField.js @@ -76,7 +76,7 @@ function SelectField({ key={`option-${field.id}-${index}`} value={option.value} > - {option.name} + {option.label} ); })}