From cfdc61c932d533e87b659768925b9e245142351b Mon Sep 17 00:00:00 2001 From: Jaimos Skriletz Date: Sat, 13 Jun 2026 19:54:02 -0600 Subject: [PATCH] Feedback button changes. This changes the feedback buttons in two ways. 1) Use copies of the bootstrap classes to color buttons. This way the feedback colors don't get changed by theming on the client side. The themes can still modify the actual classes used to change the buttons if desired, but the feedback buttons themselves can be themed independently of other bootstrap buttons. 2) Create a new feedback class "unknown" to use when showing the results of a problem with `$showPartialCorrectAnswers=0`. Currently on problems with limited feedback, it can be unclear to the student that they actually submitted the answer vs previewed the answer. This is accomplished by marking the buttons as incorrect if no answer is correct (0% on the problem), and marking them as "unknown" which shows a yellow button with a question mark in a circle if the problem is partially correct. --- htdocs/js/Problem/problem.scss | 86 +++++++++++++++++++++++++++++++--- macros/PG.pl | 29 +++++++++--- macros/core/PGessaymacros.pl | 2 +- 3 files changed, 104 insertions(+), 13 deletions(-) diff --git a/htdocs/js/Problem/problem.scss b/htdocs/js/Problem/problem.scss index 518b15d5d..0858407ff 100644 --- a/htdocs/js/Problem/problem.scss +++ b/htdocs/js/Problem/problem.scss @@ -153,6 +153,10 @@ &.partially-correct::before { content: url("data:image/svg+xml,"); } + + &.unknown::before { + content: url("data:image/svg+xml,"); + } } &.with-message::before { @@ -163,7 +167,7 @@ left: 100%; border: 1px solid black; border-radius: 50%; - background-color: var(--bs-warning); + background-color: #ffc107; padding: 0.25rem; animation: flash-in 0.75s; @@ -181,6 +185,75 @@ } } + /* Feedback button colors. */ + .btn-correct { + --bs-btn-color: #fff; + --bs-btn-bg: #198754; + --bs-btn-border-color: #198754; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #157347; + --bs-btn-hover-border-color: #146c43; + --bs-btn-focus-shadow-rgb: 60, 153, 110; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #146c43; + --bs-btn-active-border-color: #13653f; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #198754; + --bs-btn-disabled-border-color: #198754; + } + + .btn-incorrect { + --bs-btn-color: #fff; + --bs-btn-bg: #dc3545; + --bs-btn-border-color: #dc3545; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #bb2d3b; + --bs-btn-hover-border-color: #b02a37; + --bs-btn-focus-shadow-rgb: 225, 83, 97; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #b02a37; + --bs-btn-active-border-color: #a52834; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #dc3545; + --bs-btn-disabled-border-color: #dc3545; + } + + .btn-partially-correct { + --bs-btn-color: #000; + --bs-btn-bg: #ffc107; + --bs-btn-border-color: #ffc107; + --bs-btn-hover-color: #000; + --bs-btn-hover-bg: #ffca2c; + --bs-btn-hover-border-color: #ffc720; + --bs-btn-focus-shadow-rgb: 217, 164, 6; + --bs-btn-active-color: #000; + --bs-btn-active-bg: #ffcd39; + --bs-btn-active-border-color: #ffc720; + --bs-btn-active-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + --bs-btn-disabled-color: #000; + --bs-btn-disabled-bg: #ffc107; + --bs-btn-disabled-border-color: #ffc107; + } + + .btn-preview { + --bs-btn-color: #fff; + --bs-btn-bg: #1a67ea; + --bs-btn-border-color: #1a67ea; + --bs-btn-hover-color: #fff; + --bs-btn-hover-bg: #1658c7; + --bs-btn-hover-border-color: #1552bb; + --bs-btn-focus-shadow-rgb: 60, 126, 237; + --bs-btn-active-color: #fff; + --bs-btn-active-bg: #1552bb; + --bs-btn-active-border-color: #144db0; + --bs-btn-active-shadow: inset 0 3px 5px #00000020; + --bs-btn-disabled-color: #fff; + --bs-btn-disabled-bg: #1a67ea; + --bs-btn-disabled-border-color: #1a67ea; + } + .radio-buttons-container, .checkboxes-container { max-width: calc(100% - 1rem - 25px); @@ -256,7 +329,7 @@ .popover-header { text-align: center; cursor: pointer; - --bs-popover-header-bg: var(--bs-info); + --bs-popover-header-bg: #1a67ea; --bs-popover-header-color: white; .btn-close { @@ -268,21 +341,22 @@ &.correct { .popover-header { - --bs-popover-header-bg: var(--bs-success); + --bs-popover-header-bg: #198754; --bs-popover-header-color: white; } } &.incorrect { .popover-header { - --bs-popover-header-bg: var(--bs-danger); + --bs-popover-header-bg: #dc3545; --bs-popover-header-color: white; } } - &.partially-correct { + &.partially-correct, + &.unknown { .popover-header { - --bs-popover-header-bg: var(--bs-warning); + --bs-popover-header-bg: #ffc107; --bs-popover-header-color: black; } } diff --git a/macros/PG.pl b/macros/PG.pl index 316286057..3ff794a80 100644 --- a/macros/PG.pl +++ b/macros/PG.pl @@ -845,8 +845,12 @@ =head2 ENDDOCUMENT =item * -C: This is the bootstrap button class added to the feedback button. -By default it is "btn-info", "btn-success", "btn-danger", or "btn-warning" +C: This is the button class added to the feedback button. These are +based on bootstrap button styles, but are custom styles for the feedback +buttons to allow clients to theme the bootstrap buttons without changing the +feedback styles. By default it is "btn-preview" (coppied from btn-info), +"btn-correct" (coppied from btn-success), "btn-incorrect" (coppied from +btn-danger), or "btn-partially-correct" (coppied from btn-warning) depending on the status of the answer and the type of submission. =item * @@ -1059,7 +1063,7 @@ sub ENDDOCUMENT { my %options = ( resultTitle => maketext('Answer Preview'), resultClass => '', - btnClass => 'btn-info', + btnClass => 'btn-preview', btnAddClass => 'ms-2', feedbackElements => Mojo::Collection->new, insertElement => undef, @@ -1108,15 +1112,28 @@ sub ENDDOCUMENT { } elsif ($answerScore >= 1) { $options{resultTitle} = maketext('Correct'); $options{resultClass} = 'correct'; - $options{btnClass} = 'btn-success'; + $options{btnClass} = 'btn-correct'; } elsif ($answerScore == 0) { $options{resultTitle} = maketext('Incorrect'); $options{resultClass} = 'incorrect'; - $options{btnClass} = 'btn-danger'; + $options{btnClass} = 'btn-incorrect'; } else { $options{resultTitle} = maketext('[_1]% correct', round($answerScore * 100)); $options{resultClass} = 'partially-correct'; - $options{btnClass} = 'btn-warning'; + $options{btnClass} = 'btn-partially-correct'; + } + } elsif ($rh_envir->{showAttemptResults} && !$PG->{flags}{showPartialCorrectAnswers}) { + # Partially correct feedback is not being shown, but this is not a preview, so make the + # feedback look different than a preview. If the problem score is zero, everything is + # incorrect, so mark the whole problem as incorrect. Otherwise mark it as unknown correctness. + if ($problemResult->{score}) { + $options{resultTitle} = maketext('Unknown'); + $options{resultClass} = 'unknown'; + $options{btnClass} = 'btn-partially-correct'; + } else { + $options{resultTitle} = maketext('Incorrect'); + $options{resultClass} = 'incorrect'; + $options{btnClass} = 'btn-incorrect'; } } diff --git a/macros/core/PGessaymacros.pl b/macros/core/PGessaymacros.pl index 5d33fa7c3..2dcecea5b 100644 --- a/macros/core/PGessaymacros.pl +++ b/macros/core/PGessaymacros.pl @@ -67,7 +67,7 @@ sub essay_cmp { $options->{resultClass} = ''; $options->{insertMethod} = 'append_content'; - $options->{btnClass} = 'btn-info'; + $options->{btnClass} = 'btn-preview'; $options->{btnAddClass} = ''; $options->{wrapPreviewInTex} = 0; $options->{showEntered} = 0; # Suppress output of the feedback entered answer.