diff --git a/src/components/common/HeightObserver.tsx b/src/components/common/HeightObserver.tsx
deleted file mode 100644
index 9725c459..00000000
--- a/src/components/common/HeightObserver.tsx
+++ /dev/null
@@ -1,31 +0,0 @@
-import { useEffect, useRef } from 'react';
-
-type Props = {
- children: JSX.Element;
- onHeightChange: (newHeight: number) => void;
-};
-
-const HeightObserver = ({ children, onHeightChange }: Props) => {
- const containerRef = useRef(null);
-
- useEffect(() => {
- const observer = new ResizeObserver((entries) => {
- entries.forEach((entry) => {
- const { clientHeight } = entry.target;
- onHeightChange && onHeightChange(clientHeight);
- });
- });
-
- if (containerRef.current) {
- observer.observe(containerRef.current);
- }
-
- return () => {
- observer.disconnect();
- };
- }, [onHeightChange]);
-
- return
{children}
;
-};
-
-export default HeightObserver;
diff --git a/src/components/common/animations/ReorderAnimation.tsx b/src/components/common/animations/ReorderAnimation.tsx
deleted file mode 100644
index fa21304a..00000000
--- a/src/components/common/animations/ReorderAnimation.tsx
+++ /dev/null
@@ -1,141 +0,0 @@
-import { useTransition } from '@react-spring/web';
-
-import { useState } from 'react';
-import { animated } from 'react-spring';
-
-import { countKeysApparition } from '../../../utils/array';
-
-const ANIMATION_DURATION_MS = 350;
-
-type DataElementType = { elementType: number };
-
-export type TransitionData = {
- key: string;
- height?: number;
- marginBottom: number;
- y?: number;
- data: T;
-};
-
-type Props = {
- isAnimating: boolean;
- elements: TransitionData[];
- renderElement: (
- item: TransitionData,
- onHeightChange: (key: string, height: number) => void
- ) => JSX.Element;
-};
-
-const duplicatedKeys = (
- elements: TransitionData[]
-) =>
- Object.entries(countKeysApparition(elements, 'key'))
- .filter(([key, count]) => key && count > 1)
- .map(([key, _]) => key);
-
-const printDuplicatedKeysWarning = (
- elements: TransitionData[]
-) =>
- duplicatedKeys(elements).forEach((k) =>
- console.warn(
- `Be carefull, the key "${k}" is not unique ! This can cause issues with the animation.`
- )
- );
-
-export const ReorderAnimation = ({
- isAnimating,
- elements,
- renderElement,
-}: Props) => {
- const [totalHeight, setTotalHeight] = useState(0);
- // Tracks the heights of each element to recompute totalHeight when heights changed.
- // That means, if a element's height changed, the map is updated and he totalHeight
- // recomputed with all the heights, including the updated one.
- const elementHeights = new Map();
- // this variable is used to set the next element to the good y
- let transitionHeight = 0;
- const transitionAnimation = isAnimating
- ? {
- from: { height: 0, opacity: 0 },
- leave: { height: 0, opacity: 0 },
- enter: ({ y, height }: { y: number; height: number }) => ({
- y,
- height,
- opacity: 1,
- }),
- update: ({ y, height }: { y: number; height: number }) => ({
- y,
- height,
- }),
- }
- : {
- from: (_item: TransitionData, _: number) => ({
- opacity: 0,
- }),
- enter: { opacity: 1 },
- };
-
- const transitions = useTransition(
- elements.map((e) => {
- transitionHeight += e.marginBottom;
- return {
- ...e,
- y: transitionHeight - e.marginBottom,
- };
- }),
- {
- // the key must be unique and not change to keep the same component for the animation.
- key: (item: TransitionData) => `${item.key}`,
- ...transitionAnimation,
- config: { duration: ANIMATION_DURATION_MS },
- }
- );
-
- const handleElementHeight = (key: string, height: number) => {
- elementHeights.set(key, height);
- const element = elements.find((e) => e.key === key);
- if (element) {
- element.height = height;
- }
- setTotalHeight(
- Array.from(elementHeights.entries()).reduce(
- (acc, [k, h]) =>
- acc + h + (elements.find((e) => e.key === k)?.marginBottom ?? 0),
- 0
- )
- );
- };
-
- printDuplicatedKeysWarning(elements);
-
- return (
-
- {transitions(
- (style, item, _t, index) =>
- item.key && (
-
- {renderElement(item, handleElementHeight)}
-
- )
- )}
-
- );
-};
-
-export default ReorderAnimation;
diff --git a/src/components/play/PlayViewQuestionType.tsx b/src/components/play/PlayViewQuestionType.tsx
index c9c92445..c06899bf 100644
--- a/src/components/play/PlayViewQuestionType.tsx
+++ b/src/components/play/PlayViewQuestionType.tsx
@@ -97,7 +97,6 @@ export const PlayViewQuestionType = ({
}
showCorrection={showCorrection}
showCorrectness={showCorrectness}
- numberOfSubmit={numberOfSubmit}
numberOfRetry={numberOfRetry}
/>
);
diff --git a/src/components/play/multipleChoices/PlayMultipleChoices.tsx b/src/components/play/multipleChoices/PlayMultipleChoices.tsx
index 36ba54ee..6fe253ef 100644
--- a/src/components/play/multipleChoices/PlayMultipleChoices.tsx
+++ b/src/components/play/multipleChoices/PlayMultipleChoices.tsx
@@ -5,10 +5,6 @@ import { Stack, Typography } from '@mui/material';
import { buildMultipleChoiceHintPlayCy } from '../../../config/selectors';
import { QUIZ_TRANSLATIONS } from '../../../langs/constants';
-import HeightObserver from '../../common/HeightObserver';
-import ReorderAnimation, {
- TransitionData,
-} from '../../common/animations/ReorderAnimation';
import {
ChoiceState,
MultipleChoiceAppDataData,
@@ -17,9 +13,6 @@ import {
} from '../../types/types';
import ChoiceButton from './ChoiceButton';
-const DEFAULT_MARGIN = 10;
-const HINT_MARGIN = 10;
-
const computeChoiceState = (
{ value, isCorrect }: MultipleChoicesChoice,
userChoices: string[] | undefined,
@@ -43,29 +36,8 @@ const computeChoiceState = (
}
};
-const sectionTitles = [
- {
- title: QUIZ_TRANSLATIONS.MULTIPLE_CHOICE_SECTION_TITLE_CORRECT,
- state: ChoiceState.CORRECT,
- },
- {
- title: QUIZ_TRANSLATIONS.MULTIPLE_CHOICE_SECTION_TITLE_MISSING,
- state: ChoiceState.MISSING,
- },
- {
- title: QUIZ_TRANSLATIONS.MULTIPLE_CHOICE_SECTION_TITLE_INCORRECT,
- state: ChoiceState.INCORRECT,
- },
-
- {
- title: QUIZ_TRANSLATIONS.MULTIPLE_CHOICE_SECTION_TITLE_UNSELECTED,
- state: ChoiceState.UNSELECTED,
- },
-];
-
enum ElementType {
Answer,
- SectionTitle,
Hint,
}
@@ -74,44 +46,27 @@ type AnswerDataType = {
choice: MultipleChoicesChoice;
elementType: ElementType.Answer;
};
-type TitleDataType = { title: string; elementType: ElementType.SectionTitle };
type HintDataType = {
idx: number;
hint: string;
elementType: ElementType.Hint;
};
-type DataType = AnswerDataType | TitleDataType | HintDataType;
+type DataType = AnswerDataType | HintDataType;
+type DataTypes = DataType | DataType[];
const choiceToAnswer = (
choice: MultipleChoicesChoice,
- idx: number,
- marginBottom: number
-): TransitionData => ({
- key: `answer-${choice.value}-${idx}`,
- marginBottom,
- data: { idx, choice, elementType: ElementType.Answer },
-});
-
-const choiceToTitle = (title: string): TransitionData => ({
- key: `title-${title}`,
- marginBottom: DEFAULT_MARGIN,
- data: {
- title,
- elementType: ElementType.SectionTitle,
- },
+ idx: number
+): AnswerDataType => ({
+ idx,
+ choice,
+ elementType: ElementType.Answer,
});
-const choiceToHint = (
- choiceIdx: number,
- hint: string
-): TransitionData => ({
- key: `hint-${hint}-${choiceIdx}`,
- marginBottom: HINT_MARGIN,
- data: {
- hint,
- idx: choiceIdx,
- elementType: ElementType.Hint,
- },
+const choiceToHint = (choiceIdx: number, hint: string): HintDataType => ({
+ hint,
+ idx: choiceIdx,
+ elementType: ElementType.Hint,
});
const isChoiceSelected = (
@@ -122,8 +77,12 @@ const isChoiceSelected = (
export const showHint = (
isSelected: boolean,
showCorrectness: boolean,
- showCorrection: boolean
-) => showCorrection || (showCorrectness && isSelected);
+ showCorrection: boolean,
+ choiceState: ChoiceState
+) =>
+ isSelected &&
+ (showCorrectness || showCorrection) &&
+ choiceState === ChoiceState.INCORRECT;
type Props = {
choices: MultipleChoicesAppSettingData['choices'];
@@ -131,7 +90,6 @@ type Props = {
lastUserAnswer?: MultipleChoiceAppDataData;
showCorrection: boolean;
showCorrectness: boolean;
- numberOfSubmit: number;
numberOfRetry: number;
setResponse: (d: MultipleChoiceAppDataData['choices']) => void;
};
@@ -142,18 +100,16 @@ const PlayMultipleChoices = ({
lastUserAnswer,
showCorrection,
showCorrectness,
- numberOfSubmit,
numberOfRetry,
setResponse,
}: Props): JSX.Element => {
const { t } = useTranslation();
- const [elements, setElements] = useState[]>([]);
+ const [elements, setElements] = useState([]);
const isReadonly = showCorrection || showCorrectness;
const choiceStates = choices.map((choice) =>
computeChoiceState(choice, lastUserAnswer?.choices, showCorrection)
);
- const isAnimating = numberOfSubmit + numberOfRetry > 0;
const showError =
choiceStates.some(
(state) =>
@@ -163,45 +119,25 @@ const PlayMultipleChoices = ({
!showCorrection;
useEffect(() => {
- const answers = choices.map((c, idx) =>
- choiceToAnswer(c, idx, DEFAULT_MARGIN)
- );
+ const answers = choices.map((c, idx) => choiceToAnswer(c, idx));
// set the "gaming" view
if (!showCorrection && !showCorrectness) {
setElements(answers);
} else {
// set the "correctness" or "correction" view
setElements(
- sectionTitles.flatMap((sectionTitle, i) => {
- const sectionAnswers = choiceStates.some(
- (state) => sectionTitle.state === state
+ answers.map((answer, idx) => {
+ const hint = answer.choice.explanation;
+ const displayHint = showHint(
+ isChoiceSelected(response.choices, answer.choice),
+ showCorrectness,
+ showCorrection,
+ choiceStates[idx]
);
- if (!sectionAnswers) {
- return [];
- }
-
- return [
- choiceToTitle(t(sectionTitles[i].title)),
- ...answers
- .filter((_, idx) => sectionTitle.state === choiceStates[idx])
- .map((answer) => {
- const hint = answer.data.choice.explanation;
- const displayHint = showHint(
- isChoiceSelected(response.choices, answer.data.choice),
- showCorrectness,
- showCorrection
- );
-
- return displayHint && hint
- ? [
- { ...answer, marginBottom: 0 },
- choiceToHint(answer.data.idx, hint),
- ]
- : answer;
- })
- .flat(),
- ];
+ return displayHint && hint
+ ? [answer, choiceToHint(answer.idx, hint)]
+ : answer;
})
);
}
@@ -228,67 +164,47 @@ const PlayMultipleChoices = ({
}
};
- const renderElement = (
- item: TransitionData,
- onHeightChange: (key: string, height: number) => void
- ) => {
- let element: JSX.Element | undefined;
-
- switch (item.data.elementType) {
+ const renderElement = (el: DataType) => {
+ switch (el.elementType) {
case ElementType.Answer: {
- const isSelected = isChoiceSelected(response.choices, item.data.choice);
- element = (
+ const isSelected = isChoiceSelected(response.choices, el.choice);
+ return (
);
- break;
}
- case ElementType.SectionTitle:
- element = (
-
- {item.data.title}
-
- );
- break;
case ElementType.Hint:
- element = (
+ return (
- {item.data.hint}
+ {el.hint}
);
- break;
}
- return (
- onHeightChange(item.key, height)}
- >
- {element}
-
- );
+ return undefined;
};
return (
-
+
+ {elements.map((el) => {
+ // The element can be a button or an array with a button and a hint
+ if (Array.isArray(el)) {
+ return {el.map((e) => renderElement(e))};
+ }
+ return renderElement(el);
+ })}
+
{showError && (
{t(QUIZ_TRANSLATIONS.MULTIPLE_CHOICE_NOT_CORRECT)}