diff --git a/.changeset/tricky-hairs-refuse.md b/.changeset/tricky-hairs-refuse.md new file mode 100644 index 00000000..f00c6fe4 --- /dev/null +++ b/.changeset/tricky-hairs-refuse.md @@ -0,0 +1,6 @@ +--- +"@jspsych-contrib/plugin-image-hotspots": minor +"@jspsych-contrib/plugin-video-hotspots": minor +--- + +Adds an optional `prompt` parameter to the `image-hotspots` and `video-hotspots` plugins, which is an HTML-formatted string displayed below the stimulus. This can be used to remind participants how to respond. By default, the `video-hotspots` plugin will display the prompt after the video ends, but this can be changed by setting the `show_prompt_on_video_end` parameter to `false`. \ No newline at end of file diff --git a/packages/plugin-image-hotspots/docs/plugin-image-hotspots.md b/packages/plugin-image-hotspots/docs/plugin-image-hotspots.md index c9c7034c..4248e339 100644 --- a/packages/plugin-image-hotspots/docs/plugin-image-hotspots.md +++ b/packages/plugin-image-hotspots/docs/plugin-image-hotspots.md @@ -12,6 +12,7 @@ In addition to the [parameters available in all plugins](https://www.jspsych.org | hotspots | complex | [] | Array of hotspot regions. Each hotspot should have x, y, width, height, and id properties. | | trial_duration | int | null | How long to show the trial in milliseconds. If null, the trial will wait for a response. | | hotspot_highlight_css | string | "background-color: rgba(255, 255, 0, 0.3); border: 2px solid yellow;" | CSS string to style the hotspot highlight overlay that appears when clicking/touching a region. | +| prompt | string | null | This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that it can be used to provide a reminder about the action the participant is supposed to take (e.g., click on a specific area). | ### Hotspot Object Properties diff --git a/packages/plugin-image-hotspots/src/index.spec.ts b/packages/plugin-image-hotspots/src/index.spec.ts index 312c9f43..0239402e 100644 --- a/packages/plugin-image-hotspots/src/index.spec.ts +++ b/packages/plugin-image-hotspots/src/index.spec.ts @@ -243,4 +243,27 @@ describe("jsPsychImageHotspots plugin", () => { const data = getData().values()[0]; expect(data.hotspot_clicked).toBeNull(); }); + + it("should show prompt if there is one", async () => { + const { expectFinished, getHTML, getData, displayElement } = await startTimeline([ + { + type: jsPsychImageHotspots, + stimulus: "test.jpg", + prompt: "

This is a prompt

", + hotspots: [ + { + id: "test_hotspot", + x: 50, + y: 50, + width: 100, + height: 100, + }, + ], + }, + ]); + + expect(getHTML()).toContain(` + +

This is a prompt

`); + }); }); diff --git a/packages/plugin-image-hotspots/src/index.ts b/packages/plugin-image-hotspots/src/index.ts index 4962ad94..3ff652f3 100644 --- a/packages/plugin-image-hotspots/src/index.ts +++ b/packages/plugin-image-hotspots/src/index.ts @@ -26,6 +26,11 @@ const info = { type: ParameterType.STRING, default: "background-color: rgba(255, 255, 0, 0.3); border: 2px solid yellow;", }, + /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that it can be used to provide a reminder about the action the participant is supposed to take (e.g., click on a specific area). */ + prompt: { + type: ParameterType.HTML_STRING, + default: null, + }, }, data: { /** The ID of the clicked hotspot region. */ @@ -68,12 +73,16 @@ class ImageHotspotsPlugin implements JsPsychPlugin { const start_time = performance.now(); // Create the HTML structure - const html = ` + let html = `
`; + // Add prompt if there is one + if (trial.prompt !== null) { + html += trial.prompt; + } display_element.innerHTML = html; diff --git a/packages/plugin-video-hotspots/docs/plugin-video-hotspots.md b/packages/plugin-video-hotspots/docs/plugin-video-hotspots.md index 997729f6..b4b98a1b 100644 --- a/packages/plugin-video-hotspots/docs/plugin-video-hotspots.md +++ b/packages/plugin-video-hotspots/docs/plugin-video-hotspots.md @@ -22,6 +22,8 @@ In addition to the [parameters available in all plugins](https://www.jspsych.org | trial_duration | int | null | How long to show the trial in milliseconds after video ends. If null, trial waits for response. | | hotspot_highlight_css | string | "background-color: rgba(255, 255, 0, 0.3); border: 2px solid yellow;" | CSS string to style the hotspot highlight overlay that appears when clicking/touching a region. | | video_preload | boolean | true | Whether to preload the video for smoother playback. | +| prompt | string | null | This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that it can be used to provide a reminder about the action the participant is supposed to take (e.g., click on a specific area). | +| show_prompt_on_video_end | boolean | true | Whether to wait until the video ends to display the prompt string (if there is one). If true (the default), the prompt will be shown when the video has ended. If false, the prompt is shown immediately. | ### Hotspot Object Properties diff --git a/packages/plugin-video-hotspots/examples/index.html b/packages/plugin-video-hotspots/examples/index.html index 5d79021c..863ad736 100644 --- a/packages/plugin-video-hotspots/examples/index.html +++ b/packages/plugin-video-hotspots/examples/index.html @@ -82,7 +82,8 @@

Video Hotspots Plugin Demo

height: 171 } ], - hotspot_highlight_css: "background-color: rgba(0, 255, 0, 0.5); border: 3px solid green; border-radius: 8px; box-shadow: 0 0 15px rgba(0, 255, 0, 0.7);" + hotspot_highlight_css: "background-color: rgba(0, 255, 0, 0.5); border: 3px solid green; border-radius: 8px; box-shadow: 0 0 15px rgba(0, 255, 0, 0.7);", + prompt: "
Click on a square.
" }; // Now we can run the actual video hotspots trial with the demo video diff --git a/packages/plugin-video-hotspots/src/index.spec.ts b/packages/plugin-video-hotspots/src/index.spec.ts index 4f0280a9..93f461e4 100644 --- a/packages/plugin-video-hotspots/src/index.spec.ts +++ b/packages/plugin-video-hotspots/src/index.spec.ts @@ -235,4 +235,62 @@ describe("jsPsychVideoHotspots plugin", () => { expect(Number.isInteger(data.click_x)).toBe(true); expect(Number.isInteger(data.click_y)).toBe(true); }); + + it("should show prompt after video ends", async () => { + const { expectFinished, getHTML, getData, displayElement } = await startTimeline([ + { + type: jsPsychVideoHotspots, + stimulus: "test.mp4", + prompt: "

This is a prompt

", + hotspots: [ + { + id: "test_hotspot", + x: 50, + y: 50, + width: 100, + height: 100, + }, + ], + }, + ]); + + expect(getHTML()).toContain( + `
+

This is a prompt

`); + }); }); diff --git a/packages/plugin-video-hotspots/src/index.ts b/packages/plugin-video-hotspots/src/index.ts index 2105cf27..d749d137 100644 --- a/packages/plugin-video-hotspots/src/index.ts +++ b/packages/plugin-video-hotspots/src/index.ts @@ -31,6 +31,16 @@ const info = { type: ParameterType.BOOL, default: true, }, + /** This string can contain HTML markup. Any content here will be displayed below the stimulus. The intention is that it can be used to provide a reminder about the action the participant is supposed to take (e.g., click on a specific area). */ + prompt: { + type: ParameterType.HTML_STRING, + default: null, + }, + /** Whether to wait until the video ends to display the prompt string (if there is one). If true (the default), the prompt will be shown when the video has ended. If false, the prompt is shown immediately. */ + show_prompt_on_video_end: { + type: ParameterType.BOOL, + default: true, + }, }, data: { /** The ID of the clicked hotspot region. */ @@ -77,7 +87,7 @@ class VideoHotspotsPlugin implements JsPsychPlugin { let video_end_time: number | null = null; // Create the HTML structure - const html = ` + let html = `
`; + // Add prompt if there is one + if (trial.prompt !== null) { + if (trial.show_prompt_on_video_end) { + // Add to the DOM and toggle visibility after video ends, rather than adding a new element later (which causes content position jump) + html += ``; + } else { + html += trial.prompt; + } + } display_element.innerHTML = html; @@ -258,6 +277,14 @@ class VideoHotspotsPlugin implements JsPsychPlugin { // Create hotspots now that video has ended createHotspots(); + // Add prompt if there is one and it should be shown when the video ends + if (trial.prompt !== null && trial.show_prompt_on_video_end) { + const prompt = display_element.querySelector( + "#jspsych-video-hotspots-prompt" + ) as HTMLSpanElement; + prompt.style.visibility = "visible"; + } + // Handle trial duration after video ends if (trial.trial_duration !== null) { this.jsPsych.pluginAPI.setTimeout(() => {