Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Millie stagetimer mock #127

Open
wants to merge 6 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions @empirica-mocks/core/mocks.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { isFunctionDeclaration } from "typescript";

// file is in deliberation-empirica/client/node_modules/@empirica/core/mocks.js
import { StageContext } from "../../src/app/editor/stageContext"
import { TimerContext } from "../../src/app/editor/timerContext"
// "../../../../src/app/editor/stageContext"


Expand Down Expand Up @@ -53,8 +54,9 @@ export function useGame() {

export function useStageTimer() {
const stage = useContext(StageContext);
const timer = useContext(TimerContext);
console.log("useStageTimerMock", stage)
console.log("StageElapsed", stage.elapsed)
console.log("useStageTimerMock", timer)


// This is a mock function that returns a mock stage timer object
Expand Down Expand Up @@ -96,6 +98,7 @@ export function useStage() {
let elements = treatment.treatments[selectedTreatmentIndex]?.gameStages[currentStageIndex]?.elements;
if (Array.isArray(elements)) {
elements = elements.flatMap((element) => {
element.displayTime = element.displayTime ?? 0
if (element.template) {
return templatesMap.get(element.template);
}
Expand All @@ -105,7 +108,10 @@ export function useStage() {
elements = [];
}
console.log("revised elements", elements)
return elements;
return elements.map(element => ({
displayTime: 0, // default value
...element // spread the rest of the element properties
}));
} else if (varName === "discussion") {
return treatment.treatments[selectedTreatmentIndex]?.gameStages[currentStageIndex]?.discussion;
} else if (varName === "name") {
Expand Down
54 changes: 54 additions & 0 deletions cypress/e2e/mockTimer.cy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
describe('test spec', () => {
it('passes', () => {

// initial yaml code for treatment, including a survey element
let yamltreatment = `treatments: {enter}- name: mock_timer_test \n playerCount: 1 \ngameStages:{enter}- name: TestTemplateA\n duration: 60\nelements:\n - template: testA{enter}{home}`

cy.viewport(2000, 1000, { log: false });
cy.visit('http://localhost:3000/editor')
cy.typeInCodeEditor(`{command+a}{del}${yamltreatment}`) // equivalent to clear() in cypress

// verify initial text in editor

// text values from monaco-editor will include line numbers and no line breaks
// the yamltreatment variable has no line numbers and line breaks
// so right now comparison is only on the treatmentName
cy.containsInCodeEditor('mock_timer_test')
cy.get('[data-cy="yaml-save"]').realClick()
cy.wait(3000)

// Check that time-picker value is greater than 0
cy.get('[data-test="time-picker"]')
.invoke('val')
.then(Number)
.should('be.greaterThan', 0)

// Set new timer value
cy.get('[data-test="time-picker"]')
.invoke('val', 50)
.trigger('input');

// Verify that the timer value is bigger
cy.get('[data-test="time-picker"]')
.invoke('val')
.then(Number)
.should('be.greaterThan', 49);

cy.wait(3000)
cy.get('[data-test="time-picker"]')
.invoke('val')
.then(Number)
.should('be.greaterThan', 52);

// Set timer value to 60 and make sure it's capped at 60
cy.get('[data-test="time-picker"]')
.invoke('val', 60)
.trigger('input');

cy.wait(3000)
cy.get('[data-test="time-picker"]')
.invoke('val')
.then(Number)
.should('equal', 60);
})
})
54 changes: 31 additions & 23 deletions src/app/editor/components/RenderPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,9 @@ import './../../styles/player-classic.css'
import './../../styles/player.css'

import { StageContext } from '@/editor/stageContext'
import { TimerContext, TimerProvider } from '@/editor/timerContext'
import { Substitute } from 'styled-components/dist/types'

const StyleContext = createContext({})
const useStyle = () => useContext(StyleContext)

const Stage = dynamic(
() =>
import('./../../../.././deliberation-empirica/client/src/Stage.jsx').then(
Expand All @@ -26,13 +24,36 @@ const Stage = dynamic(
}
)

const StyledStage = () => {
const MemoizedStageContainer = React.memo(() => (
<div className="min-w-sm mx-auto aspect-video relative w-full">
<Stage />
</div>
))
MemoizedStageContainer.displayName = 'MemoizedStageContainer'

const TimerControls = React.memo(() => {
const { elapsed, setElapsed } = useContext(TimerContext);
const { currentStageIndex, treatment, selectedTreatmentIndex } = useContext(StageContext);

// Derive maxValue from the stage configuration if available.
const maxValue = treatment?.treatments?.[selectedTreatmentIndex]?.gameStages?.[currentStageIndex]?.duration ?? 0;

return (
<div className="min-w-sm mx-auto aspect-video relative w-full">
<Stage />
<div className="min-w-fit">
<h1>Preview of Stage {currentStageIndex}</h1>
<TimePicker
value={elapsed}
setValue={setElapsed}
maxValue={maxValue}
/>
<ReferenceData
treatment={treatment?.treatments?.[selectedTreatmentIndex]}
stageIndex={currentStageIndex}
/>
</div>
)
}
);
})
TimerControls.displayName = 'TimerControls'

export function RenderPanel() {
const [time, setTime] = useState(0)
Expand Down Expand Up @@ -78,20 +99,7 @@ export function RenderPanel() {
)}
{currentStageIndex !== 'default' && (
<div className="min-w-fit">
<h1>Preview of Stage {currentStageIndex} </h1>
<TimePicker
value={time + ' s'}
setValue={setElapsed}
maxValue={
treatment.treatments?.[selectedTreatmentIndex].gameStages[currentStageIndex]
?.duration ?? 0
}
/>
<ReferenceData
treatment={treatment.treatments?.[selectedTreatmentIndex]}
stageIndex={currentStageIndex}
/>
{/* need to retrieve stage duration from treatment */}
<TimerControls/>
</div>
)}
{currentStageIndex !== 'default' && (
Expand All @@ -112,7 +120,7 @@ export function RenderPanel() {
</div> */}

<div className="w-full flex">
{currentStageIndex !== 'default' && <StyledStage />}
{currentStageIndex !== 'default' && <MemoizedStageContainer />}
</div>
</div>
)
Expand Down
19 changes: 13 additions & 6 deletions src/app/editor/components/TimePicker.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import "react";
import { useState } from "react";
import React, { useState, useContext } from "react";
import { TimerContext } from "../timerContext";

export default function TimePicker({
value,
setValue,
Expand All @@ -9,18 +11,23 @@ export default function TimePicker({
setValue: any;
maxValue: any;
}) {
const { elapsed, setElapsed } = useContext(TimerContext);
const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const newElapsed = Number(e.target.value);
setElapsed(newElapsed);
};
return (
<div>
<h3>Select a time:</h3>
<input
<input data-test="time-picker"
className="range"
type="range"
min="0"
max={maxValue}
defaultValue="0"
onChange={(e) => setValue(e.target.value)}
value={value}
onInput={(e: React.ChangeEvent<HTMLInputElement>) => setValue(Number(e.target.value))}
/>
{value}
{Math.min(value, maxValue)} s
</div>
);
}
}
5 changes: 4 additions & 1 deletion src/app/editor/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { RenderPanel } from './components/RenderPanel'
import Timeline from './components/Timeline'

import { StageProvider } from './stageContext.jsx'
import { TimerProvider } from './timerContext.jsx'

const defaultStageContext = {
currentStageIndex: undefined,
Expand Down Expand Up @@ -45,7 +46,9 @@ export default function EditorPage({}) {
className="overflow-auto"
style={{ minHeight: 200, height: upperLeftHeight }}
>
<RenderPanel />
<TimerProvider>
<RenderPanel/>
</TimerProvider>
</div>

<DraggableSplitter dir="horizontal" {...timelineSeparatorProps} />
Expand Down
5 changes: 0 additions & 5 deletions src/app/editor/stageContext.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ const StageContext = createContext()

const StageProvider = ({ children }) => {
const [currentStageIndex, setCurrentStageIndex] = useState('default')
const [elapsed, setElapsed] = useState(0)
const [treatment, setTreatment] = useState(null)
const [templatesMap, setTemplatesMap] = useState(new Map())
const [selectedTreatmentIndex, setSelectedTreatmentIndex] = useState(0)
Expand All @@ -36,8 +35,6 @@ const StageProvider = ({ children }) => {
const contextValue = useMemo(() => ({
currentStageIndex,
setCurrentStageIndex,
elapsed,
setElapsed,
treatment,
setTreatment,
editTreatment,
Expand All @@ -51,8 +48,6 @@ const StageProvider = ({ children }) => {
}), [
currentStageIndex,
setCurrentStageIndex,
elapsed,
setElapsed,
treatment,
setTreatment,
editTreatment,
Expand Down
30 changes: 30 additions & 0 deletions src/app/editor/timerContext.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { createContext, useState, useEffect, useMemo } from 'react';
import { useStageTimer } from '@empirica/core/player/classic/react'

// Create TimerContext
export const TimerContext = createContext();

// TimerProvider component
export const TimerProvider = ({ children }) => {
const [elapsed, setElapsed] = useState(0);

useEffect(() => {
const start = Date.now() - elapsed * 1000; // Adjust start time based on current elapsed

const interval = setInterval(() => {
const secondsElapsed = Math.floor((Date.now() - start) / 1000);
setElapsed(secondsElapsed);
}, 1000);

return () => clearInterval(interval); // Cleanup interval on unmount
}, [elapsed]);

// Memoize the context value to prevent unnecessary re-creations
const value = useMemo(() => ({ elapsed, setElapsed }), [elapsed]);

return (
<TimerContext.Provider value={value}>
{children}
</TimerContext.Provider>
);
};
Loading