From 3da14a26caada413bd88540fdccf6a23488482b6 Mon Sep 17 00:00:00 2001 From: Sadman Soumique Date: Wed, 19 Aug 2026 15:48:05 +0600 Subject: [PATCH 1/4] fix: update styles for puzzle question preview in QuestionPreviewModal --- .../components/curriculum/QuestionList.tsx | 1 + .../modals/QuestionPreviewModal.tsx | 1 + .../question-previews/PuzzlePreview.tsx | 23 +++++++++----- .../entries/course-builder/services/quiz.ts | 2 ++ .../fields/quiz/questions/FormPuzzle.tsx | 31 +++++++++++++++++++ assets/src/js/v3/shared/utils/quiz.ts | 4 +++ assets/src/js/v3/shared/utils/types.ts | 2 ++ 7 files changed, 56 insertions(+), 8 deletions(-) diff --git a/assets/src/js/v3/entries/course-builder/components/curriculum/QuestionList.tsx b/assets/src/js/v3/entries/course-builder/components/curriculum/QuestionList.tsx index 0b828516ec..ce9f5a210a 100644 --- a/assets/src/js/v3/entries/course-builder/components/curriculum/QuestionList.tsx +++ b/assets/src/js/v3/entries/course-builder/components/curriculum/QuestionList.tsx @@ -269,6 +269,7 @@ const QuestionList = ({ isEditing }: { isEditing: boolean }) => { }), ...(questionType === 'puzzle' && { puzzle_grid_size: 4, + enable_puzzle_answer_background: true, }), ...(questionType === 'coordinates' && { coordinates_axis_range: 10, diff --git a/assets/src/js/v3/entries/course-builder/components/modals/QuestionPreviewModal.tsx b/assets/src/js/v3/entries/course-builder/components/modals/QuestionPreviewModal.tsx index 950656029d..595a242a28 100644 --- a/assets/src/js/v3/entries/course-builder/components/modals/QuestionPreviewModal.tsx +++ b/assets/src/js/v3/entries/course-builder/components/modals/QuestionPreviewModal.tsx @@ -410,6 +410,7 @@ const renderQuestionPreview = (question: QuizQuestion) => { answers={question.question_answers} gridSize={question.question_settings.puzzle_grid_size} questionId={question.question_id} + showPuzzleAnswerBackground={question.question_settings?.enable_puzzle_answer_background ?? true} /> ); } diff --git a/assets/src/js/v3/entries/course-builder/components/modals/question-previews/PuzzlePreview.tsx b/assets/src/js/v3/entries/course-builder/components/modals/question-previews/PuzzlePreview.tsx index 60fd850175..f9547c4d65 100644 --- a/assets/src/js/v3/entries/course-builder/components/modals/question-previews/PuzzlePreview.tsx +++ b/assets/src/js/v3/entries/course-builder/components/modals/question-previews/PuzzlePreview.tsx @@ -17,13 +17,19 @@ interface PuzzlePreviewProps { gridSize?: number | string; /** Used for hidden input id parity with `puzzle.php` (`tutor-puzzle-state-{id}`). */ questionId: ID; + showPuzzleAnswerBackground?: boolean; } /** * Course Builder puzzle preview — loads Tutor Pro `puzzle-question.js` in the preview iframe * (same pattern as {@link DrawImagePreview}, {@link PinImagePreview}, {@link ScalePreview}). */ -const PuzzlePreview = ({ answers, gridSize: gridSizeProp, questionId }: PuzzlePreviewProps) => { +const PuzzlePreview = ({ + answers, + gridSize: gridSizeProp, + questionId, + showPuzzleAnswerBackground = true, +}: PuzzlePreviewProps) => { const answer = answers[0]; const imageUrl = answer?.image_url || answer?.answer_two_gap_match || ''; const gridSize = clampGridSize(gridSizeProp); @@ -91,11 +97,17 @@ const PuzzlePreview = ({ answers, gridSize: gridSizeProp, questionId }: PuzzlePr const instructionId = `tutor-puzzle-instruction-${questionId}`; const statusId = `tutor-puzzle-status-${questionId}`; const describedByIds = `${instructionId} ${statusId}`; + const wrapperClassName = [ + 'quiz-question-ans-choice-area tutor-mt-40 tutor-puzzle-question question-type-puzzle', + showPuzzleAnswerBackground ? '' : 'tutor-puzzle-question--no-answer-background', + ] + .filter(Boolean) + .join(' '); return (
- {__('Puzzle + {__('Puzzle
)} + + + ( + { + if (calculateQuizDataStatus(activeQuestionDataStatus, QuizDataStatus.UPDATE)) { + form.setValue( + resolvedQuestionDataStatusPath, + calculateQuizDataStatus(activeQuestionDataStatus, QuizDataStatus.UPDATE) as QuizDataStatus, + ); + } + }} + /> + )} + /> +
diff --git a/assets/src/js/v3/shared/utils/quiz.ts b/assets/src/js/v3/shared/utils/quiz.ts index dc086eb1bc..bcebe2b3c9 100644 --- a/assets/src/js/v3/shared/utils/quiz.ts +++ b/assets/src/js/v3/shared/utils/quiz.ts @@ -235,6 +235,10 @@ export const convertedQuestion = (question: Omit): if (rawGridSize !== undefined && rawGridSize !== null && !Number.isNaN(Number(rawGridSize))) { question.question_settings.puzzle_grid_size = Number(rawGridSize); } + question.question_settings.enable_puzzle_answer_background = + question.question_settings.enable_puzzle_answer_background === undefined + ? true + : !!Number(question.question_settings.enable_puzzle_answer_background); } if (question.question_type === 'coordinates') { const rawCoordinatesAxisRange = question.question_settings.coordinates_axis_range; diff --git a/assets/src/js/v3/shared/utils/types.ts b/assets/src/js/v3/shared/utils/types.ts index 0f56e3b012..5ac6db7a29 100644 --- a/assets/src/js/v3/shared/utils/types.ts +++ b/assets/src/js/v3/shared/utils/types.ts @@ -343,6 +343,7 @@ export interface QuizQuestion { is_image_matching: boolean; draw_image_threshold_percent?: number; puzzle_grid_size?: number; + enable_puzzle_answer_background?: boolean; coordinates_axis_range?: number; }; question_answers: QuizQuestionOption[]; @@ -360,6 +361,7 @@ export interface QuizQuestionsForPayload extends Omit Date: Thu, 20 Aug 2026 12:09:31 +0600 Subject: [PATCH 2/4] feat(quiz): add background switch functionality to FormPuzzle component --- .../fields/quiz/questions/FormPuzzle.tsx | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/assets/src/js/v3/shared/components/fields/quiz/questions/FormPuzzle.tsx b/assets/src/js/v3/shared/components/fields/quiz/questions/FormPuzzle.tsx index f0b40df0de..ae38f81d98 100644 --- a/assets/src/js/v3/shared/components/fields/quiz/questions/FormPuzzle.tsx +++ b/assets/src/js/v3/shared/components/fields/quiz/questions/FormPuzzle.tsx @@ -38,9 +38,16 @@ interface FormPuzzleProps extends FormControllerProps { >; gridSizeControllerProps?: FormControllerProps; gridSizePath?: string; + backgroundControllerProps?: FormControllerProps; } -const FormPuzzle = ({ field, activeQuestionIndex = 0, gridSizeControllerProps, gridSizePath }: FormPuzzleProps) => { +const FormPuzzle = ({ + field, + activeQuestionIndex = 0, + gridSizeControllerProps, + gridSizePath, + backgroundControllerProps, +}: FormPuzzleProps) => { const form = useFormContext(); const option = field.value; const resolvedGridSizePath = @@ -127,6 +134,22 @@ const FormPuzzle = ({ field, activeQuestionIndex = 0, gridSizeControllerProps, g return null; } + const renderBackgroundSwitch = (controllerProps: FormControllerProps) => ( + { + if (form && calculateQuizDataStatus(activeQuestionDataStatus, QuizDataStatus.UPDATE)) { + form.setValue( + resolvedQuestionDataStatusPath, + calculateQuizDataStatus(activeQuestionDataStatus, QuizDataStatus.UPDATE) as QuizDataStatus, + ); + } + }} + /> + ); + return (
@@ -163,7 +186,7 @@ const FormPuzzle = ({ field, activeQuestionIndex = 0, gridSizeControllerProps, g helpText={__('Larger grids create more pieces and a harder puzzle for learners.', __TUTOR_TEXT_DOMAIN__)} onChange={(selectedOption) => { gridSizeControllerProps.field.onChange(selectedOption.value); - if (calculateQuizDataStatus(activeQuestionDataStatus, QuizDataStatus.UPDATE)) { + if (form && calculateQuizDataStatus(activeQuestionDataStatus, QuizDataStatus.UPDATE)) { form.setValue( resolvedQuestionDataStatusPath, calculateQuizDataStatus(activeQuestionDataStatus, QuizDataStatus.UPDATE) as QuizDataStatus, @@ -202,29 +225,16 @@ const FormPuzzle = ({ field, activeQuestionIndex = 0, gridSizeControllerProps, g )} - ( - { - if (calculateQuizDataStatus(activeQuestionDataStatus, QuizDataStatus.UPDATE)) { - form.setValue( - resolvedQuestionDataStatusPath, - calculateQuizDataStatus(activeQuestionDataStatus, QuizDataStatus.UPDATE) as QuizDataStatus, - ); - } - }} - /> - )} - /> + {backgroundControllerProps ? ( + renderBackgroundSwitch(backgroundControllerProps) + ) : ( + + )}
From 4a70d80e85a8123678c575560fbc905942597454 Mon Sep 17 00:00:00 2001 From: Sadman Soumique Date: Thu, 20 Aug 2026 13:35:57 +0600 Subject: [PATCH 3/4] feat(quiz): enhance styles for draw and pin image questions in quiz layout --- .../scss/frontend/learning-area/_quiz.scss | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/assets/src/scss/frontend/learning-area/_quiz.scss b/assets/src/scss/frontend/learning-area/_quiz.scss index 4688d6fa32..16cce397a3 100644 --- a/assets/src/scss/frontend/learning-area/_quiz.scss +++ b/assets/src/scss/frontend/learning-area/_quiz.scss @@ -52,6 +52,30 @@ $tutor-quiz-content-bottom-offset: calc( overflow: auto; } + &[data-question-layout-view='single_question']:has(.tutor-quiz-question[data-question='draw_image']), + &[data-question-layout-view='single_question']:has(.tutor-quiz-question[data-question='pin_image']), + &[data-question-layout-view='question_pagination']:has(.tutor-quiz-question[data-question='draw_image']), + &[data-question-layout-view='question_pagination']:has(.tutor-quiz-question[data-question='pin_image']) { + .tutor-quiz-questions { + justify-content: flex-start; + overflow: hidden; + } + + .tutor-quiz-question-wrapper { + flex: 1 1 auto; + min-height: 0; + max-height: none; + overflow: hidden; + } + + .tutor-quiz-question { + height: 100%; + min-height: 0; + margin-block: 0; + overflow: hidden; + } + } + &[data-question-layout-view='single_question'] { &:has(.tutor-quiz-questions-pagination) { .tutor-quiz-question-wrapper { @@ -1104,6 +1128,50 @@ $tutor-quiz-content-bottom-offset: calc( } } + &-submission[data-question-layout-view='single_question'], + &-submission[data-question-layout-view='question_pagination'] { + [data-question='draw_image'], + [data-question='pin_image'] { + .tutor-quiz-question-header { + flex-shrink: 0; + } + + .tutor-draw-image-question, + .tutor-pin-image-question { + @include tutor-flex(column, center, center); + flex: 1 1 auto; + min-height: 0; + margin-top: 0; + width: 100%; + container-type: size; + } + + .tutor-draw-image-actions { + flex-shrink: 0; + align-self: stretch; + } + + .tutor-draw-image-wrapper, + .tutor-pin-image-wrapper { + position: relative; + flex: 0 1 auto; + min-height: 0; + max-height: 100%; + width: fit-content; + max-width: 100%; + + img { + display: block; + max-height: 100cqh; + max-width: 100cqw; + width: auto; + height: auto; + object-fit: contain; + } + } + } + } + &-footer { padding-block: $tutor-spacing-8; margin-top: auto; From 00b45e1bea420c3795746f5f3dd78f1e7ecb79dd Mon Sep 17 00:00:00 2001 From: Sadman Soumique Date: Thu, 20 Aug 2026 16:27:17 +0600 Subject: [PATCH 4/4] feat(quiz): improve styles for puzzle question layout in quiz component --- .../scss/frontend/learning-area/_quiz.scss | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/assets/src/scss/frontend/learning-area/_quiz.scss b/assets/src/scss/frontend/learning-area/_quiz.scss index 16cce397a3..19e65954c4 100644 --- a/assets/src/scss/frontend/learning-area/_quiz.scss +++ b/assets/src/scss/frontend/learning-area/_quiz.scss @@ -76,6 +76,32 @@ $tutor-quiz-content-bottom-offset: calc( } } + // Puzzle: card fills the viewport slot; board + scatter scroll inside the card body. + &[data-question-layout-view='single_question']:has(.tutor-quiz-question[data-question='puzzle']), + &[data-question-layout-view='question_pagination']:has(.tutor-quiz-question[data-question='puzzle']) { + .tutor-quiz-questions { + justify-content: flex-start; + overflow: hidden; + } + + .tutor-quiz-question-wrapper { + flex: 1 1 auto; + min-height: 0; + max-height: 100%; + overflow: hidden; + @include tutor-flex(column); + } + + .tutor-quiz-question { + flex: 1 1 auto; + min-height: 0; + max-height: 100%; + height: auto; + margin-block: 0; + overflow: hidden; + } + } + &[data-question-layout-view='single_question'] { &:has(.tutor-quiz-questions-pagination) { .tutor-quiz-question-wrapper { @@ -1170,6 +1196,28 @@ $tutor-quiz-content-bottom-offset: calc( } } } + + // Puzzle: card stays viewport-bound; board keeps natural size; scroll happens on the puzzle body. + [data-question='puzzle'] { + .tutor-quiz-question-header { + flex-shrink: 0; + } + + .tutor-puzzle-question { + @include tutor-flex(column, center, flex-start); + flex: 1 1 auto; + flex-grow: 1; + // Force flex item to honor parent height so overflow-y:auto creates a scrollport. + height: 0; + min-height: 0; + margin-top: 0; + width: 100%; + overflow-x: hidden; + overflow-y: auto; + overscroll-behavior: contain; + -webkit-overflow-scrolling: touch; + } + } } &-footer {