MDL-73538 core_courseformat: fix course index manual completion

The current manual completion button triggers an event to alert the page
of any completion change. This event is capture in the course page by
the core_courseformat/local/content module but it was ignored when this
happens in an activity page. Now the activity header has its own
component to capture this event and support possible future reactive
actions.
This commit is contained in:
Ferran Recio
2022-02-02 11:54:12 +01:00
parent c352b70022
commit 7c249a7d84
7 changed files with 113 additions and 6 deletions
@@ -0,0 +1,2 @@
define ("core_courseformat/local/content/activity_header",["exports","core/reactive","core_courseformat/courseeditor","core_course/events"],function(a,b,c,d){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.default=void 0;d=f(d);function e(){if("function"!=typeof WeakMap)return null;var a=new WeakMap;e=function(){return a};return a}function f(a){if(a&&a.__esModule){return a}if(null===a||"object"!==g(a)&&"function"!=typeof a){return{default:a}}var b=e();if(b&&b.has(a)){return b.get(a)}var c={},d=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var f in a){if(Object.prototype.hasOwnProperty.call(a,f)){var h=d?Object.getOwnPropertyDescriptor(a,f):null;if(h&&(h.get||h.set)){Object.defineProperty(c,f,h)}else{c[f]=a[f]}}}c.default=a;if(b){b.set(a,c)}return c}function g(a){"@babel/helpers - typeof";if("function"==typeof Symbol&&"symbol"==typeof Symbol.iterator){g=function(a){return typeof a}}else{g=function(a){return a&&"function"==typeof Symbol&&a.constructor===Symbol&&a!==Symbol.prototype?"symbol":typeof a}}return g(a)}function h(a,b){if(!(a instanceof b)){throw new TypeError("Cannot call a class as a function")}}function i(a,b){for(var c=0,d;c<b.length;c++){d=b[c];d.enumerable=d.enumerable||!1;d.configurable=!0;if("value"in d)d.writable=!0;Object.defineProperty(a,d.key,d)}}function j(a,b,c){if(b)i(a.prototype,b);if(c)i(a,c);return a}function k(a,b){if("function"!=typeof b&&null!==b){throw new TypeError("Super expression must either be null or a function")}a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,writable:!0,configurable:!0}});if(b)l(a,b)}function l(a,b){l=Object.setPrototypeOf||function(a,b){a.__proto__=b;return a};return l(a,b)}function m(a){return function(){var b=q(a),c;if(p()){var d=q(this).constructor;c=Reflect.construct(b,arguments,d)}else{c=b.apply(this,arguments)}return n(this,c)}}function n(a,b){if(b&&("object"===g(b)||"function"==typeof b)){return b}return o(a)}function o(a){if(void 0===a){throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}return a}function p(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{Date.prototype.toString.call(Reflect.construct(Date,[],function(){}));return!0}catch(a){return!1}}function q(a){q=Object.setPrototypeOf?Object.getPrototypeOf:function(a){return a.__proto__||Object.getPrototypeOf(a)};return q(a)}var r={ACTIVITY_HEADER:"[data-for='page-activity-header']"},s=function(a){k(b,a);var e=m(b);function b(){h(this,b);return e.apply(this,arguments)}j(b,[{key:"create",value:function create(){this.name="activity_header"}},{key:"stateReady",value:function stateReady(){this.addEventListener(this.element,d.manualCompletionToggled,this._completionHandler)}},{key:"_completionHandler",value:function _completionHandler(a){var b=a.detail;if(b===void 0){return}this.reactive.dispatch("cmCompletion",[b.cmid],b.completed)}}],[{key:"init",value:function init(a,d){var e=a?a:r.ACTIVITY_HEADER;return new b({element:document.querySelector(e),reactive:(0,c.getCurrentCourseEditor)(),selectors:d})}}]);return b}(b.BaseComponent);a.default=s;return a.default});
//# sourceMappingURL=activity_header.min.js.map
@@ -0,0 +1 @@
{"version":3,"sources":["../../../src/local/content/activity_header.js"],"names":["SELECTORS","ACTIVITY_HEADER","Component","name","addEventListener","element","CourseEvents","manualCompletionToggled","_completionHandler","detail","reactive","dispatch","cmid","completed","target","selectors","elementselector","document","querySelector","BaseComponent"],"mappings":"6OA0BA,O,+pEAGMA,CAAAA,CAAS,CAAG,CACdC,eAAe,oCADD,C,CAIGC,C,+HAKR,CAEL,KAAKC,IAAL,CAAY,iBACf,C,+CAqBY,CAET,KAAKC,gBAAL,CACI,KAAKC,OADT,CAEIC,CAAY,CAACC,uBAFjB,CAGI,KAAKC,kBAHT,CAKH,C,gEAQ4B,IAATC,CAAAA,CAAS,GAATA,MAAS,CACzB,GAAIA,CAAM,SAAV,CAA0B,CACtB,MACH,CACD,KAAKC,QAAL,CAAcC,QAAd,CAAuB,cAAvB,CAAuC,CAACF,CAAM,CAACG,IAAR,CAAvC,CAAsDH,CAAM,CAACI,SAA7D,CACH,C,oCAhCWC,C,CAAQC,C,CAAW,CAC3B,GAAMC,CAAAA,CAAe,CAAIF,CAAD,CAAWA,CAAX,CAAoBd,CAAS,CAACC,eAAtD,CACA,MAAO,IAAIC,CAAAA,CAAJ,CAAc,CACjBG,OAAO,CAAEY,QAAQ,CAACC,aAAT,CAAuBF,CAAvB,CADQ,CAEjBN,QAAQ,CAAE,8BAFO,CAGjBK,SAAS,CAATA,CAHiB,CAAd,CAKV,C,cAxBkCI,e","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * The activity header component.\n *\n * @module core_courseformat/local/content/activity_header\n * @class core_courseformat/local/content/activity_header\n * @copyright 2021 Ferran Recio <[email protected]>\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {BaseComponent} from 'core/reactive';\nimport {getCurrentCourseEditor} from 'core_courseformat/courseeditor';\nimport * as CourseEvents from 'core_course/events';\n\n// Global page selectors.\nconst SELECTORS = {\n ACTIVITY_HEADER: `[data-for='page-activity-header']`,\n};\n\nexport default class Component extends BaseComponent {\n\n /**\n * Constructor hook.\n */\n create() {\n // Optional component name for debugging.\n this.name = 'activity_header';\n }\n\n /**\n * Static method to create a component instance form the mustache template.\n *\n * @param {string} target optional altentative DOM main element CSS selector\n * @param {object} selectors optional css selector overrides\n * @return {Component}\n */\n static init(target, selectors) {\n const elementselector = (target) ? target : SELECTORS.ACTIVITY_HEADER;\n return new Component({\n element: document.querySelector(elementselector),\n reactive: getCurrentCourseEditor(),\n selectors\n });\n }\n\n /**\n * Initial state ready method.\n */\n stateReady() {\n // Capture completion events.\n this.addEventListener(\n this.element,\n CourseEvents.manualCompletionToggled,\n this._completionHandler\n );\n }\n\n /**\n * Activity manual completion listener.\n *\n * @param {Event} event the custom event\n * @param {object} event.detail the event details\n */\n _completionHandler({detail}) {\n if (detail === undefined) {\n return;\n }\n this.reactive.dispatch('cmCompletion', [detail.cmid], detail.completed);\n }\n}\n"],"file":"activity_header.min.js"}
@@ -0,0 +1,84 @@
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* The activity header component.
*
* @module core_courseformat/local/content/activity_header
* @class core_courseformat/local/content/activity_header
* @copyright 2021 Ferran Recio <[email protected]>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import {BaseComponent} from 'core/reactive';
import {getCurrentCourseEditor} from 'core_courseformat/courseeditor';
import * as CourseEvents from 'core_course/events';
// Global page selectors.
const SELECTORS = {
ACTIVITY_HEADER: `[data-for='page-activity-header']`,
};
export default class Component extends BaseComponent {
/**
* Constructor hook.
*/
create() {
// Optional component name for debugging.
this.name = 'activity_header';
}
/**
* Static method to create a component instance form the mustache template.
*
* @param {string} target optional altentative DOM main element CSS selector
* @param {object} selectors optional css selector overrides
* @return {Component}
*/
static init(target, selectors) {
const elementselector = (target) ? target : SELECTORS.ACTIVITY_HEADER;
return new Component({
element: document.querySelector(elementselector),
reactive: getCurrentCourseEditor(),
selectors
});
}
/**
* Initial state ready method.
*/
stateReady() {
// Capture completion events.
this.addEventListener(
this.element,
CourseEvents.manualCompletionToggled,
this._completionHandler
);
}
/**
* Activity manual completion listener.
*
* @param {Event} event the custom event
* @param {object} event.detail the event details
*/
_completionHandler({detail}) {
if (detail === undefined) {
return;
}
this.reactive.dispatch('cmCompletion', [detail.cmid], detail.completed);
}
}
@@ -40,7 +40,7 @@ Feature: Course index completion icons
And "To do" "icon" should exist in the "courseindex-content" "region"
@javascript
Scenario: Manual completion shoudl update the course index completion
Scenario: Manual completion should update the course index completion
Given I am on the "C1" "Course" page logged in as "student1"
And "To do" "icon" should exist in the "courseindex-content" "region"
When I press "Mark as done"
@@ -50,6 +50,17 @@ Feature: Course index completion icons
And I wait until "Mark as done" "button" exists
And "To do" "icon" should exist in the "courseindex-content" "region"
@javascript
Scenario: Manual completion in an activity page should update the course index
Given I am on the "sample1" "Activity" page logged in as "student1"
And "To do" "icon" should exist in the "courseindex-content" "region"
When I press "Mark as done"
And I wait until "Done" "button" exists
Then "Done" "icon" should exist in the "courseindex-content" "region"
And I press "Done"
And I wait until "Mark as done" "button" exists
And "To do" "icon" should exist in the "courseindex-content" "region"
@javascript
Scenario: Refresh the page should keep the completion consistent
Given I am on the "C1" "Course" page logged in as "student1"
+8
View File
@@ -174,6 +174,14 @@ class activity_header implements \renderable, \templatable {
$completion = $output->activity_information($this->page->cm, $completiondetails, $activitydates);
}
$format = course_get_format($this->page->course);
if ($format->supports_components()) {
$this->page->requires->js_call_amd(
'core_courseformat/local/content/activity_header',
'init'
);
}
return [
'title' => $this->title,
'description' => $this->description,
+5 -4
View File
@@ -1041,11 +1041,12 @@ class page_requirements_manager {
public function js_call_amd($fullmodule, $func = null, $params = array()) {
global $CFG;
list($component, $module) = explode('/', $fullmodule, 2);
$modulepath = explode('/', $fullmodule);
$component = clean_param($component, PARAM_COMPONENT);
$module = clean_param($module, PARAM_ALPHANUMEXT);
$modname = "{$component}/{$module}";
$modname = clean_param(array_shift($modulepath), PARAM_COMPONENT);
foreach ($modulepath as $module) {
$modname .= '/' . clean_param($module, PARAM_ALPHANUMEXT);
}
$functioncode = [];
if ($func !== null) {
+1 -1
View File
@@ -70,7 +70,7 @@
{{#title}}
<h2>{{{title}}}</h2>
{{/title}}
<div class="activity-header">
<div class="activity-header" data-for="page-activity-header">
{{#completion}}
<span class="sr-only">{{#str}} overallaggregation, completion {{/str}}</span>
{{{completion}}}