mirror of
https://github.com/moodle/moodle.git
synced 2025-08-10 11:26:41 +02:00
MDL-81681 course: Make reactive the section page
This commit is contained in:
parent
8aef5e6574
commit
79c85ab22d
13 changed files with 65 additions and 14 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -6,6 +6,6 @@ define("core_courseformat/local/content/section",["exports","core_courseformat/l
|
|||
* @class core_courseformat/local/content/section
|
||||
* @copyright 2021 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_header=_interopRequireDefault(_header),_dndsection=_interopRequireDefault(_dndsection),_templates=_interopRequireDefault(_templates);class _default extends _dndsection.default{create(){this.name="content_section",this.selectors={SECTION_ITEM:"[data-for='section_title']",CM:'[data-for="cmitem"]',SECTIONINFO:'[data-for="sectioninfo"]',SECTIONBADGES:'[data-region="sectionbadges"]',SHOWSECTION:'[data-action="sectionShow"]',HIDESECTION:'[data-action="sectionHide"]',ACTIONTEXT:".menu-action-text",ICON:".icon"},this.classes={LOCKED:"editinprogress",HASDESCRIPTION:"description",HIDE:"d-none",HIDDEN:"hidden",CURRENT:"current"},this.id=this.element.dataset.id}stateReady(state){if(this.configState(state),this.reactive.isEditing&&this.reactive.supportComponents){const sectionItem=this.getElement(this.selectors.SECTION_ITEM);if(sectionItem){const headerComponent=new _header.default({...this,element:sectionItem,fullregion:this.element});this.configDragDrop(headerComponent)}}}getWatchers(){return[{watch:"section[".concat(this.id,"]:updated"),handler:this._refreshSection}]}validateDropData(dropdata){return("section"!==(null==dropdata?void 0:dropdata.type)||null===this.reactive.sectionReturn)&&super.validateDropData(dropdata)}getLastCm(){const cms=this.getElements(this.selectors.CM);return cms&&0!==cms.length?cms[cms.length-1]:null}_refreshSection(_ref){var _element$dragging,_element$locked,_element$visible,_element$current;let{element:element}=_ref;this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.element.classList.toggle(this.classes.HIDDEN,null!==(_element$visible=!element.visible)&&void 0!==_element$visible&&_element$visible),this.element.classList.toggle(this.classes.CURRENT,null!==(_element$current=element.current)&&void 0!==_element$current&&_element$current),this.locked=element.locked;const sectioninfo=this.getElement(this.selectors.SECTIONINFO);sectioninfo&§ioninfo.classList.toggle(this.classes.HASDESCRIPTION,element.hasrestrictions),this._updateBadges(element),this._updateActionsMenu(element)}_updateBadges(section){const current=this.getElement("".concat(this.selectors.SECTIONBADGES," [data-type='iscurrent']"));null==current||current.classList.toggle(this.classes.HIDE,!section.current);const hiddenFromStudents=this.getElement("".concat(this.selectors.SECTIONBADGES," [data-type='hiddenfromstudents']"));null==hiddenFromStudents||hiddenFromStudents.classList.toggle(this.classes.HIDE,section.visible)}async _updateActionsMenu(section){var _affectedAction$datas,_affectedAction$datas2;let selector,newAction;section.visible?(selector=this.selectors.SHOWSECTION,newAction="sectionHide"):(selector=this.selectors.HIDESECTION,newAction="sectionShow");const affectedAction=this.getElement(selector);if(!affectedAction)return;affectedAction.dataset.action=newAction;const actionText=affectedAction.querySelector(this.selectors.ACTIONTEXT);if(null!==(_affectedAction$datas=affectedAction.dataset)&&void 0!==_affectedAction$datas&&_affectedAction$datas.swapname&&actionText){const oldText=null==actionText?void 0:actionText.innerText;actionText.innerText=affectedAction.dataset.swapname,affectedAction.dataset.swapname=oldText}const icon=affectedAction.querySelector(this.selectors.ICON);if(null!==(_affectedAction$datas2=affectedAction.dataset)&&void 0!==_affectedAction$datas2&&_affectedAction$datas2.swapicon&&icon){const newIcon=affectedAction.dataset.swapicon;if(newIcon){const pixHtml=await _templates.default.renderPix(newIcon,"core");_templates.default.replaceNode(icon,pixHtml,"")}}}}return _exports.default=_default,_exports.default}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_header=_interopRequireDefault(_header),_dndsection=_interopRequireDefault(_dndsection),_templates=_interopRequireDefault(_templates);class _default extends _dndsection.default{create(){this.name="content_section",this.selectors={SECTION_ITEM:"[data-for='section_title']",CM:'[data-for="cmitem"]',SECTIONINFO:'[data-for="sectioninfo"]',SECTIONBADGES:'[data-region="sectionbadges"]',SHOWSECTION:'[data-action="sectionShow"]',HIDESECTION:'[data-action="sectionHide"]',ACTIONTEXT:".menu-action-text",ICON:".icon"},this.classes={LOCKED:"editinprogress",HASDESCRIPTION:"description",HIDE:"d-none",HIDDEN:"hidden",CURRENT:"current"},this.id=this.element.dataset.id}stateReady(state){if(this.configState(state),this.reactive.isEditing&&this.reactive.supportComponents){const sectionItem=this.getElement(this.selectors.SECTION_ITEM);if(sectionItem){const headerComponent=new _header.default({...this,element:sectionItem,fullregion:this.element});this.configDragDrop(headerComponent)}}}getWatchers(){return[{watch:"section[".concat(this.id,"]:updated"),handler:this._refreshSection}]}validateDropData(dropdata){return("section"!==(null==dropdata?void 0:dropdata.type)||null===this.reactive.sectionReturn)&&super.validateDropData(dropdata)}getLastCm(){const cms=this.getElements(this.selectors.CM);return cms&&0!==cms.length?cms[cms.length-1]:null}_refreshSection(_ref){var _element$dragging,_element$locked,_element$visible,_element$current;let{element:element}=_ref;this.element.classList.toggle(this.classes.DRAGGING,null!==(_element$dragging=element.dragging)&&void 0!==_element$dragging&&_element$dragging),this.element.classList.toggle(this.classes.LOCKED,null!==(_element$locked=element.locked)&&void 0!==_element$locked&&_element$locked),this.element.classList.toggle(this.classes.HIDDEN,null!==(_element$visible=!element.visible)&&void 0!==_element$visible&&_element$visible),this.element.classList.toggle(this.classes.CURRENT,null!==(_element$current=element.current)&&void 0!==_element$current&&_element$current),this.locked=element.locked;const sectioninfo=this.getElement(this.selectors.SECTIONINFO);sectioninfo&§ioninfo.classList.toggle(this.classes.HASDESCRIPTION,element.hasrestrictions),this._updateBadges(element),this._updateActionsMenu(element)}_updateBadges(section){const current=this.getElement("".concat(this.selectors.SECTIONBADGES," [data-type='iscurrent']"));null==current||current.classList.toggle(this.classes.HIDE,!section.current);const hiddenFromStudents=this.getElement("".concat(this.selectors.SECTIONBADGES," [data-type='hiddenfromstudents']"));null==hiddenFromStudents||hiddenFromStudents.classList.toggle(this.classes.HIDE,section.visible)}async _updateActionsMenu(section){var _affectedAction$datas,_affectedAction$datas2;let selector,newAction;section.visible?(selector=this.selectors.SHOWSECTION,newAction="sectionHide"):(selector=this.selectors.HIDESECTION,newAction="sectionShow");const affectedAction=this._getActionMenu(selector);if(!affectedAction)return;affectedAction.dataset.action=newAction;const actionText=affectedAction.querySelector(this.selectors.ACTIONTEXT);if(null!==(_affectedAction$datas=affectedAction.dataset)&&void 0!==_affectedAction$datas&&_affectedAction$datas.swapname&&actionText){const oldText=null==actionText?void 0:actionText.innerText;actionText.innerText=affectedAction.dataset.swapname,affectedAction.dataset.swapname=oldText}const icon=affectedAction.querySelector(this.selectors.ICON);if(null!==(_affectedAction$datas2=affectedAction.dataset)&&void 0!==_affectedAction$datas2&&_affectedAction$datas2.swapicon&&icon){const newIcon=affectedAction.dataset.swapicon;if(newIcon){const pixHtml=await _templates.default.renderPix(newIcon,"core");_templates.default.replaceNode(icon,pixHtml,"")}}}_getActionMenu(selector){return this.getElement(".section_action_menu")?this.getElement(selector):document.querySelector(selector)}}return _exports.default=_default,_exports.default}));
|
||||
|
||||
//# sourceMappingURL=section.min.js.map
|
File diff suppressed because one or more lines are too long
|
@ -429,7 +429,7 @@ export default class extends BaseComponent {
|
|||
return (cmList.length || sectionInfo.hassummary || sectionInfo.rawtitle);
|
||||
});
|
||||
if (!needsConfirmation) {
|
||||
this.reactive.dispatch('sectionDelete', sectionIds);
|
||||
this._dispatchSectionDelete(sectionIds, target);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -455,11 +455,25 @@ export default class extends BaseComponent {
|
|||
// Stop the default save button behaviour which is to close the modal.
|
||||
e.preventDefault();
|
||||
modal.destroy();
|
||||
this.reactive.dispatch('sectionDelete', sectionIds);
|
||||
this._dispatchSectionDelete(sectionIds, target);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the section delete action and handle the redirection if necessary.
|
||||
*
|
||||
* @param {Array} sectionIds the IDs of the sections to delete.
|
||||
* @param {Element} target the dispatch action element
|
||||
*/
|
||||
async _dispatchSectionDelete(sectionIds, target) {
|
||||
await this.reactive.dispatch('sectionDelete', sectionIds);
|
||||
if (target.baseURI.includes('section.php')) {
|
||||
// Redirect to the course main page if the section is the current page.
|
||||
window.location.href = this.reactive.get('course').baseurl;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a toggle cm selection.
|
||||
*
|
||||
|
|
|
@ -172,7 +172,7 @@ export default class extends DndSection {
|
|||
newAction = 'sectionShow';
|
||||
}
|
||||
// Find the affected action.
|
||||
const affectedAction = this.getElement(selector);
|
||||
const affectedAction = this._getActionMenu(selector);
|
||||
if (!affectedAction) {
|
||||
return;
|
||||
}
|
||||
|
@ -195,4 +195,18 @@ export default class extends DndSection {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the action menu element from the selector.
|
||||
*
|
||||
* @param {string} selector The selector to find the action menu.
|
||||
* @returns The action menu element.
|
||||
*/
|
||||
_getActionMenu(selector) {
|
||||
if (this.getElement('.section_action_menu')) {
|
||||
return this.getElement(selector);
|
||||
}
|
||||
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -217,6 +217,6 @@
|
|||
</div>
|
||||
{{#js}}
|
||||
require(['core_courseformat/local/content'], function(component) {
|
||||
component.init('course-format-{{uniqid}}', {}, {{sectionreturn}});
|
||||
component.init('page', {}, {{sectionreturn}});
|
||||
});
|
||||
{{/js}}
|
||||
|
|
|
@ -110,3 +110,15 @@ Feature: Single section course page
|
|||
Then I should see "Online users"
|
||||
And I am on the "Course 1 > Section 1" "course > section" page
|
||||
And I should see "Online users"
|
||||
|
||||
@javascript
|
||||
Scenario: Delete a section from the section page redirects to the main course page
|
||||
Given I am on the "C1 > Section 1" "course > section" page
|
||||
And I turn editing mode on
|
||||
And I open the action menu in "page-header" "region"
|
||||
When I choose "Delete" in the open action menu
|
||||
And I click on "Delete" "button" in the "Delete section?" "dialogue"
|
||||
# Section 1 should be removed.
|
||||
Then I should not see "Section 1"
|
||||
# The user should be redirected to the course page.
|
||||
And I should see "General" in the "page" "region"
|
||||
|
|
|
@ -94,3 +94,15 @@ Feature: Varify section visibility interface
|
|||
And I open the action menu in "page-header" "region"
|
||||
And I choose "Hide" in the open action menu
|
||||
And I should see "Hidden from students" in the "[data-region='sectionbadges']" "css_element"
|
||||
|
||||
@javascript
|
||||
Scenario: The section action menu should be updated properly when a section is hidden/shown
|
||||
Given I open section "1" edit menu
|
||||
When I choose "Hide" in the open action menu
|
||||
Then I should see "Hidden from students" in the "Section 1" "section"
|
||||
And I open section "1" edit menu
|
||||
And I should see "Show" in the "Section 1" "section"
|
||||
And I press the escape key
|
||||
# Confirm the Section 2 menu hasn't been updated.
|
||||
And I open section "2" edit menu
|
||||
And I should see "Hide" in the "Section 2" "section"
|
||||
|
|
|
@ -5,6 +5,6 @@ define("format_topics/section",["exports","core/reactive","core_courseformat/cou
|
|||
* @module format_topics/section
|
||||
* @copyright 2022 Ferran Recio <ferran@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};class HighlightSection extends _reactive.BaseComponent{create(){this.name="format_topics_section",this.selectors={SECTION:"[data-for='section']",SETMARKER:'[data-action="sectionHighlight"]',REMOVEMARKER:'[data-action="sectionUnhighlight"]',ACTIONTEXT:".menu-action-text",ICON:".icon"},this.classes={HIDE:"d-none"},this.formatActions={HIGHLIGHT:"sectionHighlight",UNHIGHLIGHT:"sectionUnhighlight"}}getWatchers(){return[{watch:"section.current:updated",handler:this._refreshHighlight}]}async _refreshHighlight(_ref){var _affectedAction$datas,_affectedAction$datas2;let selector,newAction,{element:element}=_ref;element.current?(selector=this.selectors.SETMARKER,newAction=this.formatActions.UNHIGHLIGHT):(selector=this.selectors.REMOVEMARKER,newAction=this.formatActions.HIGHLIGHT);const affectedAction=this.getElement("".concat(this.selectors.SECTION," ").concat(selector),element.id);if(!affectedAction)return;affectedAction.dataset.action=newAction;const actionText=affectedAction.querySelector(this.selectors.ACTIONTEXT);if(null!==(_affectedAction$datas=affectedAction.dataset)&&void 0!==_affectedAction$datas&&_affectedAction$datas.swapname&&actionText){const oldText=null==actionText?void 0:actionText.innerText;actionText.innerText=affectedAction.dataset.swapname,affectedAction.dataset.swapname=oldText}const icon=affectedAction.querySelector(this.selectors.ICON);if(null!==(_affectedAction$datas2=affectedAction.dataset)&&void 0!==_affectedAction$datas2&&_affectedAction$datas2.swapicon&&icon){const newIcon=affectedAction.dataset.swapicon;if(newIcon){const pixHtml=await _templates.default.renderPix(newIcon,"core");_templates.default.replaceNode(icon,pixHtml,"")}}}}_exports.init=()=>{const courseEditor=(0,_courseeditor.getCurrentCourseEditor)();courseEditor.supportComponents&&courseEditor.isEditing&&new HighlightSection({element:document.getElementById("region-main"),reactive:courseEditor})}}));
|
||||
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};class HighlightSection extends _reactive.BaseComponent{create(){this.name="format_topics_section",this.selectors={SETMARKER:'[data-action="sectionHighlight"]',REMOVEMARKER:'[data-action="sectionUnhighlight"]',ACTIONTEXT:".menu-action-text",ICON:".icon"},this.classes={HIDE:"d-none"},this.formatActions={HIGHLIGHT:"sectionHighlight",UNHIGHLIGHT:"sectionUnhighlight"}}getWatchers(){return[{watch:"section.current:updated",handler:this._refreshHighlight}]}async _refreshHighlight(_ref){var _affectedAction$datas,_affectedAction$datas2;let selector,newAction,{element:element}=_ref;element.current?(selector=this.selectors.SETMARKER,newAction=this.formatActions.UNHIGHLIGHT):(selector=this.selectors.REMOVEMARKER,newAction=this.formatActions.HIGHLIGHT);const affectedAction=this.getElement("".concat(selector),element.id);if(!affectedAction)return;affectedAction.dataset.action=newAction;const actionText=affectedAction.querySelector(this.selectors.ACTIONTEXT);if(null!==(_affectedAction$datas=affectedAction.dataset)&&void 0!==_affectedAction$datas&&_affectedAction$datas.swapname&&actionText){const oldText=null==actionText?void 0:actionText.innerText;actionText.innerText=affectedAction.dataset.swapname,affectedAction.dataset.swapname=oldText}const icon=affectedAction.querySelector(this.selectors.ICON);if(null!==(_affectedAction$datas2=affectedAction.dataset)&&void 0!==_affectedAction$datas2&&_affectedAction$datas2.swapicon&&icon){const newIcon=affectedAction.dataset.swapicon;if(newIcon){const pixHtml=await _templates.default.renderPix(newIcon,"core");_templates.default.replaceNode(icon,pixHtml,"")}}}}_exports.init=()=>{const courseEditor=(0,_courseeditor.getCurrentCourseEditor)();courseEditor.supportComponents&&courseEditor.isEditing&&new HighlightSection({element:document.getElementById("page"),reactive:courseEditor})}}));
|
||||
|
||||
//# sourceMappingURL=section.min.js.map
|
File diff suppressed because one or more lines are too long
|
@ -35,7 +35,6 @@ class HighlightSection extends BaseComponent {
|
|||
this.name = 'format_topics_section';
|
||||
// Default query selectors.
|
||||
this.selectors = {
|
||||
SECTION: `[data-for='section']`,
|
||||
SETMARKER: `[data-action="sectionHighlight"]`,
|
||||
REMOVEMARKER: `[data-action="sectionUnhighlight"]`,
|
||||
ACTIONTEXT: `.menu-action-text`,
|
||||
|
@ -80,7 +79,7 @@ class HighlightSection extends BaseComponent {
|
|||
newAction = this.formatActions.HIGHLIGHT;
|
||||
}
|
||||
// Find the affected action.
|
||||
const affectedAction = this.getElement(`${this.selectors.SECTION} ${selector}`, element.id);
|
||||
const affectedAction = this.getElement(`${selector}`, element.id);
|
||||
if (!affectedAction) {
|
||||
return;
|
||||
}
|
||||
|
@ -108,7 +107,7 @@ export const init = () => {
|
|||
const courseEditor = getCurrentCourseEditor();
|
||||
if (courseEditor.supportComponents && courseEditor.isEditing) {
|
||||
new HighlightSection({
|
||||
element: document.getElementById('region-main'),
|
||||
element: document.getElementById('page'),
|
||||
reactive: courseEditor,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1183,7 +1183,7 @@ class behat_course extends behat_base {
|
|||
"/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]";
|
||||
|
||||
// Component based courses do not use lightboxes anymore but js depending.
|
||||
$sectionreadyxpath = "//*[contains(@id,'page-content')]" .
|
||||
$sectionreadyxpath = "//*[contains(@id,'page')]" .
|
||||
"/descendant::*[contains(concat(' ', normalize-space(@class), ' '), ' stateready ')]";
|
||||
|
||||
$duplicationreadyxpath = "$hiddenlightboxxpath | $sectionreadyxpath";
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue