From 7a6d8303f93b2dcf9a13ee82add2e0640a956baa Mon Sep 17 00:00:00 2001 From: Caden Buckhalt Date: Wed, 31 Jan 2024 15:17:30 -0800 Subject: [PATCH 1/4] fix: runtime errors --- .../containers/Interfaces/DyadCensus/DyadCensus.js | 2 +- .../containers/Interfaces/DyadCensus/useEdgeState.js | 5 ++++- .../Interfaces/TieStrengthCensus/TieStrengthCensus.js | 7 ++++--- .../Interfaces/TieStrengthCensus/useEdgeState.js | 2 +- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js b/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js index 68bd0c3a..8fb4ffdf 100644 --- a/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js +++ b/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js @@ -206,7 +206,7 @@ const DyadCensus = ({ >
diff --git a/lib/interviewer/containers/Interfaces/DyadCensus/useEdgeState.js b/lib/interviewer/containers/Interfaces/DyadCensus/useEdgeState.js index f34cb4a7..a9ec3db6 100644 --- a/lib/interviewer/containers/Interfaces/DyadCensus/useEdgeState.js +++ b/lib/interviewer/containers/Interfaces/DyadCensus/useEdgeState.js @@ -28,7 +28,7 @@ export const matchEntry = (p === prompt && b === pair[0] && a === pair[1]); export const getIsPreviouslyAnsweredNo = (state, prompt, pair) => { - if (!state || pair.length !== 2) { + if (!Array.isArray(state) || pair.length !== 2) { return false; } @@ -43,6 +43,9 @@ export const getIsPreviouslyAnsweredNo = (state, prompt, pair) => { export const stageStateReducer = (state = [], { pair, prompt, value }) => { // Remove existing entry, if it exists, and add new one on the end + if (!Array.isArray(state) || pair.length !== 2) { + return false; + } const newState = [ ...state.filter((item) => !matchEntry(prompt, pair)(item)), [prompt, ...pair, value], diff --git a/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js b/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js index 9db402a2..aacec5c6 100644 --- a/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js +++ b/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js @@ -224,7 +224,7 @@ const TieStrengthCensus = (props) => { >
@@ -259,8 +259,9 @@ const TieStrengthCensus = (props) => { // Set the max width of the container based on the number of options // This prevents them getting too wide, but also ensures that they // expand to take up all available space. - maxWidth: `${(edgeVariableOptions.length + 1) * 20 + 3.6 - }rem`, + maxWidth: `${ + (edgeVariableOptions.length + 1) * 20 + 3.6 + }rem`, }} >
diff --git a/lib/interviewer/containers/Interfaces/TieStrengthCensus/useEdgeState.js b/lib/interviewer/containers/Interfaces/TieStrengthCensus/useEdgeState.js index 18e7087c..310aab91 100644 --- a/lib/interviewer/containers/Interfaces/TieStrengthCensus/useEdgeState.js +++ b/lib/interviewer/containers/Interfaces/TieStrengthCensus/useEdgeState.js @@ -35,7 +35,7 @@ export const matchEntry = (p === prompt && b === pair[0] && a === pair[1]); export const getIsPreviouslyAnsweredNo = (state, prompt, pair) => { - if (!state || pair.length !== 2) { + if (!Array.isArray(state) || pair.length !== 2) { return false; } From a22be8fcf29c0a378900aadfce5fca244b1108d7 Mon Sep 17 00:00:00 2001 From: Caden Buckhalt Date: Thu, 1 Feb 2024 14:46:39 -0800 Subject: [PATCH 2/4] fix: interfaces not progressing to next stage if there are no steps, dyad and tie strength should automatically allow progress to next stage even if there are additional prompts (because those will also not have steps). this functionality was removed with implementation of useNavigationHelpers. reimplemented using an optional prop options in moveForward and moveBackward --- .../Interfaces/DyadCensus/DyadCensus.js | 7 +- .../Interfaces/DyadCensus/useSteps.js | 98 ++++++++++--------- .../TieStrengthCensus/TieStrengthCensus.js | 17 ++-- lib/interviewer/hooks/useNavigationHelpers.ts | 14 ++- 4 files changed, 77 insertions(+), 59 deletions(-) diff --git a/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js b/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js index 8fb4ffdf..8cd9a336 100644 --- a/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js +++ b/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js @@ -89,7 +89,7 @@ const DyadCensus = ({ if (isIntroduction) { // If there are no steps, clicking next should advance the stage if (stepsState.totalSteps === 0) { - navigationActions.moveForward(); + navigationActions.moveForward({ forceChangeStage: true }); return; } @@ -132,6 +132,10 @@ const DyadCensus = ({ } if (stepsState.isStageStart) { + if (stepsState.totalSteps === 0) { + navigationActions.moveBackward({ forceChangeStage: true }); + return; + } navigationActions.moveBackward(); } @@ -146,6 +150,7 @@ const DyadCensus = ({ previousStep, stepsState.isStageStart, stepsState.isStart, + stepsState.totalSteps, ]); const beforeNext = useCallback( diff --git a/lib/interviewer/containers/Interfaces/DyadCensus/useSteps.js b/lib/interviewer/containers/Interfaces/DyadCensus/useSteps.js index 3e03c0d9..c0abcdf6 100644 --- a/lib/interviewer/containers/Interfaces/DyadCensus/useSteps.js +++ b/lib/interviewer/containers/Interfaces/DyadCensus/useSteps.js @@ -2,44 +2,44 @@ import { useState } from 'react'; /* given a map of steps, where are we given a specific 'total' step number */ const getSubStep = (steps, nextStep) => { - const [r] = steps.reduce(([result, target], step, index) => { - if (step > target && result === null) { - return [{ step: target, stage: index }]; - } - - if (step <= target) { - return [result, target - step]; - } - - return [result, target]; - }, [null, nextStep]); + const [r] = steps.reduce( + ([result, target], step, index) => { + if (step > target && result === null) { + return [{ step: target, stage: index }]; + } + + if (step <= target) { + return [result, target - step]; + } + + return [result, target]; + }, + [null, nextStep], + ); return r; }; // state reducer for steps state -const stateReducer = ({ - step, - substep, - stage, - direction, -}) => (state) => { - const progress = step > state.progress ? step : state.progress; - - return ({ - ...state, - step, - progress, - substep, - stage, - direction, - isCompletedStep: progress > step, - isStageStart: substep === 0, - isStageEnd: substep >= state.steps[stage] - 1, - isStart: step === 0, - isEnd: step >= state.totalSteps - 1, - }); -}; +const stateReducer = + ({ step, substep, stage, direction }) => + (state) => { + const progress = step > state.progress ? step : state.progress; + + return { + ...state, + step, + progress, + substep, + stage, + direction, + isCompletedStep: progress > step, + isStageStart: substep === 0, + isStageEnd: substep >= state.steps[stage] - 1, + isStart: step === 0, + isEnd: step >= state.totalSteps - 1, + }; + }; /** * Models 'substeps' in prompts, which allows us to keep track @@ -48,9 +48,7 @@ const stateReducer = ({ * * @param {array} steps - map of steps per prompt, e.g. [3, 2, 1] */ -const useSteps = ( - steps = [], -) => { +const useSteps = (steps = []) => { const totalSteps = steps.reduce((count, step) => count + step, 0); const initialValues = { @@ -77,12 +75,14 @@ const useSteps = ( const substep = getSubStep(steps, nextStep); - setState(stateReducer({ - step: nextStep, - substep: substep.step, - stage: substep.stage, - direction: 'forward', - })); + setState( + stateReducer({ + step: nextStep, + substep: substep.step, + stage: substep.stage, + direction: 'forward', + }), + ); }; const previous = () => { @@ -94,12 +94,14 @@ const useSteps = ( const substep = getSubStep(steps, nextStep); - setState(stateReducer({ - step: nextStep, - substep: substep.step, - stage: substep.stage, - direction: 'backward', - })); + setState( + stateReducer({ + step: nextStep, + substep: substep.step, + stage: substep.stage, + direction: 'backward', + }), + ); }; return [state, next, previous]; diff --git a/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js b/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js index aacec5c6..e6af7c7c 100644 --- a/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js +++ b/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js @@ -79,7 +79,7 @@ const TieStrengthCensus = (props) => { const pair = get(pairs, stepsState.substep, null); const [fromNode, toNode] = getNodePair(nodes, pair); - const navigateActions = useNavigationHelpers(); + const navigationActions = useNavigationHelpers(); // hasEdge: // - false: user denied @@ -104,7 +104,7 @@ const TieStrengthCensus = (props) => { if (isIntroduction) { // If there are no steps, clicking next should advance the stage if (stepsState.totalSteps === 0) { - navigateActions.moveForward(); + navigationActions.moveForward({ forceChangeStage: true }); return; } @@ -121,7 +121,7 @@ const TieStrengthCensus = (props) => { } if (stepsState.isStageEnd) { - navigateActions.moveForward(); + navigationActions.moveForward(); } if (stepsState.isEnd) { @@ -133,7 +133,7 @@ const TieStrengthCensus = (props) => { edgeVariableValue, hasEdge, isIntroduction, - navigateActions, + navigationActions, nextStep, stepsState.isEnd, stepsState.isStageEnd, @@ -150,7 +150,11 @@ const TieStrengthCensus = (props) => { } if (stepsState.isStageStart) { - navigateActions.moveBackward(); + if (stepsState.totalSteps === 0) { + navigationActions.moveBackward({ forceChangeStage: true }); + return; + } + navigationActions.moveBackward(); } if (stepsState.isStart) { @@ -160,10 +164,11 @@ const TieStrengthCensus = (props) => { previousStep(); }, [ isIntroduction, - navigateActions, + navigationActions, previousStep, stepsState.isStageStart, stepsState.isStart, + stepsState.totalSteps, ]); const beforeNext = useCallback( diff --git a/lib/interviewer/hooks/useNavigationHelpers.ts b/lib/interviewer/hooks/useNavigationHelpers.ts index 6defffff..24bc7012 100644 --- a/lib/interviewer/hooks/useNavigationHelpers.ts +++ b/lib/interviewer/hooks/useNavigationHelpers.ts @@ -10,6 +10,10 @@ import { parseAsInteger, useQueryState } from 'nuqs'; export type directions = 'forwards' | 'backwards'; +type NavigationOptions = { + forceChangeStage?: boolean; +}; + export const useNavigationHelpers = () => { const dispatch = useDispatch(); const skipMap = useSelector(getSkipMap); @@ -94,12 +98,13 @@ export const useNavigationHelpers = () => { [beforeNextFunction], ); - const moveForward = async () => { + const moveForward = async (options?: NavigationOptions) => { if (!(await checkCanNavigate('forwards'))) { return; } - if (isLastPrompt) { + // forceChangeStage used in Dyad Census and Tie Strength Census when there are no steps + if (isLastPrompt || options?.forceChangeStage) { const nextStage = calculateNextStage(); void setCurrentStage(nextStage); return; @@ -110,12 +115,13 @@ export const useNavigationHelpers = () => { ); }; - const moveBackward = async () => { + const moveBackward = async (options?: NavigationOptions) => { if (!(await checkCanNavigate('backwards'))) { return; } - if (isFirstPrompt) { + // forceChangeStage used in Dyad Census and Tie Strength Census when there are no steps + if (isFirstPrompt || options?.forceChangeStage) { const previousStage = calculatePreviousStage(); void setCurrentStage(previousStage); return; From a13e68f12ea8e6ddef8bf0bb37d4dc896042ff03 Mon Sep 17 00:00:00 2001 From: Caden Buckhalt Date: Thu, 1 Feb 2024 14:51:39 -0800 Subject: [PATCH 3/4] fix: navigating forward when back was clicked --- lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js | 3 ++- .../Interfaces/TieStrengthCensus/TieStrengthCensus.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js b/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js index 8cd9a336..1bbb1efc 100644 --- a/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js +++ b/lib/interviewer/containers/Interfaces/DyadCensus/DyadCensus.js @@ -157,8 +157,9 @@ const DyadCensus = ({ (direction) => { if (direction === 'backwards') { back(); + } else { + next(); } - next(); return false; }, diff --git a/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js b/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js index e6af7c7c..7e4acb9d 100644 --- a/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js +++ b/lib/interviewer/containers/Interfaces/TieStrengthCensus/TieStrengthCensus.js @@ -175,8 +175,9 @@ const TieStrengthCensus = (props) => { (direction) => { if (direction === 'backwards') { back(); + } else { + next(); } - next(); return false; }, From d07a12e3eb4da249898c6683cb9bc224403d284b Mon Sep 17 00:00:00 2001 From: Caden Buckhalt Date: Thu, 1 Feb 2024 14:54:13 -0800 Subject: [PATCH 4/4] fix: runtime error by checking if state is an array before filtering --- .../containers/Interfaces/TieStrengthCensus/useEdgeState.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/interviewer/containers/Interfaces/TieStrengthCensus/useEdgeState.js b/lib/interviewer/containers/Interfaces/TieStrengthCensus/useEdgeState.js index 310aab91..9a9b23c7 100644 --- a/lib/interviewer/containers/Interfaces/TieStrengthCensus/useEdgeState.js +++ b/lib/interviewer/containers/Interfaces/TieStrengthCensus/useEdgeState.js @@ -50,6 +50,9 @@ export const getIsPreviouslyAnsweredNo = (state, prompt, pair) => { export const stageStateReducer = (state = [], { pair, prompt, value }) => { // Remove existing entry, if it exists, and add new one on the end + if (!Array.isArray(state) || pair.length !== 2) { + return false; + } const newState = [ ...state.filter((item) => !matchEntry(prompt, pair)(item)), [prompt, ...pair, value],