diff --git a/public/assets/click.png b/public/assets/click.png
new file mode 100644
index 0000000..be0a3db
Binary files /dev/null and b/public/assets/click.png differ
diff --git a/src/components/Ending.tsx b/src/components/Ending.tsx
new file mode 100644
index 0000000..6e115f8
--- /dev/null
+++ b/src/components/Ending.tsx
@@ -0,0 +1,25 @@
+import { FC, useEffect } from "react";
+import { Box, Divider, Typography } from "@mui/material";
+
+export const Ending: FC = () => {
+ useEffect(() => {
+ sessionStorage.clear();
+ }, []);
+
+ return (
+ <>
+
+
+ Congratulations
+
+
+
+ You have completed the test. Thank you for participating.
+
+
+ You may now close the tab.
+
+
+ >
+ );
+};
diff --git a/src/components/GeneralDirection.tsx b/src/components/GeneralDirection.tsx
index 1ed20c8..2e4f7a3 100644
--- a/src/components/GeneralDirection.tsx
+++ b/src/components/GeneralDirection.tsx
@@ -1,20 +1,40 @@
import { FC, useContext } from "react";
-import { Button } from "@mui/material";
+import { Box, Button, Divider, Typography } from "@mui/material";
import { GeneralContext, Stage } from "../contexts/general.context";
export const GeneralDirection: FC = () => {
const cxt = useContext(GeneralContext);
const continueButtonHandler = () => {
- cxt?.setStage(Stage.SOUND_CHECK);
+ cxt?.setStage(Stage.TRANSITION);
};
return (
<>
-
General Direction Placeholder
-
+
+
+ Directions
+
+
+
+ Please do NOT use paper! Solve all questions in your mind. Click "Begin Test" to proceed.
+
+
+
+
+
>
);
};
diff --git a/src/components/SoundCheck.tsx b/src/components/SoundCheck.tsx
index 423fafe..5cecb49 100644
--- a/src/components/SoundCheck.tsx
+++ b/src/components/SoundCheck.tsx
@@ -1,4 +1,4 @@
-import { Box, Grid, IconButton, Typography } from "@mui/material";
+import { Box, Divider, Grid, IconButton, Typography } from "@mui/material";
import { FC, useContext, useEffect, useState } from "react";
import { soundCheckConfig as uiConfig } from "../config/ui.config";
import { GeneralContext, Stage } from "../contexts/general.context";
@@ -89,7 +89,7 @@ export const SoundCheck: FC = () => {
if (index === cxt?.soundCheckNumber) {
setTimeout(() => {
- cxt?.setStage(Stage.TRANSITION);
+ cxt?.setStage(Stage.GENERAL_DIRECTION);
}, 2000);
} else {
setTimeout(() => {
@@ -126,18 +126,19 @@ export const SoundCheck: FC = () => {
};
return (
-
+
Sound Check
-
+
+
If you can hear this message, click the announced number.
-
+
Otherwise, please increase your speaks volume.
-
+
{Array.from({ length: 3 }).map((_, rowIndex) => (
{Array.from({ length: 3 }).map((_, colIndex) => {
diff --git a/src/components/Transition.tsx b/src/components/Transition.tsx
index 5662765..1406eb1 100644
--- a/src/components/Transition.tsx
+++ b/src/components/Transition.tsx
@@ -1,8 +1,9 @@
-import { FC, useContext } from "react";
+import { FC, useContext, useState } from "react";
import { GeneralContext } from "../contexts/general.context";
import { generalConfig as testConfig } from "../config/test.config";
import { ProgressTracker } from "./ProgressTracker";
-import { Button } from "@mui/material";
+import { Box, Button } from "@mui/material";
+import { InstructionContainer, instructionComponents } from "./instructions/InstructionContainer";
interface TransitionProps {
handleTransition: () => void;
@@ -11,12 +12,42 @@ interface TransitionProps {
export const Transition: FC = ({ handleTransition }) => {
const cxt = useContext(GeneralContext);
+ const [displayInstructions, setDisplayInstructions] = useState(false);
+
return (
<>
- phase === cxt!.testPhase)} />
-
+ {!displayInstructions && (
+
+ phase === cxt!.testPhase)} />
+
+ )}
+ {displayInstructions && }
+
+ {instructionComponents[cxt!.testPhase]!.length <= 0 ? (
+
+ ) : (
+
+ )}
+
>
);
};
diff --git a/src/components/instructions/ChoiceReactionTime.tsx b/src/components/instructions/ChoiceReactionTime.tsx
new file mode 100644
index 0000000..51936b7
--- /dev/null
+++ b/src/components/instructions/ChoiceReactionTime.tsx
@@ -0,0 +1,72 @@
+import { Box, Typography } from "@mui/material";
+import { FC } from "react";
+import { choiceReactionTimeConfig as uiConfig } from "../../config/ui.config";
+import { generalConfig as testConfig } from "../../config/test.config";
+
+const color0 = uiConfig.choiceColor.color0;
+const color1 = uiConfig.choiceColor.color1;
+
+interface CRTVisualProps {
+ symbols: string[];
+ colors: string[];
+}
+
+const CRTVisual: FC = ({ symbols, colors }) => (
+
+ {symbols.map((symbol, index) => (
+
+
+ {symbol}
+
+
+ ))}
+
+);
+
+export const crtInstructions: JSX.Element[] = [
+ <>
+ ", "<", ">"]} colors={[color0, color1, color1]} />
+
+ Look at the 3 colored squares.
+
+
+ Two squares are the same color, one is different (ODD-COLOR).
+
+ >,
+ <>
+ ", "<", ">"]} colors={[color0, color1, color1]} />
+
+ Now look at the ARROW inside the ODD-COLOR.
+
+
+ When the odd-color arrow points right, tap the RIGHT-ARROW button at the{" "}
+ bottom of the screen with your right hand.
+
+ >,
+ <>
+ ", "<", "<"]} colors={[color0, color0, color1]} />
+
+ When the odd-color arrow points left, tap the LEFT-ARROW button at the{" "}
+ bottom of the screen with your left hand.
+
+ >,
+ <>
+ ", "<", "<"]} colors={[color0, color0, color1]} />
+
+ You will see {Object.keys(testConfig.choiceReactionTimeAns).length} sets of ARROWs. Please response as fast as you
+ can.
+
+ >,
+];
diff --git a/src/components/instructions/DigitSymbolMatching.tsx b/src/components/instructions/DigitSymbolMatching.tsx
new file mode 100644
index 0000000..274721e
--- /dev/null
+++ b/src/components/instructions/DigitSymbolMatching.tsx
@@ -0,0 +1,64 @@
+import { Divider, Grid, Typography } from "@mui/material";
+import { FC } from "react";
+import { Cell } from "../tests/DigitSymbolMatchingMain";
+import { digitSymbolConfig as testConfig } from "../../config/test.config";
+import { generalConfig } from "../../config/test.config";
+import { digitSymbolConfig as uiConfig } from "../../config/ui.config";
+
+const DSMVisual: FC = () => (
+
+ {testConfig.symbolPairs.map((symbol, index) => (
+
+
+
+
+
+ {symbol.num}
+
+ |
+
+ ))}
+
+);
+
+export const dsmInstructions: JSX.Element[] = [
+ <>
+
+
+ Each symbol has a number.
+
+ >,
+ <>
+
+
+
+ When a symbol appears at the top, press its number on the number pad at the{" "}
+ bottom of the screen (here it is 1).
+
+ >,
+ <>
+
+ You will see {Object.keys(generalConfig.digitSymbolAns).length} questions in this test.
+
+
+ Your score will be how many correct responeses you make, so try to be accurate and{" "}
+ quick!
+
+ >,
+];
diff --git a/src/components/instructions/InstructionContainer.tsx b/src/components/instructions/InstructionContainer.tsx
new file mode 100644
index 0000000..4b164eb
--- /dev/null
+++ b/src/components/instructions/InstructionContainer.tsx
@@ -0,0 +1,83 @@
+import { FC, useState } from "react";
+import { TestPhase } from "../../contexts/general.context";
+import { Box, Button, Divider, Typography } from "@mui/material";
+import { vpmInstructions, vprInstructions } from "./VisualPairs";
+import { crtInstructions } from "./ChoiceReactionTime";
+import { dsmInstructions } from "./DigitSymbolMatching";
+import { smInstructions } from "./SpatialMemory";
+import { mrdInstructions, mriInstructions } from "./MemoryRecall";
+
+const titleMapping: { [key in TestPhase]?: string } = {
+ [TestPhase.MEMORY_RECALL_IMMEDIATE]: "Memory - Immediate Recall",
+ [TestPhase.VISUAL_PAIRS_MEMORIZE]: "Visual Paired Associates - Learn",
+ [TestPhase.CHOICE_REACTION_TIME]: "Choice Reaction Time",
+ [TestPhase.VISUAL_PAIRS_RECALL]: "Visual Paired Associates - Test",
+ [TestPhase.DIGIT_SYMBOL_MATCHING]: "Digit Symbol Matching",
+ [TestPhase.SPATIAL_MEMORY]: "Spatial Memory",
+ [TestPhase.MEMORY_RECALL_DELAYED]: "Memory - Delayed Recall",
+};
+
+export const instructionComponents: { [key in TestPhase]?: JSX.Element[] } = {
+ [TestPhase.MEMORY_RECALL_IMMEDIATE]: mriInstructions,
+ [TestPhase.VISUAL_PAIRS_MEMORIZE]: vpmInstructions,
+ [TestPhase.CHOICE_REACTION_TIME]: crtInstructions,
+ [TestPhase.VISUAL_PAIRS_RECALL]: vprInstructions,
+ [TestPhase.DIGIT_SYMBOL_MATCHING]: dsmInstructions,
+ [TestPhase.SPATIAL_MEMORY]: smInstructions,
+ [TestPhase.MEMORY_RECALL_DELAYED]: mrdInstructions,
+};
+
+interface InstructionContainerProps {
+ phase: TestPhase;
+ handleTransition: () => void;
+}
+
+export const InstructionContainer: FC = ({ phase, handleTransition }) => {
+ const [instructionIdx, setInstructionIdx] = useState(0);
+
+ return (
+ <>
+
+
+ {titleMapping[phase]}
+
+
+ {instructionComponents[phase]![instructionIdx]}
+
+
+ {instructionIdx + 1 < instructionComponents[phase]!.length ? (
+
+ ) : (
+
+ )}
+
+ >
+ );
+};
diff --git a/src/components/instructions/MemoryRecall.tsx b/src/components/instructions/MemoryRecall.tsx
new file mode 100644
index 0000000..b7933dd
--- /dev/null
+++ b/src/components/instructions/MemoryRecall.tsx
@@ -0,0 +1,23 @@
+import { Typography } from "@mui/material";
+
+export const mriInstructions: JSX.Element[] = [
+ <>
+
+ You will need to play the audio and remember the five animals you hear. You need to memorize them
+ until the end of the test.
+
+ >,
+ <>
+
+ For this section, you will be asked to select the animals you heard from the list after you hear the audio.
+
+ >,
+];
+
+export const mrdInstructions: JSX.Element[] = [
+ <>
+
+ You have heard the names of five animals at the beginning of the test. Select those five animals.
+
+ >,
+];
diff --git a/src/components/instructions/SpatialMemory.tsx b/src/components/instructions/SpatialMemory.tsx
new file mode 100644
index 0000000..5c6367c
--- /dev/null
+++ b/src/components/instructions/SpatialMemory.tsx
@@ -0,0 +1,71 @@
+import { Grid, Typography } from "@mui/material";
+import { FC } from "react";
+import { Cell } from "../tests/SpatialMemoryMain";
+import { spatialMemoryConfig as testConfig } from "../../config/test.config";
+
+const exampleMatrix0 = [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 1, 0],
+ [0, 0, 0, 1],
+];
+
+const exampleMatrix1 = [
+ [1, 0, 0, 0],
+ [0, 1, 0, 0],
+ [0, 0, 0, 0],
+ [0, 0, 0, 1],
+];
+
+interface SMVisualProps {
+ matrix: number[][];
+ clickPosX?: number;
+ clickPosY?: number;
+}
+
+const SMVisual: FC = ({ matrix, clickPosX, clickPosY }) => (
+
+ {matrix.map((row: any[], rowIndex: number) => (
+
+ {row.map((cell: boolean, colIndex: number) => (
+
+ |
+ {colIndex === clickPosX && rowIndex === clickPosY && (
+
+ )}
+
+ ))}
+
+ ))}
+
+);
+
+export const smInstructions: JSX.Element[] = [
+ <>
+
+
+ Memorize the pattern on the grid
+
+ >,
+ <>
+
+
+ Fill in the blank squares to recreate the pattern.
+
+ >,
+];
diff --git a/src/components/instructions/VisualPairs.tsx b/src/components/instructions/VisualPairs.tsx
new file mode 100644
index 0000000..41e5ff5
--- /dev/null
+++ b/src/components/instructions/VisualPairs.tsx
@@ -0,0 +1,68 @@
+import { Box, Grid, Typography } from "@mui/material";
+import { generalConfig as testConfig } from "../../config/test.config";
+import { FC } from "react";
+
+const VPVisual0: FC = () => (
+
+
+
+
+);
+
+const VPVisual1: FC = () => (
+
+
+
+
+
+
+
+
+
+
+ {[3, 4].map((option, idx) => (
+
+
+
+ ))}
+
+
+ {[5, 6].map((option, idx) => (
+
+
+
+ ))}
+
+
+);
+
+export const vpmInstructions: JSX.Element[] = [
+ <>
+
+
+ You will see {Object.keys(testConfig.visualPairsAns).length} image pairs, like above. Learn which images go
+ together. Later you will be tested on that.
+
+ >,
+];
+
+export const vprInstructions: JSX.Element[] = [
+ <>
+
+
+ Let's test your memory for the images you learned a few minutes ago.
+
+ >,
+ <>
+
+
+ You will need to select the image that goes together with the image with red outline at the{" "}
+ top left corner.
+
+ >,
+ <>
+
+ You will be asked to recall all {Object.keys(testConfig.visualPairsAns).length} pairs of images. Let's start!
+
+ >,
+];
diff --git a/src/components/tests/DigitSymbolMatchingMain.tsx b/src/components/tests/DigitSymbolMatchingMain.tsx
index 5ab1426..aca7c50 100644
--- a/src/components/tests/DigitSymbolMatchingMain.tsx
+++ b/src/components/tests/DigitSymbolMatchingMain.tsx
@@ -8,7 +8,7 @@ import { TestContext } from "../../contexts/test.context";
import { getNextTestPhase } from "../../utils/general.utils";
import { DigitSymbolMatchingResult } from "../../contexts/types/result.type";
-const Cell = styled(Box, {
+export const Cell = styled(Box, {
shouldForwardProp: (prop) => prop !== "leftBox" && prop !== "rightBox" && prop !== "middleBox",
})<{ leftBox?: boolean; rightBox?: boolean; middleBox?: boolean }>(({ leftBox, rightBox }) => ({
borderLeft: leftBox ? "2px solid black" : "1px solid black",
diff --git a/src/components/tests/MemoryRecallMain.tsx b/src/components/tests/MemoryRecallMain.tsx
index 41482f1..c8dadb1 100644
--- a/src/components/tests/MemoryRecallMain.tsx
+++ b/src/components/tests/MemoryRecallMain.tsx
@@ -154,18 +154,18 @@ export const MemoryRecallMain: FC = ({ phase, toTestPhase
top: 0,
left: 0,
right: 0,
- width: "80vw",
+ width: "85vw",
marginX: "auto",
marginY: 4,
}}
>
{!showInstruction && phase === TestPhase.MEMORY_RECALL_IMMEDIATE && (
-
+
Please memorize these five animals until the end of the test.
)}
{showInstruction && (
-
+
Select the five animals you have heard.
)}
@@ -176,7 +176,7 @@ export const MemoryRecallMain: FC = ({ phase, toTestPhase
)}
{showOptions && (
-
+
{Array.from({ length: 8 }).map((_, rowIndex) => (
{Array.from({ length: 2 }).map((_, colIndex) => {
diff --git a/src/components/tests/SpatialMemoryMain.tsx b/src/components/tests/SpatialMemoryMain.tsx
index 81ac8cf..33a7aaf 100644
--- a/src/components/tests/SpatialMemoryMain.tsx
+++ b/src/components/tests/SpatialMemoryMain.tsx
@@ -6,20 +6,25 @@ import { TestContext } from "../../contexts/test.context";
import { TestPhase } from "../../contexts/general.context";
import { SpatialMemoryResult } from "../../contexts/types/result.type";
-const Cell = styled(Box, {
+export const Cell = styled(Box, {
shouldForwardProp: (prop) => prop !== "topBox" && prop !== "bottomBox" && prop !== "leftBox" && prop !== "rightBox",
-})<{ topBox?: boolean; bottomBox?: boolean; leftBox?: boolean; rightBox?: boolean }>(
- ({ theme, topBox, bottomBox, leftBox, rightBox }) => ({
- width: uiConfig.cellSize,
- height: uiConfig.cellSize,
- backgroundColor: theme.palette.background.paper,
- borderTop: topBox ? "3px solid black" : "1px solid black",
- borderBottom: bottomBox ? "3px solid black" : "1px solid black",
- borderLeft: leftBox ? "3px solid black" : "1px solid black",
- borderRight: rightBox ? "3px solid black" : "1px solid black",
- transition: "background-color 0.2s ease",
- })
-);
+})<{
+ topBox?: boolean;
+ bottomBox?: boolean;
+ leftBox?: boolean;
+ rightBox?: boolean;
+ cellwidth?: string;
+ cellheight?: string;
+}>(({ theme, topBox, bottomBox, leftBox, rightBox, cellwidth, cellheight }) => ({
+ width: cellwidth ?? uiConfig.cellSize,
+ height: cellheight ?? uiConfig.cellSize,
+ backgroundColor: theme.palette.background.paper,
+ borderTop: topBox ? "3px solid black" : "1px solid black",
+ borderBottom: bottomBox ? "3px solid black" : "1px solid black",
+ borderLeft: leftBox ? "3px solid black" : "1px solid black",
+ borderRight: rightBox ? "3px solid black" : "1px solid black",
+ transition: "background-color 0.2s ease",
+}));
interface SpatialMemoryMainProps {
toTestPhase: (testPhase: TestPhase) => void;
@@ -130,7 +135,13 @@ export const SpatialMemoryMain: FC = ({ toTestPhase }) =
p: 1,
}}
>
-
diff --git a/src/config/test.config.ts b/src/config/test.config.ts
index 3b73b1f..ae8d1ad 100644
--- a/src/config/test.config.ts
+++ b/src/config/test.config.ts
@@ -23,7 +23,7 @@ export const generalConfig = {
digitSymbolAns: [3, 8, 4, 0, 7, 0, 3, 5, 8, 2],
choiceReactionTimeAns: [2, 2, 1, 1, 0, 2, 1, 0, 1, 0] as (0 | 1 | 2)[],
visualPairsAns: {
- example: [3, 1],
+ bar: [3, 1],
lobby: [1, 2],
underwater: [8, 5],
temple: [4, 6],
diff --git a/src/config/ui.config.ts b/src/config/ui.config.ts
index 644357a..b2d6727 100644
--- a/src/config/ui.config.ts
+++ b/src/config/ui.config.ts
@@ -51,7 +51,7 @@ export const spatialMemoryConfig = {
* @property textColor - Text color configuration
*/
export const memoryRecallConfig = {
- buttonWidth: "40vw",
+ buttonWidth: "42vw",
buttonHeight: "8vh",
fontSize: 25,
buttonColor: {
diff --git a/src/contexts/general.context.tsx b/src/contexts/general.context.tsx
index 328c283..125ef11 100644
--- a/src/contexts/general.context.tsx
+++ b/src/contexts/general.context.tsx
@@ -2,10 +2,11 @@ import { createContext, Dispatch, FC, ReactNode, SetStateAction, useState } from
export enum Stage {
NULL = 0,
- GENERAL_DIRECTION = 1,
- SOUND_CHECK = 2,
+ SOUND_CHECK = 1,
+ GENERAL_DIRECTION = 2,
TRANSITION = 3,
TEST = 4,
+ ENDING = 5,
}
export enum TestPhase {
diff --git a/src/index.css b/src/index.css
index f5f1d13..071d25d 100644
--- a/src/index.css
+++ b/src/index.css
@@ -52,7 +52,7 @@ button:hover {
}
button:focus,
button:focus-visible {
- outline: 4px auto -webkit-focus-ring-color;
+ outline: none;
}
@media (prefers-color-scheme: light) {
diff --git a/src/pages/TestPage.tsx b/src/pages/TestPage.tsx
index 7007bfe..8eba21d 100644
--- a/src/pages/TestPage.tsx
+++ b/src/pages/TestPage.tsx
@@ -11,6 +11,7 @@ import { SoundCheck } from "../components/SoundCheck";
import { GeneralDirection } from "../components/GeneralDirection";
import { useNavigate, useParams } from "react-router-dom";
import { generalConfig } from "../config/test.config";
+import { Ending } from "../components/Ending";
export const TestPage: FC = () => {
const { studyId } = useParams<{ studyId: string }>();
@@ -28,10 +29,10 @@ export const TestPage: FC = () => {
if (!sessionStorage.getItem("testPhase") || !sessionStorage.getItem("stage")) {
sessionStorage.setItem("testPhase", String(generalConfig.testOrder[0]));
- sessionStorage.setItem("stage", String(Stage.GENERAL_DIRECTION));
+ sessionStorage.setItem("stage", String(Stage.SOUND_CHECK));
sessionStorage.setItem("questionNumber", "0");
cxt!.setTestPhase(generalConfig.testOrder[0]);
- cxt!.setStage(Stage.GENERAL_DIRECTION);
+ cxt!.setStage(Stage.SOUND_CHECK);
} else {
cxt!.setTestPhase(Number(sessionStorage.getItem("testPhase")) as TestPhase);
cxt!.setStage(Number(sessionStorage.getItem("stage")) as Stage);
@@ -45,8 +46,18 @@ export const TestPage: FC = () => {
sessionStorage.setItem("results", "[]");
sessionStorage.setItem("testPhase", String(target));
- sessionStorage.setItem("stage", String(Stage.TRANSITION));
sessionStorage.setItem("questionNumber", "0");
+
+ if (target === TestPhase.FINISHED) {
+ sessionStorage.setItem("stage", String(Stage.ENDING));
+ cxt!.setTestPhase(target);
+ cxt!.setStage(Stage.ENDING);
+
+ return;
+ }
+
+ sessionStorage.setItem("stage", String(Stage.TRANSITION));
+
cxt!.setTestPhase(target);
cxt!.setStage(Stage.TRANSITION);
};
@@ -85,10 +96,11 @@ export const TestPage: FC = () => {
return (
<>
- {cxt?.stage === Stage.GENERAL_DIRECTION && }
{cxt?.stage === Stage.SOUND_CHECK && }
+ {cxt?.stage === Stage.GENERAL_DIRECTION && }
{cxt?.stage === Stage.TRANSITION && }
{cxt?.stage === Stage.TEST && }
+ {cxt?.stage === Stage.ENDING && }
>
);
};