diff --git a/docs/web/docs/guides/how_to_use/form_composer/configuration/config_files.md b/docs/web/docs/guides/how_to_use/form_composer/configuration/config_files.md
index 91ee09b99..68675c48e 100644
--- a/docs/web/docs/guides/how_to_use/form_composer/configuration/config_files.md
+++ b/docs/web/docs/guides/how_to_use/form_composer/configuration/config_files.md
@@ -44,6 +44,7 @@ Task data config file `task_data.json` specifies layout of all form versions tha
"form": {
"title": "Form example",
"instruction": "Please answer all questions to the best of your ability as part of our study.",
+ "show_instructions_as_modal": false,
"sections": [
// Two sections
{
@@ -159,8 +160,9 @@ TBD: Other classes and styles insertions
`form` is a top-level config object with the following attributes:
- `id` - Unique HTML id of the form, in case we need to refer to it from custom handlers code (String, Optional)
-- `classes` = Custom classes that you can use to restyle element or refer to it from custom handlers code (String, Optional)
+- `classes` - Custom classes that you can use to restyle element or refer to it from custom handlers code (String, Optional)
- `instruction` - HTML content describing this form; it is located before all contained sections (String, Optional)
+- `show_instructions_as_modal` - Enables showing `instruction` content as a modal (opened by clicking a sticky button in top-right corner); this make lengthy task instructions available from any place of a lengthy form without scrolling the page (Boolean, Optional, Default: false)
- `title` - HTML header of the form (String)
- `submit_button` - Button to submit the whole form and thus finish a task (Object)
- `id` - Unique HTML id of the button, in case we need to refer to it from custom handlers code (String, Optional)
diff --git a/examples/form_composer_demo/data/dynamic/form_config.json b/examples/form_composer_demo/data/dynamic/form_config.json
index fcaf62231..f293f928d 100644
--- a/examples/form_composer_demo/data/dynamic/form_config.json
+++ b/examples/form_composer_demo/data/dynamic/form_config.json
@@ -2,6 +2,7 @@
"form": {
"title": "Form example",
"instruction": "insertions/form_instruction.html",
+ "show_instructions_as_modal": true,
"sections": [
{
"name": "section_about",
diff --git a/mephisto/generators/form_composer/config_validation/config_validation_constants.py b/mephisto/generators/form_composer/config_validation/config_validation_constants.py
index 5a7fad653..860f0af42 100644
--- a/mephisto/generators/form_composer/config_validation/config_validation_constants.py
+++ b/mephisto/generators/form_composer/config_validation/config_validation_constants.py
@@ -40,6 +40,10 @@
"type": list,
"required": True,
},
+ "show_instructions_as_modal": {
+ "type": bool,
+ "required": False,
+ },
"submit_button": {
"type": dict,
"required": True,
diff --git a/packages/react-form-composer/src/FormComposer/FormComposer.css b/packages/react-form-composer/src/FormComposer/FormComposer.css
index 6cc26b7cb..beff3b976 100644
--- a/packages/react-form-composer/src/FormComposer/FormComposer.css
+++ b/packages/react-form-composer/src/FormComposer/FormComposer.css
@@ -16,8 +16,9 @@ video {
/* --- Form --- */
.form-composer {
/* Variables */
- --input-bg-color: #fafafa;
--error-color: red;
+ --form-max-width: 1280px;
+ --input-bg-color: #fafafa;
--orange-color: orange;
margin: 0 auto;
@@ -25,7 +26,7 @@ video {
display: flex;
flex-direction: column;
justify-content: center;
- max-width: 1280px;
+ max-width: var(--form-max-width);
}
.form-composer .form-header {
@@ -38,6 +39,12 @@ video {
.form-composer .form-header .form-instruction {
}
+.form-composer .form-header .form-instruction-button {
+ position: fixed;
+ right: 10px;
+ top: 10px;
+}
+
/* --- Section --- */
.form-composer .section {
}
@@ -311,6 +318,58 @@ video {
.form-composer .form-buttons .button-submit {
}
+/* --- Form instruction modal --- */
+.form-composer .form-instruction-modal {
+ padding: 10px 0;
+ background-color: #ffffff;
+}
+
+.form-composer .form-instruction-modal .modal-dialog {
+ width: initial;
+ max-width: var(--form-max-width);
+ max-height: 100%;
+ margin: 0 auto;
+}
+
+.form-composer .form-instruction-modal .modal-dialog .modal-content {
+ box-shadow: 0 10px 20px 10px rgba(0, 0, 0, 0.5);
+ -webkit-box-shadow: 0 10px 20px 10px rgba(0, 0, 0, 0.5);
+}
+
+.form-composer
+ .form-instruction-modal
+ .modal-dialog
+ .modal-content
+ .modal-header {
+ padding: 10px 20px;
+ align-items: center;
+ background-color: #cce5ff;
+}
+
+.form-composer
+ .form-instruction-modal
+ .modal-dialog
+ .modal-content
+ .modal-header
+ .modal-title {
+ font-size: 21px;
+ font-weight: 500;
+ line-height: initial;
+}
+
+.form-composer
+ .form-instruction-modal
+ .modal-dialog
+ .modal-content
+ .modal-header
+ .close {
+ margin: 0 0 0 auto;
+ font-size: 30px;
+ width: 40px;
+ height: 40px;
+ line-height: 0;
+}
+
/* --- Bootstrap overriding --- */
.form-control::placeholder {
diff --git a/packages/react-form-composer/src/FormComposer/FormComposer.js b/packages/react-form-composer/src/FormComposer/FormComposer.js
index 8ed760dae..1b3399735 100644
--- a/packages/react-form-composer/src/FormComposer/FormComposer.js
+++ b/packages/react-form-composer/src/FormComposer/FormComposer.js
@@ -20,6 +20,8 @@ import { SelectField } from "./fields/SelectField";
import { TextareaField } from "./fields/TextareaField";
import "./FormComposer.css";
import { FormErrors } from "./FormErrors";
+import { FormInstructionsButton } from "./FormInstructionsButton";
+import { FormInstructionsModal } from "./FormInstructionsModal";
import { SectionErrors } from "./SectionErrors";
import { SectionErrorsCountBadge } from "./SectionErrorsCountBadge";
import {
@@ -63,6 +65,12 @@ function FormComposer({
// Fild list by section index for error display: {
}
+ {/* Show instruction or button that opens a modal with instructions */}
+ {showFormInstructionAsModal ? (
+ <>
+ {/* Instructions */}
+ {formTitle && formInstruction &&
}
- {formInstruction && (
-
+ {formInstruction && (
+
}
+
+ {formInstruction && (
+
+ )}
+ >
)}
)}
@@ -689,6 +724,21 @@ function FormComposer({
{/* Unexpected server errors */}
{!!submitErrors.length &&