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

Add an option to show the correct answer with a reveal button. #982

Merged
merged 4 commits into from
Feb 7, 2024
Merged
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
50 changes: 45 additions & 5 deletions htdocs/js/Feedback/feedback.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
(() => {
const feedbackPopovers = [];

const initializeFeedback = (feedbackBtn) => {
if (feedbackBtn.dataset.popoverInitialized) return;
feedbackBtn.dataset.popoverInitialized = 'true';

new bootstrap.Popover(feedbackBtn, { sanitize: false, container: feedbackBtn.parentElement });
const feedbackPopover = new bootstrap.Popover(feedbackBtn, {
sanitize: false,
container: feedbackBtn.parentElement
});
feedbackPopovers.push(feedbackPopover);

// Render MathJax previews.
if (window.MathJax) {
Expand All @@ -13,18 +19,52 @@
}

feedbackBtn.addEventListener('shown.bs.popover', () => {
const bsPopover = bootstrap.Popover.getInstance(feedbackBtn);

// Execute javascript in the answer preview.
bsPopover.tip?.querySelectorAll('script').forEach((origScript) => {
feedbackPopover.tip?.querySelectorAll('script').forEach((origScript) => {
const newScript = document.createElement('script');
Array.from(origScript.attributes).forEach((attr) => newScript.setAttribute(attr.name, attr.value));
newScript.appendChild(document.createTextNode(origScript.innerHTML));
origScript.parentNode.replaceChild(newScript, origScript);
setTimeout(() => feedbackPopover.update());
});

const moveToFront = () => {
if (feedbackPopover.tip) feedbackPopover.tip.style.zIndex = 18;
for (const popover of feedbackPopovers) {
if (popover === feedbackPopover) continue;
popover.tip?.style.setProperty('z-index', null);
}
};
feedbackPopover.tip?.addEventListener('click', moveToFront);
feedbackPopover.tip?.addEventListener('focusin', moveToFront);
moveToFront();

// Make a click on the popover header close the popover.
bsPopover.tip?.querySelector('.popover-header')?.addEventListener('click', () => bsPopover?.hide());
feedbackPopover.tip
?.querySelector('.popover-header')
?.addEventListener('click', () => feedbackPopover.hide());

const revealCorrectBtn = feedbackPopover.tip?.querySelector('.reveal-correct-btn');
if (revealCorrectBtn && feedbackPopover.correctRevealed) {
revealCorrectBtn.nextElementSibling?.classList.remove('d-none');
revealCorrectBtn.remove();
} else {
revealCorrectBtn?.addEventListener('click', () => {
feedbackPopover.correctRevealed = true;
revealCorrectBtn.classList.add('fade-out');
revealCorrectBtn.parentElement.classList.add('resize-transition');
revealCorrectBtn.parentElement.style.maxWidth = `${revealCorrectBtn.parentElement.offsetWidth}px`;
revealCorrectBtn.parentElement.style.maxHeight = `${revealCorrectBtn.parentElement.offsetHeight}px`;
revealCorrectBtn.addEventListener('animationend', () => {
revealCorrectBtn.nextElementSibling?.classList.remove('d-none');
revealCorrectBtn.nextElementSibling?.classList.add('fade-in');
revealCorrectBtn.parentElement.style.maxWidth = '1000px';
revealCorrectBtn.parentElement.style.maxHeight = '1000px';
revealCorrectBtn.remove();
feedbackPopover.update();
});
});
}
});
};

Expand Down
1 change: 1 addition & 0 deletions htdocs/js/GraphTool/graphtool.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ window.graphTool = (containerId, options) => {
const gt = {};

gt.graphContainer = document.getElementById(containerId);
if (!gt.graphContainer) return;
if (gt.graphContainer.offsetWidth === 0) {
setTimeout(() => window.graphTool(containerId, options), 100);
return;
Expand Down
35 changes: 34 additions & 1 deletion htdocs/js/Problem/problem.scss
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@
--bs-popover-body-padding-x: 0;
--bs-popover-body-padding-y: 0;
--bs-popover-max-width: 600px;
--bs-popover-zindex: 18;
--bs-popover-zindex: 17;
position: absolute;
min-width: 200px;

Expand Down Expand Up @@ -288,6 +288,39 @@
border-bottom-right-radius: var(--bs-card-inner-border-radius);
}
}

&.resize-transition {
overflow: clip;
transition: max-height 1s ease-in, max-width 1s ease-in;
}

.fade-out {
animation: fade-out 0.25s ease-in;

@keyframes fade-out {
0% {
opacity: 1;
}

100% {
opacity: 0;
}
}
}

.fade-in {
animation: fade-in 0.5s ease-in;

@keyframes fade-in {
0% {
opacity: 0;
}

100% {
opacity: 1;
}
}
}
}
}
}
Expand Down
7 changes: 5 additions & 2 deletions lib/WeBWorK/PG.pm
Original file line number Diff line number Diff line change
Expand Up @@ -513,9 +513,12 @@ The summary will also be generated if this is true.

Determines if any messages generated in answer evaluation will be shown.

=item showCorrectAnswers (boolean, default: 0)
=item showCorrectAnswers (numeric, default: 0)

Determines if correct answers will be shown.
Determines if correct answers will be shown. If 0, then correct answers are not
shown. If set to 1, then correct answers are shown but hidden, and a "Reveal"
button is shown at first. If that button is clicked, then the answer is shown.
If set to 2, then correct answers are shown immediately.

=item answerPrefix (string, default: '')

Expand Down
31 changes: 24 additions & 7 deletions macros/PG.pl
Original file line number Diff line number Diff line change
Expand Up @@ -902,8 +902,10 @@ =head2 ENDDOCUMENT
=item *

C<showCorrect>: This is a boolean value that is 1 by default. If this is true
and the translator option C<showCorrectAnswers> is also true, then a preview of
the correct answer is shown in the feedback popover.
and the translator option C<showCorrectAnswers> is nonzero, then a preview of
the correct answer is shown in the feedback popover. In other words, this option
prevents showing correct answers even if the frontend requests that correct
answers be shown.

=item *

Expand Down Expand Up @@ -1230,14 +1232,29 @@ sub ENDDOCUMENT {
)
. (
$rh_envir->{showCorrectAnswers} && $options{showCorrect}
? feedbackLine(
maketext('Correct Answer'),
previewAnswer(
? do {
my $correctAnswer = previewAnswer(
$ansHash->{correct_ans_latex_string},
$options{wrapPreviewInTex},
$ansHash->{correct_ans}
)
)
);
feedbackLine(
maketext('Correct Answer'),
$rh_envir->{showCorrectAnswers} > 1
? $correctAnswer
: Mojo::DOM->new_tag(
'button',
type => 'button',
class => 'reveal-correct-btn btn btn-secondary btn-sm',
maketext('Reveal')
)
. Mojo::DOM->new_tag(
'div',
class => 'd-none',
sub {$correctAnswer}
)
);
}
: ''
);
}
Expand Down
Loading