diff --git a/.changeset/new-shrimps-smoke.md b/.changeset/new-shrimps-smoke.md new file mode 100644 index 00000000..a8929461 --- /dev/null +++ b/.changeset/new-shrimps-smoke.md @@ -0,0 +1,5 @@ +--- +"@jspsych-contrib/plugin-html-keyboard-slider": major +--- + +Add HTML Keyboard Slider diff --git a/README.md b/README.md index 27811182..9c1247cf 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ Plugin/Extension | Contributor | Description [gamepad](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-gamepad/README.md) | [Shaobin Jiang](https://github.com/Shaobin-Jiang) | This plugin allows one to use gamepads in a jsPsych experiment. [html-choice](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-html-choice/README.md) | [Younes Strittmatter](https://github.com/younesStrittmatter) | This plugin displays clickable html elements that can be used to present a choice. [html-keyboard-response-raf](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-html-keyboard-response-raf/README.md) | [Josh de Leeuw](https://github.com/jodeleeuw) | This plugin displays an arbitrary HTML string and collects responses using the keyboard. It uses requestAnimationFrame for timing. +[html-keyboard-slider](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-html-keyboard-slider/docs/jspsych-html-keyboard-slider.md) | [Max Lovell](https://github.com/Max-Lovell) | Sliders which allow for keyboard responses. [html-multi-response](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-html-multi-response/README.md) | [Adam Richie-Halford](https://github.com/richford) | This plugin collects responses to an arbitrary HTML string using both button clicks and key presses. [html-swipe-response](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-html-swipe-response/README.md) | [Adam Richie-Halford](https://github.com/richford) | This plugin collects responses to an arbitrary HTML string using swipe gestures and keyboard responses. [html-vas-response](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-html-vas-response/README.md) | [Isaac Kinley](https://github.com/kinleyid) | This plugin collects responses to an arbitrary HTML string using a point-and-click visual analogue scale. @@ -42,6 +43,7 @@ Plugin/Extension | Contributor | Description [rok](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-rok/docs/jspsych-rok.md#jspsych-rok-plugin) | [Younes Strittmatter](https://github.com/younesStrittmatter) | This plugin displays a Random Object Kinematogram (ROK) and allows the subject to report the primary direction of motion or the primary orientation by pressing a key on the keyboard. [self-paced-reading](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-self-paced-reading/docs/jspsych-self-paced-reading.md) | [@igmmgi](https://github.com/igmmgi) | Self-paced reading tasks with different display options. [survey-number](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-survey-number/README.md) | [Josh de Leeuw](https://github.com/jodeleeuw) | This plugin displays a survey question and collects a numeric response. +[survey-slider](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-survey-slider/README.md) | [Max Lovell](https://github.com/Max-Lovell) & [Dominique Makowski](https://github.com/DominiqueMakowski) | Add several analogue scales on the same page for use in questionnaires. [vsl-animate-occlusion](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-vsl-animate-occlusion/docs/jspsych-vsl-animate-occlusion.md#jspsych-vsl-animate-occlusion-plugin) | [Josh de Leeuw](https://github.com/jodeleeuw) | The VSL (visual statistical learning) animate occlusion plugin displays an animated sequence of shapes that disappear behind an occluding rectangle while they change from one shape to another. [vsl-grid-scene](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-vsl-grid-scene/docs/jspsych-vsl-grid-scene.md#jspsych-vsl-grid-scene-plugin) | [Josh de Leeuw](https://github.com/jodeleeuw) | The VSL (visual statistical learning) grid scene plugin displays images arranged in a grid. diff --git a/package-lock.json b/package-lock.json index 09b483e5..629f0b81 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3139,6 +3139,10 @@ "resolved": "packages/plugin-html-keyboard-response-raf", "link": true }, + "node_modules/@jspsych-contrib/plugin-html-keyboard-slider": { + "resolved": "packages/plugin-html-keyboard-slider", + "link": true + }, "node_modules/@jspsych-contrib/plugin-html-multi-response": { "resolved": "packages/plugin-html-multi-response", "link": true @@ -17815,6 +17819,19 @@ "jspsych": ">=7.0.0" } }, + "packages/plugin-html-keyboard-slider": { + "name": "@jspsych-contrib/plugin-html-keyboard-slider", + "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-html-multi-response": { "name": "@jspsych-contrib/plugin-html-multi-response", "version": "1.0.2", diff --git a/packages/plugin-html-keyboard-slider/README.md b/packages/plugin-html-keyboard-slider/README.md new file mode 100644 index 00000000..293d2f0d --- /dev/null +++ b/packages/plugin-html-keyboard-slider/README.md @@ -0,0 +1,35 @@ +# html-keyboard-slider + +## Overview + +HTML slider which allows for keyboard responses + +## Loading + +### In browser + +```js + +``` + +### Via NPM + +``` +npm install @jspsych-contrib/plugin-html-keyboard-slider +``` + +```js +import jsPsychHtmlKeyboardSlider from '@jspsych-contrib/plugin-html-keyboard-slider'; +``` + +## Compatibility + +jsPsych 7.0.0 + +## Documentation + +See [documentation](https://github.com/jspsych/jspsych-contrib/blob/main/packages/plugin-html-keyboard-slider/docs/jspsych-html-keyboard-slider.md) + +## Author / Citation + +Max Lovell diff --git a/packages/plugin-html-keyboard-slider/docs/html-keyboard-slider.md b/packages/plugin-html-keyboard-slider/docs/html-keyboard-slider.md new file mode 100644 index 00000000..36f047ee --- /dev/null +++ b/packages/plugin-html-keyboard-slider/docs/html-keyboard-slider.md @@ -0,0 +1,198 @@ +# html-keyboard-slider + +HTML slider which allows for keyboard responses, with a few extra parameters. + +## 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 | +| ------------------- | ---------------- | ------------------ | ---------------------------------------- | +| min | INT | 0 | Slider minimum value. Can be an integer or a float. | +| max | INT | 10 | Slider maximum value. Can be an integer or a float. | +| step | INT | 1 | Minimum increase in value for the slider. | +| step_any | BOOL | false | For a more coninuous slider, set HTML Range input's step attribute to 'any', see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range#examples. Step values above still apply to 'increase_keys' and 'decrease_keys'. | +| slider_start | INT | null | Starting value of the slider. Defaults to minimum value. | +| slider_width | INT | null | Width of the slider in pixels. Defaults to 100% of the container. | +| minimum_keys | KEYS | ["`", "§"] | Keys that set the slider to its minimum value. Note '..._keys' parameters can take either single string or array, can include numbers, and either empty [] or '' turns relevant functionality off. Also are case sensitive (e.g. ['a', 'A']). | +| maximum_keys | KEYS | ["="] | Keys that set the slider to its maximum value. | +| decrease_keys | KEYS | ["ArrowLeft", "ArrowDown"] | Keys that decrease the slider by one step. | +| increase_keys | KEYS | ["ArrowRight", "ArrowUp"] | Keys that increase the slider by one step. | +| number_keys | BOOL | true | Whether or not to listen to number keys. | +| keys_step | FLOAT | null | Amount the increase and decrease keys change the slider value. Defaults to step size. | +| input_multiplier | INT | 1 | Multiplies the input value after key buffer is accounted for (e.g. 1=10, 2=20 on 0-100% scale)| +| key_buffer_on | BOOL | false | Tracks key presses over a specified time to allow multiple button presses. Handles '-' and '.' if not set to _keys params above. | +| key_buffer_timeout | INT | 300 | Length of time consecutive key presses are held in memory (ms). | +| prompt | HTML_STRING | "" | Prompt displayed above the slider. | +| ticks | BOOL | true | Whether to display ticks under each value of the slider. These are also slightly 'sticky'. | +| ticks_interval | FLOAT | null | Interval at which to display ticks. Defaults to step size. | +| labels | HTML_STRING | null | Labels displayed equidistantly below the stimulus. Accepts HTML. | +| label_dividers | BOOL | true | Whether to display dividing lines between labels. | +| display_value | BOOL | true | Whether to display the current value of the slider below it. | +| unit_text | STRING | "" | Text displayed next to display value (e.g., %, cm). | +| prepend_unit | BOOL | false | Whether to prepend the unit text (e.g., £5). Default is to append (e.g., 5%). | +| stimulus | HTML_STRING | null | Stimulus to be displayed. Any HTML is valid. | +| stimulus_duration | INT | null | Duration of stimulus (ms). | +| trial_duration | INT | null | Duration of trial (ms). Response recorded as null if no response is made. | +| response_ends_trial | BOOL | false | Whether a response ends the trial. | +| require_movement | BOOL | false | Whether the slider must be interacted with to continue. | +| button_label | STRING | "Continue" | Label of the button displayed - Note button is also clicked by 'Enter' key | + +## 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 | Description | +| -------------- | -------- | -------------------------------------- | +| response | INT | Final value of the slider. Defaults to slider starting value if not interacted with, or null if trial_duration ends first. | +| rt | FLOAT | Reaction time in milliseconds. | +| stimulus | HTML_STRING | Stimulus presented. | +| slider_start | INT | Starting value of the slider. | + +## 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-html-keyboard-slider +``` + +```js +import HtmlKeyboardSlider from '@jspsych-contrib/plugin-html-keyboard-slider'; +``` + +## Examples + +### Simple discreet/categorical slider + +Labelled discreet slider displaying defaults. Note pressing 'Enter' key also clicks the 'Continue' button. + +```javascript +var discreet = { + type: jsPsychHtmlKeyboardSlider, + min: 1, + max: 5, + prompt: "Rate your confidence in your response:", + labels: ["Pure guess", "More or less guessing", "Somewhat confident", "Almost sure", "Certain"], +} +``` + +### Percentage slider + +Percentage slider where pressing 1 goes to 10, unless you press 1 again within 300 ms, in which case goes to 11 + +```javascript +var percentage = { + type: jsPsychHtmlKeyboardSlider, + min: 0, + max: 100, + step: 1, + input_multiplier: 10, + slider_start: 50, + ticks: false, + key_buffer_on: true, + display_value: true, + unit_text: '%', + prompt: "Rate your confidence in your response: ", + //labels: ["Complete Guess", "Complete Certainty"], +} + +``` + +### Cost comparison + +This is more of a list of parameters to play around with + +```javascript +//https://www.freecodecamp.org/news/javascript-range-create-an-array-of-numbers-with-the-from-method/ +// function to create array of £ labels +var arrayRange = (start, stop, step) => + Array.from({ length: (stop - start) / step + 1 }, (value, index) => start + index * step +); + +var cost = { + type: jsPsychHtmlKeyboardSlider, + // Slider properties + min: -5, + max: 5, + step: 0.01, + step_any: false, // Generally don't worry about this one! + slider_start: 0, + slider_width: 700, + // Special input keys + number_keys: true, + minimum_keys: '[', // an empty array or string turns _keys functionality off + maximum_keys: ']', + decrease_keys: ['ArrowLeft','ArrowDown'], + increase_keys: ['ArrowRight','ArrowUp','+'], + keys_step: 1, + // Inputs extras + input_multiplier: 1, + key_buffer_on: true, + key_buffer_timeout: 700, + // Text + stimulus: '
', + ticks: true, + ticks_interval: 0.5, + prompt: "How much more/less expensive is the car on the left?", + //labels: arrayRange(-5, 5, 1).map(i => '£' + i), //add £ sign to front of array -5 to 5 + labels: ['£-5.00','','','','','£0.00','','','','','£5.00'], // Spacing can be handled this way too + label_dividers: false, + display_value: true, + unit_text: 'Difference in Value: £', + prepend_unit: true, + button_label: 'Submit', + // Meta-trial parameters + stimulus_duration: 3000, // After 3 seconds + trial_duration: 20000, // After 20 seconds + require_movement: true, + response_ends_trial: false, +} +``` + +### Visual scale labels + +2 happiness scale examples showing how to use other label types with HTML + +```javascript +var emojis = { + type: jsPsychHtmlKeyboardSlider, + min: -2, + max: 2, + slider_start: 0, + display_value: false, + ticks: false, + key_buffer_on: true, + display_value: true, + key_buffer_timeout: 300, + stimulus: '', + prompt: "How do you feel about this dog?", + labels: ["😭","😫","😐", "😃","😁"], +} + +var images = { + type: jsPsychHtmlKeyboardSlider, + min: -1, + max: 1, + slider_start: 0, + slider_width: 500, + display_value: false, + prompt: "How happy are you?", + labels: [ + "", + "", + "" + ], +} +``` \ No newline at end of file diff --git a/packages/plugin-html-keyboard-slider/examples/keyboard-slider-example.html b/packages/plugin-html-keyboard-slider/examples/keyboard-slider-example.html new file mode 100644 index 00000000..5414e560 --- /dev/null +++ b/packages/plugin-html-keyboard-slider/examples/keyboard-slider-example.html @@ -0,0 +1,133 @@ + + + + + + + + + + \ No newline at end of file diff --git a/packages/plugin-html-keyboard-slider/jest.config.cjs b/packages/plugin-html-keyboard-slider/jest.config.cjs new file mode 100644 index 00000000..6ac19d5c --- /dev/null +++ b/packages/plugin-html-keyboard-slider/jest.config.cjs @@ -0,0 +1 @@ +module.exports = require("@jspsych/config/jest").makePackageConfig(__dirname); diff --git a/packages/plugin-html-keyboard-slider/package.json b/packages/plugin-html-keyboard-slider/package.json new file mode 100644 index 00000000..eac2d065 --- /dev/null +++ b/packages/plugin-html-keyboard-slider/package.json @@ -0,0 +1,44 @@ +{ + "name": "@jspsych-contrib/plugin-html-keyboard-slider", + "version": "0.0.1", + "description": "HTML slider which allows for keyboard responses", + "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-html-keyboard-slider" + }, + "author": "Max Lovell", + "license": "MIT", + "bugs": { + "url": "https://github.com/jspsych/jspsych-contrib/issues" + }, + "homepage": "https://github.com/jspsych/jspsych-contrib/tree/main/packages/plugin-html-keyboard-slider", + "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-html-keyboard-slider/rollup.config.mjs b/packages/plugin-html-keyboard-slider/rollup.config.mjs new file mode 100644 index 00000000..87f70171 --- /dev/null +++ b/packages/plugin-html-keyboard-slider/rollup.config.mjs @@ -0,0 +1,3 @@ +import { makeRollupConfig } from "@jspsych/config/rollup"; + +export default makeRollupConfig("jsPsychHtmlKeyboardSlider"); diff --git a/packages/plugin-html-keyboard-slider/src/index.spec.ts b/packages/plugin-html-keyboard-slider/src/index.spec.ts new file mode 100644 index 00000000..6a079b98 --- /dev/null +++ b/packages/plugin-html-keyboard-slider/src/index.spec.ts @@ -0,0 +1,44 @@ +import { clickTarget, pressKey, startTimeline } from "@jspsych/test-utils"; + +import jsPsychHtmlKeyboardSlider from "."; + +jest.useFakeTimers(); + +describe("my plugin", () => { + it("should load and finish", async () => { + const { expectFinished, getHTML, getData, displayElement, jsPsych } = await startTimeline([ + { + type: jsPsychHtmlKeyboardSlider, + min: 1, + max: 5, + prompt: "Rate your confidence in your response:", + labels: [ + "Pure guess", + "More or less guessing", + "Somewhat confident", + "Almost sure", + "Certain", + ], + // Options Jest needs to end trial: + //trial_duration: 5000 + //require_movement: false, // Allow the trial to end without interaction + //response_ends_trial: true, // End the trial after a response + }, + ]); + + // Simulate key press + //pressKey('3'); + + // Click continue button + clickTarget(displayElement.querySelector("#keyboardSliderButton")); + + // Advance timers for setTimeout + //jest.runAllTimers(); + + await expectFinished(); + + // check the trial output + // const data = getData().values()[0]; + // expect(data).toBeDefined(); + }); +}); diff --git a/packages/plugin-html-keyboard-slider/src/index.ts b/packages/plugin-html-keyboard-slider/src/index.ts new file mode 100644 index 00000000..ccc36246 --- /dev/null +++ b/packages/plugin-html-keyboard-slider/src/index.ts @@ -0,0 +1,577 @@ +// Needs ability to map keys to values and functions using object array? e.g. add a different value to the step controlled by arrow keys +// Need to trim params down in future + +import { JsPsych, JsPsychPlugin, ParameterType, TrialType } from "jspsych"; +//Cannot find module '../package.json'. Consider using '--resolveJsonModule' to import module with '.json' extension.ts(2732)// +//import { version } from '../package.json'; + +const info =