Skip to content

Commit

Permalink
Merge pull request #1108 from facebookresearch/form-builder-code-inse…
Browse files Browse the repository at this point in the history
…rtions

Enabled custom code insertions for FormComposer
  • Loading branch information
meta-paul committed Mar 15, 2024
2 parents cb9cc8c + d56de4e commit cd04708
Show file tree
Hide file tree
Showing 74 changed files with 16,346 additions and 2,774 deletions.
50 changes: 50 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
version: 2

updates:
# Maintain dependencies for GitHub Actions
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
# ignore all patch version updates
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]

# Maintain dependencies for npm
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
# ignore all patch version updates
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]

# Maintain dependencies for pip
- package-ecosystem: "pip"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
# ignore all patch version updates
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]

# Maintain dependencies for Docker
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
labels:
- "dependencies"
# ignore all patch version updates
ignore:
- dependency-name: "*"
update-types: ["version-update:semver-patch"]
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ examples/**/build/*
examples/form_composer_demo/preview/*_preview.html

# Form Composer
mephisto/generators/form_composer/data/*.json
mephisto/generators/form_composer/webapp/package-lock.json
mephisto/generators/form_composer/data/*
!mephisto/generators/form_composer/data/insertions/custom_validators.js
!mephisto/generators/form_composer/data/insertions/custom_triggers.js
mephisto/generators/form_composer/hydra_configs/conf/*.yaml
!mephisto/generators/form_composer/hydra_configs/conf/default.yaml

Expand Down
2 changes: 1 addition & 1 deletion docker/entrypoints/server.mturk.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ cd /mephisto
# Directory for Task results
mkdir -p "data" && chmod 777 "data"
# Directory for Cypress testing
mkdir -p "/root/.cache/cypress" && chmod 777 "/root/.cache/cypress"
mkdir -p "/root/.cache/Cypress" && chmod 777 "/root/.cache/Cypress"

mephisto register mturk_sandbox \
name=$MTURK_SANDBOX_NAME \
Expand Down
2 changes: 1 addition & 1 deletion docker/entrypoints/server.prolific.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ cd /mephisto
# Directory for Task results
mkdir -p "data" && chmod 777 "data"
# Directory for Cypress testing
mkdir -p "/root/.cache/cypress" && chmod 777 "/root/.cache/cypress"
mkdir -p "/root/.cache/Cypress" && chmod 777 "/root/.cache/Cypress"

mephisto register prolific name=prolific api_key=$PROLIFIC_API_KEY

Expand Down
2 changes: 2 additions & 0 deletions docker/envs/env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,5 @@ AWS_SECRET_ACCESS_KEY=...
AWS_DEFAULT_REGION=...

S3_URL_EXPIRATION_MINUTES=60

CYPRESS_CACHE_FOLDER=/tmp
5 changes: 4 additions & 1 deletion examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,9 @@ Putting it altogether, let's prepare and launch a task featuring a form containi
- Create file `docker/aws_credentials` and populate it with AWS keys info (for infrastructure and Mturk)
- Populate your AWS credentials into `docker/envs/env.local` file (for presigning S3 URLs)
- Clone file `docker/docker-compose.dev.yml` as `docker/docker-compose.local.yml`, and point its `env_file` to `envs/env.local`
- Ensure `envs/env.local` file has a defeinition of these env variables:
- `PROLIFIC_API_KEY`: set it to an empty string if you don't have a value yet
- `CYPRESS_CACHE_FOLDER`: set it to any writable folder, e.g. `/tmp`
- Remove content of folder `/tmp` (if you didn't shut the previous Task run correctly)
- Launch docker containers: `docker-compose -f docker/docker-compose.local.yml up`
- SSH into the running container: `docker exec -it mephisto_dc bash`
Expand Down Expand Up @@ -181,7 +184,7 @@ Putting it altogether, let's prepare and launch a task featuring a form containi
```
- After the Task is completed by all workers, launch task review app and acces it at [http://localhost:8081](http://localhost:8081) (for more details see `mephisto/review_app/README.md`):
```shell
mephisto review_app -h 0.0.0.0 -p 8000 -d True -f True
mephisto review_app --host 0.0.0.0 --port 8000 --debug --force-rebuild
```

_Note: if a package build was terminated/failed, or related source code was changed, FormComposer needs to be rebuilt with this command: `mephisto scripts form_composer rebuild_all_apps`._
Expand Down
74 changes: 67 additions & 7 deletions examples/form_composer_demo/data/dynamic/form_config.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"form": {
"title": "Form example",
"instruction": "Please answer <b>all</b> questions to the best of your ability as part of our study.",
"instruction": "insertions/form_instruction.html",
"sections": [
{
"name": "section_about",
"title": "About you",
"instruction": "<p>Please introduce yourself. We would like to know more about your:</p><ul><li>Background</li><li>Personal information</li><li>Etc</li></ul>",
"instruction": "<p>Please introduce yourself. We would like to know more about your:</p>{{about_section_items}}",
"collapsable": false,
"fieldsets": [
{
Expand Down Expand Up @@ -94,6 +94,9 @@
],
"placeholder": "",
"tooltip": "Country",
"triggers": {
"onChange": "onChangeCountry"
},
"type": "select",
"validators": {
"required": true
Expand Down Expand Up @@ -132,7 +135,50 @@
"minLength": 2,
"maxLength": 3
},
"value": ""
"value": []
}
]
},
{
"fields": [
{
"classes": "hidden col-6",
"help": "Select region of your residence",
"id": "id_region",
"label": "Region",
"name": "region",
"options": [
{
"label": "---",
"value": ""
},
{
"label": "North-East",
"value": "NE"
},
{
"label": "North-West",
"value": "NW"
},
{
"label": "Mid-West",
"value": "MW"
},
{
"label": "South-East",
"value": "SE"
},
{
"label": "South-West",
"value": "SW"
}
],
"placeholder": "",
"tooltip": "Region",
"type": "select",
"validators": {
"required": true
}
}
]
}
Expand All @@ -154,7 +200,8 @@
"tooltip": "Your bio in a few paragraphs",
"type": "textarea",
"validators": {
"required": false
"required": false,
"checkForbiddenWords": true
},
"value": ""
}
Expand Down Expand Up @@ -270,13 +317,17 @@
],
"help": "Some additional details about your persona"
}
]
],
"triggers": {
"onClick": ["onClickSectionHeader", "FIRST"]
}
},
{
"name": "section_second",
"title": "Second section",
"instruction": "Example of another section",
"initially_collapsed": true,
"id": "id_section_second",
"fieldsets": [
{
"title": "Motivation",
Expand All @@ -291,15 +342,24 @@
"tooltip": "Your personal motto",
"type": "input",
"validators": {
"required": true
"required": false
}
},
{
"id": "id_additional_data",
"label": "",
"name": "additional_data",
"type": "hidden"
}
],
"help": "Please type in your favorite personal motto"
}
]
}
]
],
"triggers": {
"onClick": ["onClickSectionHeader", "SECOND"]
}
}
],
"submit_button": {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<ul>
<li>Background</li>
<li>Personal information</li>
<li>Etc</li>
</ul>
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// NOTE: that `react-form-composer` library must be set in webpack config as an alias.
import { validateFieldValue } from "react-form-composer";

export function onChangeCountry(
formData, // React state for the entire form
updateFormData, // callback to set the React state
element, // "field", "section", or "submit button" element that invoked this trigger
fieldValue, // (optional) current field value, if the `element` is a form field
formFields, // Object containing all form fields as defined in 'form_config.json'
...args // Arguments for this trigger (taken from form config)
) {
// By default, `id_section_second` section is collapsed, and `id_region` field is hidden.
// Selecting "USA" in `id_country` should open that section, and display that field.
const secondSectionElement = document.getElementById("id_section_second");
const regionFieldElement = document.getElementById("id_region");

if (fieldValue === "USA") {
// Open `id_section_second` section
secondSectionElement.classList.add("hidden");

// If you want to check (during development) that you're assigning a valid value to a field,
// use `validateFieldValue` function from form composer utils (see the import above).
const newMottoValueIsValid = validateFieldValue(formFields.motto, "", true);
if (!newMottoValueIsValid) {
console.log(
"Write additional log message or logic here " +
"if logs with argument `writeConsoleLog` is not enough for you"
);
}

updateFormData("motto", ""); // Clear field value in React state

// Show `id_region` field
regionFieldElement.closest(".field").classList.remove("hidden");
} else {
// Collapse `id_section_second` section
secondSectionElement.classList.remove("hidden");

// Hide `id_region` field
regionFieldElement.closest(".field").classList.add("hidden");
updateFormData("region", ""); // Clear field value in React state
}
}

export function onClickSectionHeader(
formData, // React state for the entire form
updateFormData, // callback to set the React state
element, // "field", "section", or "submit button" element that invoked this trigger
fieldValue, // (optional) current field value, if the `element` is a form field
formFields, // Object containing all form fields as defined in 'form_config.json'
sectionName // Argument for this trigger (taken from form config)
) {
// Do something when header is clicked (toggle a section content)
alert(`${sectionName} section title clicked.`);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
const FORBIDDEN_WORDS = ["fool", "silly", "stupid"];

export function checkForbiddenWords(field, value, check) {
if (!check) {
return null;
}

let invalid = false;

FORBIDDEN_WORDS.forEach((word) => {
if (value.includes(word)) {
invalid = true;
}
});

if (invalid) {
return `Field cannot contain any of these words: ${FORBIDDEN_WORDS.join(
", "
)}.`;
}

return null;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<div class="instruction">
Please answer <b>all</b> questions to the best of your ability as part of our
study.
</div>

<style>
.instruction {
font-style: italic;
}
</style>
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
{
"company_name": ["Facebook", "Mephisto"],
"about_section_items": ["insertions/about_section_items.html"],
"since_age": [18]
}
Loading

0 comments on commit cd04708

Please sign in to comment.