All files / src/utils/components/question-bank/question-display/drop-down-menu DropDownMenuViewOnly.jsx

96% Statements 24/25
93.33% Branches 14/15
100% Functions 5/5
95.83% Lines 23/24

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77                              7x 6x 6x 6x 6x         7x 9x 9x 9x   9x 9x 6x 6x 6x     9x                 7x 8x 8x 8x           8x 9x 9x     8x                                  
import { Box, Typography } from "@mui/material";
import { useTheme } from "@mui/material/styles";
import React, { memo } from "react";
import TinyMCETextEditorReadOnly from "@/utils/components/TinyMCETextEditorReadOnly";
import {
    inlineSelectBlankColors,
    questionCardSurfaceBg,
    questionItemNumberSx,
    questionStemTypographySx,
} from "@/utils/components/question-bank/question-display/utils/questionCardStyles";
import {
    extractAnswerContent,
    htmlContentEquals,
} from "@/utils/components/question-bank/question-display/utils/questionAnswerUtils";
 
const unwrapBlock = (html) => {
    const el = document.createElement("div");
    el.innerHTML = html;
    Eif (el.children.length === 1 && el.children[0].tagName === "P") {
        return el.children[0].innerHTML;
    }
    return html;
};
 
const buildSelectHTML = (index, showAnswers, blank, choices, theme) => {
    const answerContent = extractAnswerContent(blank);
    const isAnswer = showAnswers && answerContent;
    const colors = inlineSelectBlankColors(theme, isAnswer);
 
    let displayContent = "Select...";
    if (isAnswer) {
        const items = choices?.[index]?.items || [];
        const matched = items.find((item) => htmlContentEquals(item, answerContent));
        displayContent = matched ? unwrapBlock(matched) : unwrapBlock(answerContent);
    }
 
    return (
        `<span style="display:inline-block;padding:2px 8px;` +
        `border:1px solid ${colors.borderColor};border-radius:4px;background-color:${colors.backgroundColor};` +
        `vertical-align:middle;margin:0 4px;color:${colors.color};` +
        `white-space:nowrap;line-height:normal;">` +
        `${displayContent}</span>`
    );
};
 
const DropDownMenuViewOnly = ({ question, showAnswers, itemNumber }) => {
    const theme = useTheme();
    const cardBg = questionCardSurfaceBg(theme);
    let blankIndex = 0;
    // Modified by Allan Ninal — optional chaining here; the identical unguarded
    // dereference its twin DragDropViewOnly had. A question saved with no correct
    // answer threw on `.answers` and took the whole rendered LIST with it. The
    // choices side was already guarded (`choices?.[index]?.items`) — only the
    // answers side was not.
    const content = question.question.replace(/__blank__/g, () => {
        const idx = blankIndex++;
        return buildSelectHTML(idx, showAnswers, question.correctAnswer?.answers?.[idx], question.choices, theme);
    });
 
    return (
        <Box sx={{ display: "flex", flexDirection: "column", gap: 2, width: "100%" }}>
            <Box sx={{ display: "flex", alignItems: "flex-start", gap: 1 }}>
                {itemNumber != null && itemNumber !== false && (
                    <Typography component="span" sx={{ ...questionItemNumberSx, flexShrink: 0 }}>
                        {itemNumber}.
                    </Typography>
                )}
                <Box sx={{ flex: 1, ...questionStemTypographySx }}>
                    <TinyMCETextEditorReadOnly content={content} backgroundColor={cardBg} />
                </Box>
            </Box>
        </Box>
    );
};
 
export default memo(DropDownMenuViewOnly);