add different question types/params, survey-level params, expand example file (WIP)

This commit is contained in:
Becky Gilbert 2021-10-22 13:56:32 -07:00
parent a073b07f2a
commit c45cd249e3
2 changed files with 149 additions and 41 deletions

View File

@ -3,7 +3,8 @@
<head>
<script src="../jspsych/dist/index.browser.js"></script>
<script src="./dist/index.browser.js"></script>
<link rel="stylesheet" href="../jspsych/css/jspsych.css"></script>
<link rel="stylesheet" href="../jspsych/css/jspsych.css">
<link rel="stylesheet" href="css/plugin-survey.css">
</head>
<body></body>
<script type="text/javascript">
@ -14,26 +15,46 @@
}
});
var options = ['option 1', 'option 2', 'option 3', 'option 4', 'option 5', 'option 6'];
var survey_trial = {
type: jsPsychSurvey,
pages: [
[
{type: 'html', prompt: '<p>Here is some arbitrary text via an "html" question type.<br>Similar to preamble but can be inserted anywhere in the question set.</p>'},
{type: 'text', prompt: 'Page 1, question 1.'},
{type: 'text', prompt: 'Page 1, question 2.'},
{type: 'html', prompt: '<p>Here is some arbitrary text via an "html" question type.<br>Similar to preamble but can be inserted anywhere in the question set.<br>This trial uses automatic question numbering (continued across pages).</p>'},
{type: 'text', prompt: 'This is a single-line text question.', rows: 1, columns: 20},
{type: 'text', prompt: 'This is a multi-line text question.', placeholder: 'This is a placeholder.', rows: 5, columns: 40},
],
[
{type: 'text', prompt: 'Page 2, question 1.'},
{type: 'html', prompt: '<p>Don&#39;t forget to answer the next question!</p>'},
{type: 'text', prompt: 'Page 2, question 2.'},
{type: 'multi-choice', prompt: 'This is a multi-choice question with options in one column (the default).', options: options},
{type: 'multi-choice', prompt: 'This is a multi-choice question with options in one row.', options: options, columns: 0},
{type: 'multi-choice', prompt: 'This is a multi-choice question with options in two columns.', options: options, columns: 2},
{type: 'html', prompt: '<p>Another HTML question type.<br>Don&#39;t forget to answer the next question!</p>'},
{type: 'multi-select', prompt: 'This is a multi-select question.', options: options},
{type: 'multi-select', prompt: 'This is a multi-select question with three columns and random option ordering.', options: options, columns: 3, option_reorder: 'random'},
]
],
button_label_next: "Next >",
button_label_back: "< Back",
button_label_finish: "Finish!",
show_question_numbers: 'on'
};
jsPsych.run([survey_trial]);
var survey_trial_random = {
type: jsPsychSurvey,
pages: [[
{type: 'text', prompt: 'Question 1.', rows: 1, columns: 20},
{type: 'text', prompt: 'Question 2.', rows: 1, columns: 20},
{type: 'text', prompt: 'Question 3.', rows: 1, columns: 20},
{type: 'text', prompt: 'Question 4.', rows: 1, columns: 20},
{type: 'text', prompt: 'Question 5.', rows: 1, columns: 20},
{type: 'text', prompt: 'Question 6.', rows: 1, columns: 20},
]],
title: 'This is a separate survey trial. The order of questions should be randomized.',
randomize_question_order: true
};
jsPsych.run([survey_trial, survey_trial_random]);
</script>
</html>

View File

@ -15,17 +15,17 @@ const info = <const>{
type: ParameterType.SELECT,
pretty_name: "Type",
default: null, //undefined,
options: ["html", "text"], // TO DO: other types
options: ["html", "text", "multi-choice", "multi-select"], // TO DO: other types
},
/** Question prompt. */
prompt: {
type: ParameterType.HTML_STRING,
pretty_name: "Prompt",
default: null, //undefined,
default: null, // TO DO: undefined for HTML questions
},
/** Whether or not a response to this question must be given in order to continue. */
required: {
type: ParameterType.BOOL, // TO DO
type: ParameterType.BOOL,
pretty_name: "Required",
default: false,
},
@ -35,44 +35,47 @@ const info = <const>{
pretty_name: "Question Name",
default: "",
},
/** Multi-choice only: Array of strings that contains the set of multiple choice options to display for the question. */
/** Multi-choice/multi-select only: Array of strings that contains the set of multiple choice options to display for the question. */
options: {
type: ParameterType.STRING, // TO DO
type: ParameterType.STRING,
pretty_name: "Options",
default: null, // TO DO: how to make this undefined for certain question types?
default: null, // TO DO: undefined for multi-choice/multi-select
array: true,
},
/** Multi-choice only: If true, then the question is centered and the options are displayed horizontally. */
horizontal: {
type: ParameterType.BOOL, // TO DO
pretty_name: "Horizontal",
default: false,
/** Multi-choice/multi-select only: re-ordering of options array */
option_reorder: {
type: ParameterType.SELECT,
pretty_name: "Option reorder",
options: ["none", "asc", "desc", "random"],
default: "none",
},
/** Text only: Placeholder text in the response text box. */
placeholder: {
type: ParameterType.STRING, // TO DO
type: ParameterType.STRING,
pretty_name: "Placeholder",
default: "",
},
/** Text only: The number of rows for the response text box. */
rows: {
type: ParameterType.INT, // TO DO
type: ParameterType.INT,
pretty_name: "Rows",
default: 1,
},
/** Text only: The number of columns for the response text box. */
/**
* Text: The number of columns for the response text box.
* Multi-choice/multi-select: The number of columns
*/
columns: {
type: ParameterType.INT, // TO DO
type: ParameterType.INT,
pretty_name: "Columns",
default: 40,
},
},
},
/** If true, the order of the questions in each of the 'pages' arrays will be randomized. */
/** Whether or not to randomize the question order on each page */
randomize_question_order: {
// TO DO
type: ParameterType.BOOL,
pretty_name: "Randomize order",
pretty_name: "Randomize question order",
default: false,
},
/** Label of the button to move forward thorugh survey pages. */
@ -112,6 +115,15 @@ const info = <const>{
default: "off",
options: ["on", "onPage", "off"],
},
/**
* HTML-formatted text to be shown at the top of the survey pages. This also provides a method for fixing any arbitrary text to the top of the page when
* randomizing the question order, since HTML question types are also randomized.
*/
title: {
type: ParameterType.HTML_STRING,
pretty_name: "Title",
default: null,
},
},
};
@ -135,7 +147,7 @@ class SurveyPlugin implements JsPsychPlugin<Info> {
// https://surveyjs.io/Examples/Library/?id=custom-theme
}
// keys as strings so that the question types can have hyphens
// map jsPsych question types onto SurveyJS question types (keys as strings so that the question types can have hyphens)
readonly question_type_map: { [key: string]: string } = {
"drop-down": "dropdown",
html: "html",
@ -147,17 +159,62 @@ class SurveyPlugin implements JsPsychPlugin<Info> {
text: "text",
};
// TO DO:
// - move the setup functions outside of the trial method?
// - fix the question row spacing CSS
// - add other and none options to multi-choice/select
trial(display_element: HTMLElement, trial: TrialType<Info>) {
this.applyStyles();
this.applyStyles(); // applies bootstrap theme
// apply jsPsych CSS
// TO DO: add CSS class for question prompts etc., need a new class for this (jspsych-display-element adds other stuff that causes problems)
// add to this object in a modular way (by question type)?
// add custom CSS classes to survey elements
// https://surveyjs.io/Examples/Library/?id=survey-customcss&platform=Knockoutjs&theme=bootstrap#content-docs
const jspsych_css = {
question: {
title: "jspsych-display-element",
title: "jspsych-survey-question-prompt",
},
navigationButton: "jspsych-btn",
html: {
root: "jspsych-survey-html",
},
navigationButton: "jspsych-btn jspsych-survey-btn",
};
// functions for setting up different question types
const setup_dropdown_question = () => {};
const setup_html_question = (question, question_params) => {
// required: prompt
question.html = question_params.prompt;
return question;
};
const setup_likert_question = () => {};
const setup_multichoice_question = (question, question_params) => {
// required: options
// optional: columns, option_reorder
question.title = question_params.prompt;
question.isRequired = question_params.required;
question.choices = question_params.options;
if (typeof question_params.option_reorder == "undefined") {
question.choicesOrder = info.parameters.pages.nested.option_reorder.default; // TO DO: is this how to get the default value from parameter info?
} else {
question.choicesOrder = question_params.option_reorder;
}
question.colCount = question_params.columns;
return question;
};
const setup_ranking_question = () => {};
const setup_rating_question = () => {};
const setup_text_question = (question, question_params) => {
// optional: placeholder, rows, columns
question.title = question_params.prompt;
question.isRequired = question_params.required;
question.placeHolder = question_params.placeholder;
if (question.type == "comment") {
question.rows = question_params.rows;
question.cols = question_params.columns;
} else {
question.size = question_params.columns;
}
return question;
};
// https://surveyjs.io/Documentation/Library#survey-objects
@ -172,27 +229,57 @@ class SurveyPlugin implements JsPsychPlugin<Info> {
// page numbers
survey.showQuestionNumbers = trial.show_question_numbers;
// survey title
if (trial.title !== null) {
survey.title = trial.title;
}
// pages and questions
for (let i = 0; i < trial.pages.length; i++) {
let page_id = "page" + i.toString();
let page = survey.addNewPage(page_id);
if (trial.randomize_question_order) {
page.questionsOrder = "random"; // TO DO: save question presentation order to data
}
for (let j = 0; j < trial.pages[i].length; j++) {
// TO DO: move question set-up to modular question-type methods
let q_type = this.question_type_map[trial.pages[i][j].type];
if (q_type == "text" && trial.pages[i][j].rows > 1) {
let question_params = trial.pages[i][j];
let q_type = this.question_type_map[question_params.type];
if (q_type == "text" && question_params.rows > 1) {
q_type = "comment";
}
let question = page.addNewQuestion(q_type);
question.name = "p" + i.toString() + "_q" + j.toString();
if (q_type == "html") {
question.html = trial.pages[i][j].prompt;
} else {
question.title = trial.pages[i][j].prompt;
switch (q_type) {
case "comment": // text (multiple rows)
setup_text_question(question, question_params);
break;
case "dropdown":
break;
case "html":
setup_html_question(question, question_params);
break;
case "matrix": // likert
break;
case "radiogroup": // multi-choice
setup_multichoice_question(question, question_params);
break;
case "checkbox": // multi-select
setup_multichoice_question(question, question_params);
break;
case "ranking": // ranking
break;
case "rating": // rating
break;
case "text": // text (single row)
setup_text_question(question, question_params);
break;
default:
console.error('Error in survey plugin: invalid question type "', q_type, '"');
}
}
}
// set up survey structure via JS object
// for reference: how to set up survey structure via JS object
// var json = {
// pages: [
// {