diff --git a/package-lock.json b/package-lock.json index 0a97c4a4..51d76279 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3454,6 +3454,10 @@ "resolved": "packages/plugin-image-array-keyboard-response", "link": true }, + "node_modules/@jspsych-contrib/plugin-image-click-response": { + "resolved": "packages/plugin-image-click-response", + "link": true + }, "node_modules/@jspsych-contrib/plugin-image-multi-response": { "resolved": "packages/plugin-image-multi-response", "link": true @@ -18937,6 +18941,19 @@ "jspsych": ">=7.0.0" } }, + "packages/plugin-image-click-response": { + "name": "@jspsych-contrib/plugin-image-click-response", + "version": "0.0.1", + "license": "MIT", + "devDependencies": { + "@jspsych/config": "^2.0.0", + "@jspsych/test-utils": "^1.0.0", + "jspsych": "^7.0.0" + }, + "peerDependencies": { + "jspsych": ">=7.0.0" + } + }, "packages/plugin-image-multi-response": { "name": "@jspsych-contrib/plugin-image-multi-response", "version": "1.0.1", diff --git a/packages/plugin-image-click-response/README.md b/packages/plugin-image-click-response/README.md new file mode 100644 index 00000000..f8300c5c --- /dev/null +++ b/packages/plugin-image-click-response/README.md @@ -0,0 +1,35 @@ +# image-click-response + +## Overview + +This plugin shows an image on which the user can place points by clicking/touching the image. The location of each point is recorded as data. + +## Loading + +### In browser + +```js + +``` + +### Via NPM + +``` +npm install @jspsych-contrib/plugin-image-click-response +``` + +```js +import jsPsychImageClickResponse from '@jspsych-contrib/plugin-image-click-response'; +``` + +## Compatibility + +jsPsych 7.0.0 + +## Documentation + +See [documentation](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-image-click-response/docs/jspsych-image-click-response.md) + +## Author / Citation + +[Christophe Bossens](https://github.com/ChristopheBossens) diff --git a/packages/plugin-image-click-response/docs/image-click-response.md b/packages/plugin-image-click-response/docs/image-click-response.md new file mode 100644 index 00000000..6edc8414 --- /dev/null +++ b/packages/plugin-image-click-response/docs/image-click-response.md @@ -0,0 +1,64 @@ +# image-click-response + +This plugin shows an image on which the user can place points by clicking/touching the image. The x,y coordinates for each point are recorderd. + +## Parameters + +In addition to the [parameters available in all plugins](https://jspsych.org/latest/overview/plugins.md#parameters-available-in-all-plugins), this plugin accepts the following parameters. Parameters with a default value of undefined must be specified. Other parameters can be left unspecified if the default value is acceptable. + +| Parameter | Type | Default Value | Description | +| ------------------- | ---------------- | ------------------ | ---------------------------------------- | +| preamble | HTML_STRING |
Click the image to add a point. Click a point to remove
| Instruction text that will appear above the image | +| stimulus |IMAGE | undefined | URL to the image that will be displayed| +| dot_radius | INT | 5| Radius of the dot in pixels| +| dot_color | STRING | lightblue| Color name for the dot| +| button_label | STRING | Continue| Label for the continue button | +| minimum_dots_required | INT | 0| Minimum number of dots required before the Continue button becomes active| + +## Data Generated + +In addition to the [default data collected by all plugins](https://jspsych.org/latest/overview/plugins.md#data-collected-by-all-plugins), this plugin collects the following data for each trial. + +| Name | Type | Value | +| --------- | ------- | ---------------------------------------- | +| stimulus | IMAGE | URL to the image that will be displayed | +| rt | INT | Time in milliseconds to compete the trial | +| points | ARRAY | Array of (x,y) coordinates for each point on the image | + +## Install + +Using the CDN-hosted JavaScript file: + +```js + +``` + +Using the JavaScript file downloaded from a GitHub release dist archive: + +```js + +``` + +Using NPM: + +``` +npm install @jspsych-contrib/plugin-image-click-response +``` + +```js +import ImageClickResponse from '@jspsych-contrib/plugin-image-click-response'; +``` + +## Examples + +### Title of Example + +```javascript +const trial = { + type: jsPsychImageClickResponse, + stimulus: "https://www.jspsych.org/7.3/img/jspsych-logo.jpg", + dot_radius : 10 + }; +``` + +See [example #1](../examples/index.html) for a live demonstration. \ No newline at end of file diff --git a/packages/plugin-image-click-response/examples/index.html b/packages/plugin-image-click-response/examples/index.html new file mode 100644 index 00000000..6bad3081 --- /dev/null +++ b/packages/plugin-image-click-response/examples/index.html @@ -0,0 +1,25 @@ + + + +
+ + + + + + + + + + + \ No newline at end of file diff --git a/packages/plugin-image-click-response/examples/jspsych-logo.jpg b/packages/plugin-image-click-response/examples/jspsych-logo.jpg new file mode 100644 index 00000000..c402d41d Binary files /dev/null and b/packages/plugin-image-click-response/examples/jspsych-logo.jpg differ diff --git a/packages/plugin-image-click-response/jest.config.cjs b/packages/plugin-image-click-response/jest.config.cjs new file mode 100644 index 00000000..6ac19d5c --- /dev/null +++ b/packages/plugin-image-click-response/jest.config.cjs @@ -0,0 +1 @@ +module.exports = require("@jspsych/config/jest").makePackageConfig(__dirname); diff --git a/packages/plugin-image-click-response/package.json b/packages/plugin-image-click-response/package.json new file mode 100644 index 00000000..ccda282f --- /dev/null +++ b/packages/plugin-image-click-response/package.json @@ -0,0 +1,47 @@ +{ + "name": "@jspsych-contrib/plugin-image-click-response", + "version": "0.0.1", + "description": "This plugin shows an image on which the user can place points by clicking/touching the image. The location of each point is recorded as data.", + "type": "module", + "main": "dist/index.cjs", + "exports": { + "import": "./dist/index.js", + "require": "./dist/index.cjs" + }, + "typings": "dist/index.d.ts", + "unpkg": "dist/index.browser.min.js", + "files": [ + "src", + "dist" + ], + "source": "src/index.ts", + "scripts": { + "test": "jest", + "test:watch": "npm test -- --watch", + "tsc": "tsc", + "build": "rollup --config", + "build:watch": "npm run build -- --watch" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/jspsych/jspsych-contrib.git", + "directory": "packages/plugin-image-click-response" + }, + "author": { + "name": "Christophe Bossens", + "url": "https://github.com/ChristopheBossens" + }, + "license": "MIT", + "bugs": { + "url": "https://github.com/jspsych/jspsych-contrib/issues" + }, + "homepage": "https://github.com/jspsych/jspsych-contrib/tree/main/packages/plugin-image-click-response", + "peerDependencies": { + "jspsych": ">=7.0.0" + }, + "devDependencies": { + "@jspsych/config": "^2.0.0", + "@jspsych/test-utils": "^1.0.0", + "jspsych": "^7.0.0" + } +} diff --git a/packages/plugin-image-click-response/rollup.config.mjs b/packages/plugin-image-click-response/rollup.config.mjs new file mode 100644 index 00000000..51c93323 --- /dev/null +++ b/packages/plugin-image-click-response/rollup.config.mjs @@ -0,0 +1,3 @@ +import { makeRollupConfig } from "@jspsych/config/rollup"; + +export default makeRollupConfig("jsPsychImageClickResponse"); diff --git a/packages/plugin-image-click-response/src/index.spec.ts b/packages/plugin-image-click-response/src/index.spec.ts new file mode 100644 index 00000000..8e085f98 --- /dev/null +++ b/packages/plugin-image-click-response/src/index.spec.ts @@ -0,0 +1,106 @@ +import { clickTarget, startTimeline } from "@jspsych/test-utils"; + +import jsPsychImageClickResponse from "."; + +jest.useFakeTimers(); + +describe("imag-click-response", () => { + it("should load", async () => { + const { expectFinished, getHTML, getData, displayElement, jsPsych } = await startTimeline([ + { + type: jsPsychImageClickResponse, + stimulus: "image.jpg", + dot_radius: 6, + dot_color: "blue", + minimum_dots_required: 1, + }, + ]); + }); + + it("correctly configures layout parameters", async () => { + const stimulus_value = "../examples/jspsych-logo.jpg"; + const button_label_value = "random label"; + + const { expectRunning, getHTML } = await startTimeline([ + { + type: jsPsychImageClickResponse, + stimulus: stimulus_value, + button_label: button_label_value, + }, + ]); + + await expectRunning(); + + const html = getHTML().replace(/"/g, "'"); + + expect(html).toContain(`href='${stimulus_value}'`); + expect(html).toContain(button_label_value); + }); + + it("correctly sets the continue button state when the minimum_dots_required parameter is > 0", async () => { + const minimum_dots_required = 1; + + const { expectRunning, getHTML, getData, displayElement, jsPsych } = await startTimeline([ + { + type: jsPsychImageClickResponse, + stimulus: "image.jpg", + minimum_dots_required: 1, + }, + ]); + + const response_button = displayElement.querySelector( + "#image-click-response-button" + ) as HTMLButtonElement; + const svg_container = document.getElementById("image-click-response-svg") as HTMLElement; + + await expectRunning(); + + expect(response_button.disabled).toBe(true); + await clickTarget(svg_container); + + expect(response_button.disabled).toBe(false); + }); + + it("should end the trial when the button is clicked", async () => { + const { expectRunning, getHTML, expectFinished, getData, displayElement, jsPsych } = + await startTimeline([ + { + type: jsPsychImageClickResponse, + stimulus: "image.jpg", + }, + ]); + + const response_button = displayElement.querySelector( + "#image-click-response-button" + ) as HTMLButtonElement; + await clickTarget(response_button); + + await expectFinished(); + }); + + it("should add (x,y) coordinates to the data structure when the image is clicked", async () => { + const { expectRunning, getHTML, expectFinished, getData, displayElement, jsPsych } = + await startTimeline([ + { + type: jsPsychImageClickResponse, + stimulus: "image.jpg", + }, + ]); + + const response_button = displayElement.querySelector( + "#image-click-response-button" + ) as HTMLButtonElement; + const svg_container = document.getElementById("image-click-response-svg") as HTMLElement; + + await clickTarget(svg_container); + await clickTarget(response_button); + + await expectFinished(); + + const data = getData().values()[0]; + console.log(data); + expect(data).toHaveProperty("points"); + expect(data.points.length).toBe(1); + expect(data.points[0].length).toBe(2); + }); +}); diff --git a/packages/plugin-image-click-response/src/index.ts b/packages/plugin-image-click-response/src/index.ts new file mode 100644 index 00000000..115dc191 --- /dev/null +++ b/packages/plugin-image-click-response/src/index.ts @@ -0,0 +1,179 @@ +import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych"; + +import { version } from "../package.json"; + +const info =Click the image to add a point. Click a point to remove
", + pretty_name: "Preamble", + }, + /** The image that needs to be displayed */ + stimulus: { + type: ParameterType.IMAGE, + default: undefined, + pretty_name: "Stimulus", + }, + /** Radius of the dot on the image */ + dot_radius: { + type: ParameterType.INT, + default: 5, + pretty_name: "Dot radius'", + }, + /** Color of the dot on the image */ + dot_color: { + type: ParameterType.STRING, + default: "lightblue", + pretty_name: "Dot color", + }, + /** Text for the continue button */ + button_label: { + type: ParameterType.STRING, + default: "Continue", + pretty_name: "Button label", + }, + /** How many dots are required before the trial can be completed */ + minimum_dots_required: { + type: ParameterType.INT, + default: -1, + pretty_name: "Minimum number of dots", + }, + }, + data: { + /** Image that was presented */ + stimulus: { + type: ParameterType.IMAGE, + }, + /** Time to complete the trial */ + rt: { + type: ParameterType.INT, + }, + /** Array of (x,y) values for each point in the image */ + points: { + type: ParameterType.COMPLEX, + }, + }, +}; + +type Info = typeof info; + +/** + * **image-click-response** + * + * This plugin shows an image on which the user can place points by clicking/touching the image. The location of each point is recorded as data. + * + * @author Christophe Bossens + * @see {@link https://github.com/jspsych/jspsych-contrib/packages/plugin-image-click-response/README.md}} + */ +class ImageClickResponsePlugin implements JsPsychPlugin