Skip to content

Commit

Permalink
Merge pull request #3444 from crava2199/plugin-survey-multi-choice-fo…
Browse files Browse the repository at this point in the history
…rmid-fix

added form id in querySelector to plugin-survey-multi-choice
  • Loading branch information
jodeleeuw authored Dec 16, 2024
2 parents ebc5b2a + 140a693 commit 4303c91
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 58 deletions.
5 changes: 5 additions & 0 deletions .changeset/rare-years-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@jspsych/plugin-survey-multi-choice": patch
---

The plugin will now work in cases where there are multiple forms on the page.
33 changes: 32 additions & 1 deletion packages/plugin-survey-multi-choice/src/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { clickTarget, simulateTimeline, startTimeline } from "@jspsych/test-utils";

import { initJsPsych } from "jspsych";
import surveyMultiChoice from ".";

jest.useFakeTimers();
Expand All @@ -10,6 +11,36 @@ const getInputElement = (choiceId: number, value: string) =>
) as HTMLInputElement;

describe("survey-multi-choice plugin", () => {
test("properly ends when has sibling form", async () => {

const container = document.createElement('div')
const outerForm = document.createElement('form')
outerForm.id = 'outer_form'
container.appendChild(outerForm)
const innerDiv = document.createElement('div')
innerDiv.id = 'target_id';
container.appendChild(innerDiv);
document.body.appendChild(container)
const jsPsychInst = initJsPsych({ display_element: innerDiv })
const options = ["a", "b", "c"];

const { getData, expectFinished } = await startTimeline([
{
type: surveyMultiChoice,
questions: [
{ prompt: "Q0", options },
{ prompt: "Q1", options },
]
},
], jsPsychInst);

getInputElement(0, "a").checked = true;
await clickTarget(document.querySelector("#jspsych-survey-multi-choice-next"));
await expectFinished();

})


test("data are logged with the right question when randomize order is true", async () => {
var scale = ["a", "b", "c", "d", "e"];
const { getData, expectFinished } = await startTimeline([
Expand Down Expand Up @@ -45,7 +76,7 @@ describe("survey-multi-choice plugin", () => {
});
});

describe("survey-likert plugin simulation", () => {
describe("survey-multi-choice plugin simulation", () => {
test("data-only mode works", async () => {
const scale = ["a", "b", "c", "d", "e"];
const { getData, expectFinished } = await simulateTimeline([
Expand Down
96 changes: 39 additions & 57 deletions packages/plugin-survey-multi-choice/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ const info = <const>{

type Info = typeof info;

const plugin_id_name = "jspsych-survey-multi-choice";

/**
* **survey-multi-choice**
*
Expand All @@ -107,38 +109,37 @@ type Info = typeof info;
class SurveyMultiChoicePlugin implements JsPsychPlugin<Info> {
static info = info;

constructor(private jsPsych: JsPsych) {}
constructor(private jsPsych: JsPsych) { }

trial(display_element: HTMLElement, trial: TrialType<Info>) {
var plugin_id_name = "jspsych-survey-multi-choice";

const trial_form_id = `${plugin_id_name}_form`;

var html = "";

// inject CSS for trial
html += '<style id="jspsych-survey-multi-choice-css">';
html +=
".jspsych-survey-multi-choice-question { margin-top: 2em; margin-bottom: 2em; text-align: left; }" +
".jspsych-survey-multi-choice-text span.required {color: darkred;}" +
".jspsych-survey-multi-choice-horizontal .jspsych-survey-multi-choice-text { text-align: center;}" +
".jspsych-survey-multi-choice-option { line-height: 2; }" +
".jspsych-survey-multi-choice-horizontal .jspsych-survey-multi-choice-option { display: inline-block; margin-left: 1em; margin-right: 1em; vertical-align: top;}" +
"label.jspsych-survey-multi-choice-text input[type='radio'] {margin-right: 1em;}";
html += "</style>";
html += `
<style id="${plugin_id_name}-css">
.${plugin_id_name}-question { margin-top: 2em; margin-bottom: 2em; text-align: left; }
.${plugin_id_name}-text span.required {color: darkred;}
.${plugin_id_name}-horizontal .${plugin_id_name}-text { text-align: center;}
.${plugin_id_name}-option { line-height: 2; }
.${plugin_id_name}-horizontal .${plugin_id_name}-option { display: inline-block; margin-left: 1em; margin-right: 1em; vertical-align: top;}
label.${plugin_id_name}-text input[type='radio'] {margin-right: 1em;}
</style>`;

// show preamble text
if (trial.preamble !== null) {
html +=
'<div id="jspsych-survey-multi-choice-preamble" class="jspsych-survey-multi-choice-preamble">' +
trial.preamble +
"</div>";
html += `<div id="${plugin_id_name}-preamble" class="${plugin_id_name}-preamble">${trial.preamble}</div>`;
}

// form element
if (trial.autocomplete) {
html += '<form id="jspsych-survey-multi-choice-form">';
html += `<form id="${trial_form_id}">`;
} else {
html += '<form id="jspsych-survey-multi-choice-form" autocomplete="off">';
html += `<form id="${trial_form_id}" autocomplete="off">`;
}

// generate question order. this is randomized here as opposed to randomizing the order of trial.questions
// so that the data are always associated with the same question regardless of order
var question_order = [];
Expand All @@ -156,22 +157,15 @@ class SurveyMultiChoicePlugin implements JsPsychPlugin<Info> {
var question_id = question_order[i];

// create question container
var question_classes = ["jspsych-survey-multi-choice-question"];
var question_classes = [`${plugin_id_name}-question`];
if (question.horizontal) {
question_classes.push("jspsych-survey-multi-choice-horizontal");
question_classes.push(`${plugin_id_name}-horizontal`);
}

html +=
'<div id="jspsych-survey-multi-choice-' +
question_id +
'" class="' +
question_classes.join(" ") +
'" data-name="' +
question.name +
'">';
html += `<div id="${plugin_id_name}-${question_id}" class="${question_classes.join(" ")}" data-name="${question.name}">`;

// add question text
html += '<p class="jspsych-survey-multi-choice-text survey-multi-choice">' + question.prompt;
html += `<p class="${plugin_id_name}-text survey-multi-choice">${question.prompt}`;
if (question.required) {
html += "<span class='required'>*</span>";
}
Expand All @@ -180,47 +174,35 @@ class SurveyMultiChoicePlugin implements JsPsychPlugin<Info> {
// create option radio buttons
for (var j = 0; j < question.options.length; j++) {
// add label and question text
var option_id_name = "jspsych-survey-multi-choice-option-" + question_id + "-" + j;
var input_name = "jspsych-survey-multi-choice-response-" + question_id;
var input_id = "jspsych-survey-multi-choice-response-" + question_id + "-" + j;
var option_id_name = `${plugin_id_name}-option-${question_id}-${j}`;
var input_name = `${plugin_id_name}-response-${question_id}`;
var input_id = `${plugin_id_name}-response-${question_id}-${j}`;

var required_attr = question.required ? "required" : "";

// add radio button container
html += '<div id="' + option_id_name + '" class="jspsych-survey-multi-choice-option">';
html += '<label class="jspsych-survey-multi-choice-text" for="' + input_id + '">';
html +=
'<input type="radio" name="' +
input_name +
'" id="' +
input_id +
'" value="' +
question.options[j] +
'" ' +
required_attr +
"></input>";
html += question.options[j] + "</label>";
html += "</div>";
html += `
<div id="${option_id_name}" class="${plugin_id_name}-option">
<label class="${plugin_id_name}-text" for="${input_id}">
<input type="radio" name="${input_name}" id="${input_id}" value="${question.options[j]}" ${required_attr} />
${question.options[j]}
</label>
</div>`;
}

html += "</div>";
}

// add submit button
html +=
'<input type="submit" id="' +
plugin_id_name +
'-next" class="' +
plugin_id_name +
' jspsych-btn"' +
(trial.button_label ? ' value="' + trial.button_label + '"' : "") +
"></input>";
html += `<input type="submit" id="${plugin_id_name}-next" class="${plugin_id_name} jspsych-btn"${trial.button_label ? ' value="' + trial.button_label + '"' : ""} />`;
html += "</form>";

// render
display_element.innerHTML = html;

document.querySelector("form").addEventListener("submit", (event) => {
const trial_form = display_element.querySelector<HTMLFormElement>(`#${trial_form_id}`);

trial_form.addEventListener("submit", (event) => {
event.preventDefault();
// measure response time
var endTime = performance.now();
Expand All @@ -229,7 +211,7 @@ class SurveyMultiChoicePlugin implements JsPsychPlugin<Info> {
// create object to hold responses
var question_data = {};
for (var i = 0; i < trial.questions.length; i++) {
var match = display_element.querySelector("#jspsych-survey-multi-choice-" + i);
var match = display_element.querySelector(`#${plugin_id_name}-${i}`);
var id = "Q" + i;
var val: String;
if (match.querySelector("input[type=radio]:checked") !== null) {
Expand Down Expand Up @@ -317,7 +299,7 @@ class SurveyMultiChoicePlugin implements JsPsychPlugin<Info> {
for (let i = 0; i < answers.length; i++) {
this.jsPsych.pluginAPI.clickTarget(
display_element.querySelector(
`#jspsych-survey-multi-choice-response-${i}-${trial.questions[i].options.indexOf(
`#${plugin_id_name}-response-${i}-${trial.questions[i].options.indexOf(
answers[i][1]
)}`
),
Expand All @@ -326,7 +308,7 @@ class SurveyMultiChoicePlugin implements JsPsychPlugin<Info> {
}

this.jsPsych.pluginAPI.clickTarget(
display_element.querySelector("#jspsych-survey-multi-choice-next"),
display_element.querySelector(`#${plugin_id_name}-next`),
data.rt
);
}
Expand Down

0 comments on commit 4303c91

Please sign in to comment.