diff --git a/course/format/classes/output/local/content/cm/completion.php b/course/format/classes/output/local/content/cm/completion.php index 999452f894e..aac38a1aa66 100644 --- a/course/format/classes/output/local/content/cm/completion.php +++ b/course/format/classes/output/local/content/cm/completion.php @@ -102,16 +102,21 @@ class completion implements named_templatable, renderable { * @return array the completion dialog exported for template */ private function get_completion_dialog(\renderer_base $output, stdClass $completioninfo): array { + $dialogcontent = $output->render_from_template('core_courseformat/local/content/cm/completion_dialog', $completioninfo); + $buttoncontent = get_string('completionmenuitem', 'completion'); + $buttonclass = ''; + if ($completioninfo->istrackeduser) { $buttoncontent = get_string('todo', 'completion'); - } else { - $buttoncontent = get_string('completionmenuitem', 'completion'); + if ($completioninfo->overallcomplete) { + $buttoncontent = $output->pix_icon('i/checked', '') . " " . get_string('completion_manual:done', 'core_course'); + $buttonclass = 'btn-success'; + } } - $content = $output->render_from_template('core_courseformat/local/content/cm/completion_dialog', $completioninfo); - $completiondialog = new dropdown_dialog($buttoncontent, $content, [ + $completiondialog = new dropdown_dialog($buttoncontent, $dialogcontent, [ 'classes' => 'completion-dropdown', - 'buttonclasses' => 'btn btn-sm btn-outline-secondary dropdown-toggle', + 'buttonclasses' => 'btn btn-sm dropdown-toggle icon-no-margin ' . $buttonclass, 'dropdownposition' => dropdown_dialog::POSITION['end'], ]); diff --git a/course/format/templates/local/content/cm/activity.mustache b/course/format/templates/local/content/cm/activity.mustache index 259a1af6f7b..3f36b708b39 100644 --- a/course/format/templates/local/content/cm/activity.mustache +++ b/course/format/templates/local/content/cm/activity.mustache @@ -117,7 +117,7 @@ {{! Completion }} {{#activityinfo}} {{#hascompletion}} -
+
{{$ core_courseformat/local/content/cm/activity_info}} {{> core_courseformat/local/content/cm/activity_info}} {{/ core_courseformat/local/content/cm/activity_info}} diff --git a/course/format/tests/behat/behat_courseformat.php b/course/format/tests/behat/behat_courseformat.php new file mode 100644 index 00000000000..36a5798aab9 --- /dev/null +++ b/course/format/tests/behat/behat_courseformat.php @@ -0,0 +1,43 @@ +. + +declare(strict_types=1); + +// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. +require_once(__DIR__ . '/../../../../lib/behat/behat_base.php'); + +/** + * Behat step definitions for Course format + * + * @package core_courseformat + * @copyright 2023 Mikel Martín + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_courseformat extends behat_base { + + /** + * Return the list of partial named selectors + * + * @return behat_component_named_selector[] + */ + public static function get_partial_named_selectors(): array { + return [ + new behat_component_named_selector('Activity completion', [ + ".//*[@data-activityname=%locator%]//*[@data-region='completionrequirements']", + ]), + ]; + } +} diff --git a/course/format/tests/behat/coursepage_completion.feature b/course/format/tests/behat/coursepage_completion.feature new file mode 100644 index 00000000000..d973a91f633 --- /dev/null +++ b/course/format/tests/behat/coursepage_completion.feature @@ -0,0 +1,55 @@ +@core @core_course @core_courseformat @core_completion @javascript +Feature: Course page activities completion + In order to check activities completions + As a student + I need to see the activity completion criterias dropdown. + + Background: + Given the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student1@example.com | + And the following "courses" exist: + | shortname | fullname | enablecompletion | + | C1 | Course 1 | 1 | + And the following "activities" exist: + | activity | name | intro | course | idnumber | section | completion | completionview | + | assign | Activity sample 1 | Test assignment description | C1 | sample1 | 1 | 1 | 0 | + | assign | Activity sample 2 | Test assignment description | C1 | sample2 | 1 | 2 | 1 | + And the following "course enrolments" exist: + | user | course | role | + | student1 | C1 | student | + | teacher1 | C1 | editingteacher | + + Scenario: Teacher does not see manual completion button + When I am on the "C1" "Course" page logged in as "teacher1" + Then "Mark as done" "button" should not exist in the "Activity sample 1" "activity" + And I click on "Completion" "button" in the "Activity sample 1" "activity" + And I should see "Mark as done" in the "Activity sample 1" "core_courseformat > Activity completion" + + Scenario: Student should see the manual completion button + When I am on the "C1" "Course" page logged in as "student1" + Then the manual completion button for "Activity sample 1" should exist + And the manual completion button of "Activity sample 1" is displayed as "Mark as done" + And I toggle the manual completion state of "Activity sample 1" + And the manual completion button of "Activity sample 1" is displayed as "Done" + + Scenario: Teacher should see the automatic completion criterias of activities + When I am on the "C1" "Course" page logged in as "teacher1" + And I click on "Completion" "button" in the "Activity sample 2" "activity" + Then I should see "View" in the "Activity sample 2" "core_courseformat > Activity completion" + # After viewing the activity, the completion criteria dropdown should still display "Completion". + And I am on the "sample2" Activity page + And I am on the "Course 1" course page + And "Completion" "button" should exist in the "Activity sample 2" "activity" + + Scenario: Student should see the automatic completion criterias statuses of activities + When I am on the "C1" "Course" page logged in as "student1" + And I click on "To do" "button" in the "Activity sample 2" "activity" + Then I should see "View" in the "Activity sample 2" "core_courseformat > Activity completion" + # After viewing the activity, the completion criteria dropdown should display "Done" instead of "To do". + And I am on the "sample2" Activity page + And I am on the "Course 1" course page + And "To do" "button" should not exist in the "Activity sample 2" "activity" + And I click on "Done" "button" in the "Activity sample 2" "activity" + And I should see "View" in the "Activity sample 2" "core_courseformat > Activity completion" diff --git a/theme/boost/scss/moodle/course.scss b/theme/boost/scss/moodle/course.scss index 388a374a709..56c78d67eb4 100644 --- a/theme/boost/scss/moodle/course.scss +++ b/theme/boost/scss/moodle/course.scss @@ -1266,6 +1266,26 @@ $activity-item-hover: theme-color-level('primary', -12) !default; $activity-item-border-radius: 1rem !default; $activity-add-hover: theme-color-level('primary', -10) !default; +/* Functions/Mixins definition */ + +// Activity completion button custom styling mixin. +@mixin completion-button() { + &.btn { + border: $border-width solid $gray-400; + font-weight: bold; + @include border-radius(); + .icon { + font-size: inherit; + } + } + @each $color, $value in $theme-colors { + &.btn-#{$color} { + $bg-color: theme-color-level($color, $alert-bg-level); + @include alert-variant($bg-color, $bg-color, $value); + } + } +} + /* Activity & Block 'add' buttons */ .activity-add, @@ -1558,15 +1578,13 @@ $activity-add-hover: theme-color-level('primary', -10) !default; } } - .activity-info { + .activity-completion { grid-area: completion; justify-self: end; // Re-style completion buttons (mark as done & dropdown). button, a[role="button"] { - border: $border-width solid $border-color; - @include border-radius(); - font-weight: bold; + @include completion-button(); } @include media-breakpoint-down(xs) { width: 100%; @@ -1687,21 +1705,6 @@ $activity-add-hover: theme-color-level('primary', -10) !default; display: none; } -// Revisit these styles when MDL-78289 lands. Probably not needed anymore. -.automatic-completion-conditions .badge { - font-size: 80%; - padding: map-get($spacers, 2); - margin-top: map-get($spacers, 1); - .icon { - width: 0.7rem; - height: 0.7rem; - font-size: 0.7rem; - } - &:first-child { - margin-top: 0; - } -} - /* Activity divider */ .activity:focus-within + .activity div.divider button, diff --git a/theme/boost/style/moodle.css b/theme/boost/style/moodle.css index 8e6e922073a..dbf92e094c9 100644 --- a/theme/boost/style/moodle.css +++ b/theme/boost/style/moodle.css @@ -28954,6 +28954,7 @@ span.editinstructions .alert-link { } /* Variables definition*/ +/* Functions/Mixins definition */ /* Activity & Block 'add' buttons */ .activity-add, .block-add { @@ -29188,31 +29189,147 @@ span.editinstructions .alert-link { font-size: 0.875em; padding: 0; } -.activity-item .activity-info { +.activity-item .activity-completion { grid-area: completion; justify-self: end; } -.activity-item .activity-info button, -.activity-item .activity-info a[role=button] { - border: 1px solid #dee2e6; - border-radius: 0.5rem; +.activity-item .activity-completion button.btn, +.activity-item .activity-completion a[role=button].btn { + border: 1px solid #ced4da; font-weight: bold; + border-radius: 0.5rem; +} +.activity-item .activity-completion button.btn .icon, +.activity-item .activity-completion a[role=button].btn .icon { + font-size: inherit; +} +.activity-item .activity-completion button.btn-primary, +.activity-item .activity-completion a[role=button].btn-primary { + color: #0f6cbf; + background-color: #cfe2f2; + border-color: #cfe2f2; +} +.activity-item .activity-completion button.btn-primary hr, +.activity-item .activity-completion a[role=button].btn-primary hr { + border-top-color: #bbd6ed; +} +.activity-item .activity-completion button.btn-primary .alert-link, +.activity-item .activity-completion a[role=button].btn-primary .alert-link { + color: #0b5190; +} +.activity-item .activity-completion button.btn-secondary, +.activity-item .activity-completion a[role=button].btn-secondary { + color: #ced4da; + background-color: #f5f6f8; + border-color: #f5f6f8; +} +.activity-item .activity-completion button.btn-secondary hr, +.activity-item .activity-completion a[role=button].btn-secondary hr { + border-top-color: #e6e9ee; +} +.activity-item .activity-completion button.btn-secondary .alert-link, +.activity-item .activity-completion a[role=button].btn-secondary .alert-link { + color: #b1bbc4; +} +.activity-item .activity-completion button.btn-success, +.activity-item .activity-completion a[role=button].btn-success { + color: #357a32; + background-color: #d7e4d6; + border-color: #d7e4d6; +} +.activity-item .activity-completion button.btn-success hr, +.activity-item .activity-completion a[role=button].btn-success hr { + border-top-color: #c8dac7; +} +.activity-item .activity-completion button.btn-success .alert-link, +.activity-item .activity-completion a[role=button].btn-success .alert-link { + color: #255623; +} +.activity-item .activity-completion button.btn-info, +.activity-item .activity-completion a[role=button].btn-info { + color: #008196; + background-color: #cce6ea; + border-color: #cce6ea; +} +.activity-item .activity-completion button.btn-info hr, +.activity-item .activity-completion a[role=button].btn-info hr { + border-top-color: #badde3; +} +.activity-item .activity-completion button.btn-info .alert-link, +.activity-item .activity-completion a[role=button].btn-info .alert-link { + color: #005563; +} +.activity-item .activity-completion button.btn-warning, +.activity-item .activity-completion a[role=button].btn-warning { + color: #f0ad4e; + background-color: #fcefdc; + border-color: #fcefdc; +} +.activity-item .activity-completion button.btn-warning hr, +.activity-item .activity-completion a[role=button].btn-warning hr { + border-top-color: #fae4c5; +} +.activity-item .activity-completion button.btn-warning .alert-link, +.activity-item .activity-completion a[role=button].btn-warning .alert-link { + color: #ec971f; +} +.activity-item .activity-completion button.btn-danger, +.activity-item .activity-completion a[role=button].btn-danger { + color: #ca3120; + background-color: #f4d6d2; + border-color: #f4d6d2; +} +.activity-item .activity-completion button.btn-danger hr, +.activity-item .activity-completion a[role=button].btn-danger hr { + border-top-color: #efc3be; +} +.activity-item .activity-completion button.btn-danger .alert-link, +.activity-item .activity-completion a[role=button].btn-danger .alert-link { + color: #9e2619; +} +.activity-item .activity-completion button.btn-light, +.activity-item .activity-completion a[role=button].btn-light { + color: #f8f9fa; + background-color: #fefefe; + border-color: #fefefe; +} +.activity-item .activity-completion button.btn-light hr, +.activity-item .activity-completion a[role=button].btn-light hr { + border-top-color: #f1f1f1; +} +.activity-item .activity-completion button.btn-light .alert-link, +.activity-item .activity-completion a[role=button].btn-light .alert-link { + color: #dae0e5; +} +.activity-item .activity-completion button.btn-dark, +.activity-item .activity-completion a[role=button].btn-dark { + color: #343a40; + background-color: #d6d8d9; + border-color: #d6d8d9; +} +.activity-item .activity-completion button.btn-dark hr, +.activity-item .activity-completion a[role=button].btn-dark hr { + border-top-color: #c9cbcd; +} +.activity-item .activity-completion button.btn-dark .alert-link, +.activity-item .activity-completion a[role=button].btn-dark .alert-link { + color: #1d2124; } @media (max-width: 575.98px) { - .activity-item .activity-info { + .activity-item .activity-completion { width: 100%; margin-top: 0.5rem; } - .activity-item .activity-info button { + .activity-item .activity-completion button { width: 100%; } } -.activity-item .activity-info .completion-dialog { +.activity-item .activity-completion .completion-dialog { color: #495057; font-size: 0.8203125rem; min-width: 12rem; } -.activity-item .activity-info .completion-dialog .icon { +.activity-item .activity-completion .completion-dialog .icon { font-size: 0.8203125rem; width: 0.8203125rem; height: 0.8203125rem; @@ -29314,20 +29431,6 @@ span.editinstructions .alert-link { display: none; } -.automatic-completion-conditions .badge { - font-size: 80%; - padding: 0.5rem; - margin-top: 0.25rem; -} -.automatic-completion-conditions .badge .icon { - width: 0.7rem; - height: 0.7rem; - font-size: 0.7rem; -} -.automatic-completion-conditions .badge:first-child { - margin-top: 0; -} - /* Activity divider */ .activity:focus-within + .activity div.divider button, .course-section-header:focus-within + .content .section .activity:first-child div.divider button, diff --git a/theme/classic/style/moodle.css b/theme/classic/style/moodle.css index e1bb6842f9a..c86ae7f37ce 100644 --- a/theme/classic/style/moodle.css +++ b/theme/classic/style/moodle.css @@ -28954,6 +28954,7 @@ span.editinstructions .alert-link { } /* Variables definition*/ +/* Functions/Mixins definition */ /* Activity & Block 'add' buttons */ .activity-add, .block-add { @@ -29188,31 +29189,147 @@ span.editinstructions .alert-link { font-size: 0.875em; padding: 0; } -.activity-item .activity-info { +.activity-item .activity-completion { grid-area: completion; justify-self: end; } -.activity-item .activity-info button, -.activity-item .activity-info a[role=button] { - border: 1px solid #dee2e6; - border-radius: 0.25rem; +.activity-item .activity-completion button.btn, +.activity-item .activity-completion a[role=button].btn { + border: 1px solid #ced4da; font-weight: bold; + border-radius: 0.25rem; +} +.activity-item .activity-completion button.btn .icon, +.activity-item .activity-completion a[role=button].btn .icon { + font-size: inherit; +} +.activity-item .activity-completion button.btn-primary, +.activity-item .activity-completion a[role=button].btn-primary { + color: #0f6cbf; + background-color: #cfe2f2; + border-color: #cfe2f2; +} +.activity-item .activity-completion button.btn-primary hr, +.activity-item .activity-completion a[role=button].btn-primary hr { + border-top-color: #bbd6ed; +} +.activity-item .activity-completion button.btn-primary .alert-link, +.activity-item .activity-completion a[role=button].btn-primary .alert-link { + color: #0b5190; +} +.activity-item .activity-completion button.btn-secondary, +.activity-item .activity-completion a[role=button].btn-secondary { + color: #ced4da; + background-color: #f5f6f8; + border-color: #f5f6f8; +} +.activity-item .activity-completion button.btn-secondary hr, +.activity-item .activity-completion a[role=button].btn-secondary hr { + border-top-color: #e6e9ee; +} +.activity-item .activity-completion button.btn-secondary .alert-link, +.activity-item .activity-completion a[role=button].btn-secondary .alert-link { + color: #b1bbc4; +} +.activity-item .activity-completion button.btn-success, +.activity-item .activity-completion a[role=button].btn-success { + color: #357a32; + background-color: #d7e4d6; + border-color: #d7e4d6; +} +.activity-item .activity-completion button.btn-success hr, +.activity-item .activity-completion a[role=button].btn-success hr { + border-top-color: #c8dac7; +} +.activity-item .activity-completion button.btn-success .alert-link, +.activity-item .activity-completion a[role=button].btn-success .alert-link { + color: #255623; +} +.activity-item .activity-completion button.btn-info, +.activity-item .activity-completion a[role=button].btn-info { + color: #008196; + background-color: #cce6ea; + border-color: #cce6ea; +} +.activity-item .activity-completion button.btn-info hr, +.activity-item .activity-completion a[role=button].btn-info hr { + border-top-color: #badde3; +} +.activity-item .activity-completion button.btn-info .alert-link, +.activity-item .activity-completion a[role=button].btn-info .alert-link { + color: #005563; +} +.activity-item .activity-completion button.btn-warning, +.activity-item .activity-completion a[role=button].btn-warning { + color: #f0ad4e; + background-color: #fcefdc; + border-color: #fcefdc; +} +.activity-item .activity-completion button.btn-warning hr, +.activity-item .activity-completion a[role=button].btn-warning hr { + border-top-color: #fae4c5; +} +.activity-item .activity-completion button.btn-warning .alert-link, +.activity-item .activity-completion a[role=button].btn-warning .alert-link { + color: #ec971f; +} +.activity-item .activity-completion button.btn-danger, +.activity-item .activity-completion a[role=button].btn-danger { + color: #ca3120; + background-color: #f4d6d2; + border-color: #f4d6d2; +} +.activity-item .activity-completion button.btn-danger hr, +.activity-item .activity-completion a[role=button].btn-danger hr { + border-top-color: #efc3be; +} +.activity-item .activity-completion button.btn-danger .alert-link, +.activity-item .activity-completion a[role=button].btn-danger .alert-link { + color: #9e2619; +} +.activity-item .activity-completion button.btn-light, +.activity-item .activity-completion a[role=button].btn-light { + color: #f8f9fa; + background-color: #fefefe; + border-color: #fefefe; +} +.activity-item .activity-completion button.btn-light hr, +.activity-item .activity-completion a[role=button].btn-light hr { + border-top-color: #f1f1f1; +} +.activity-item .activity-completion button.btn-light .alert-link, +.activity-item .activity-completion a[role=button].btn-light .alert-link { + color: #dae0e5; +} +.activity-item .activity-completion button.btn-dark, +.activity-item .activity-completion a[role=button].btn-dark { + color: #343a40; + background-color: #d6d8d9; + border-color: #d6d8d9; +} +.activity-item .activity-completion button.btn-dark hr, +.activity-item .activity-completion a[role=button].btn-dark hr { + border-top-color: #c9cbcd; +} +.activity-item .activity-completion button.btn-dark .alert-link, +.activity-item .activity-completion a[role=button].btn-dark .alert-link { + color: #1d2124; } @media (max-width: 575.98px) { - .activity-item .activity-info { + .activity-item .activity-completion { width: 100%; margin-top: 0.5rem; } - .activity-item .activity-info button { + .activity-item .activity-completion button { width: 100%; } } -.activity-item .activity-info .completion-dialog { +.activity-item .activity-completion .completion-dialog { color: #495057; font-size: 0.8203125rem; min-width: 12rem; } -.activity-item .activity-info .completion-dialog .icon { +.activity-item .activity-completion .completion-dialog .icon { font-size: 0.8203125rem; width: 0.8203125rem; height: 0.8203125rem; @@ -29314,20 +29431,6 @@ span.editinstructions .alert-link { display: none; } -.automatic-completion-conditions .badge { - font-size: 80%; - padding: 0.5rem; - margin-top: 0.25rem; -} -.automatic-completion-conditions .badge .icon { - width: 0.7rem; - height: 0.7rem; - font-size: 0.7rem; -} -.automatic-completion-conditions .badge:first-child { - margin-top: 0; -} - /* Activity divider */ .activity:focus-within + .activity div.divider button, .course-section-header:focus-within + .content .section .activity:first-child div.divider button,