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:
@@ -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"
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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}}}
|
||||
|
||||
Reference in New Issue
Block a user