MDL-67883 core: Make core ready for MoodleNet.
[moodle.git] / course / amd / src / local / activitychooser / dialogue.js
index 3cbc5e8..cd045a3 100644 (file)
@@ -31,6 +31,7 @@ import {addIconToContainer} from 'core/loadingicon';
 import * as Repository from 'core_course/local/activitychooser/repository';
 import Notification from 'core/notification';
 import {debounce} from 'core/utils';
+const getPlugin = pluginName => import(pluginName);
 
 /**
  * Given an event from the main module 'page' navigate to it's help section via a carousel.
@@ -38,8 +39,13 @@ import {debounce} from 'core/utils';
  * @method showModuleHelp
  * @param {jQuery} carousel Our initialized carousel to manipulate
  * @param {Object} moduleData Data of the module to carousel to
+ * @param {jQuery} modal We need to figure out if the current modal has a footer.
  */
-const showModuleHelp = (carousel, moduleData) => {
+const showModuleHelp = (carousel, moduleData, modal = null) => {
+    // If we have a real footer then we need to change temporarily.
+    if (modal !== null && moduleData.showFooter === true) {
+        modal.setFooter(Templates.render('core_course/local/activitychooser/footer_partial', moduleData));
+    }
     const help = carousel.find(selectors.regions.help)[0];
     help.innerHTML = '';
     help.classList.add('m-auto');
@@ -107,8 +113,9 @@ const manageFavouriteState = async(modalBody, caller, partialFavourite) => {
  * @param {Promise} modal Our modal that we are working with
  * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}
  * @param {Function} partialFavourite Partially applied function we need to manage favourite status
+ * @param {Object} footerData Our base footer object.
  */
-const registerListenerEvents = (modal, mappedModules, partialFavourite) => {
+const registerListenerEvents = (modal, mappedModules, partialFavourite, footerData) => {
     const bodyClickListener = async(e) => {
         if (e.target.closest(selectors.actions.optionActions.showSummary)) {
             const carousel = $(modal.getBody()[0].querySelector(selectors.regions.carousel));
@@ -116,7 +123,9 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite) => {
             const module = e.target.closest(selectors.regions.chooserOption.container);
             const moduleName = module.dataset.modname;
             const moduleData = mappedModules.get(moduleName);
-            showModuleHelp(carousel, moduleData);
+            // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.
+            moduleData.showFooter = modal.hasFooterContent();
+            showModuleHelp(carousel, moduleData, modal);
         }
 
         if (e.target.closest(selectors.actions.optionActions.manageFavourite)) {
@@ -128,7 +137,7 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite) => {
             const firstChooserOption = sectionChooserOptions
                 .querySelector(selectors.regions.chooserOption.container);
             toggleFocusableChooserOption(firstChooserOption, true);
-            initChooserOptionsKeyboardNavigation(modal.getBody()[0], mappedModules, sectionChooserOptions);
+            initChooserOptionsKeyboardNavigation(modal.getBody()[0], mappedModules, sectionChooserOptions, modal);
         }
 
         // From the help screen go back to the module overview.
@@ -150,7 +159,18 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite) => {
             const searchInput = modal.getBody()[0].querySelector(selectors.actions.search);
             searchInput.value = "";
             searchInput.focus();
-            toggleSearchResultsView(modal.getBody()[0], mappedModules, searchInput.value);
+            toggleSearchResultsView(modal, mappedModules, searchInput.value);
+        }
+    };
+
+    // We essentially have two types of footer.
+    // A fake one that is handled within the template for chooser_help and then all of the stuff for
+    // modal.footer. We need to ensure we know exactly what type of footer we are using so we know what we
+    // need to manage. The below code handles a real footer going to a mnet carousel item.
+    const footerClickListener = async(e) => {
+        if (footerData.footer === true) {
+            const footerjs = await getPlugin(footerData.customfooterjs);
+            await footerjs.footerClickListener(e, footerData, modal);
         }
     };
 
@@ -183,7 +203,7 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite) => {
         // The search input is triggered.
         searchInput.addEventListener('input', debounce(() => {
             // Display the search results.
-            toggleSearchResultsView(body, mappedModules, searchInput.value);
+            toggleSearchResultsView(modal, mappedModules, searchInput.value);
         }, 300));
         return body;
     })
@@ -197,12 +217,22 @@ const registerListenerEvents = (modal, mappedModules, partialFavourite) => {
 
         toggleFocusableChooserOption(firstChooserOption, true);
         initTabsKeyboardNavigation(body);
-        initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions);
+        initChooserOptionsKeyboardNavigation(body, mappedModules, sectionChooserOptions, modal);
 
         return body;
     })
     .catch();
 
+    modal.getFooterPromise()
+
+    // The return value of getBodyPromise is a jquery object containing the body NodeElement.
+    .then(footer => footer[0])
+    // Add the listener for clicks on the footer.
+    .then(footer => {
+        footer.addEventListener('click', footerClickListener);
+        return footer;
+    })
+    .catch();
 };
 
 /**
@@ -283,8 +313,9 @@ const initTabsKeyboardNavigation = (body) => {
  * @param {HTMLElement} body Our modal that we are working with
  * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}
  * @param {HTMLElement} chooserOptionsContainer The section that contains the chooser items
+ * @param {Object} modal Our created modal for the section
  */
-const initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOptionsContainer) => {
+const initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOptionsContainer, modal = null) => {
     const chooserOptions = chooserOptionsContainer.querySelectorAll(selectors.regions.chooserOption.container);
 
     Array.from(chooserOptions).forEach((element) => {
@@ -303,7 +334,10 @@ const initChooserOptionsKeyboardNavigation = (body, mappedModules, chooserOption
                         pause: true,
                         keyboard: false
                     });
-                    showModuleHelp(carousel, moduleData);
+
+                    // We need to know if the overall modal has a footer so we know when to show a real / vs fake footer.
+                    moduleData.showFooter = modal.hasFooterContent();
+                    showModuleHelp(carousel, moduleData, modal);
                 }
             }
 
@@ -424,11 +458,12 @@ const renderSearchResults = async(searchResultsContainer, searchResultsData) =>
  * Toggle (display/hide) the search results depending on the value of the search query
  *
  * @method toggleSearchResultsView
- * @param {HTMLElement} modalBody The body of the created modal for the section
+ * @param {Object} modal Our created modal for the section
  * @param {Map} mappedModules A map of all of the modules we are working with with K: mod_name V: {Object}
  * @param {String} searchQuery The search query
  */
-const toggleSearchResultsView = async(modalBody, mappedModules, searchQuery) => {
+const toggleSearchResultsView = async(modal, mappedModules, searchQuery) => {
+    const modalBody = modal.getBody()[0];
     const searchResultsContainer = modalBody.querySelector(selectors.regions.searchResults);
     const chooserContainer = modalBody.querySelector(selectors.regions.chooser);
     const clearSearchButton = modalBody.querySelector(selectors.elements.clearsearch);
@@ -443,7 +478,7 @@ const toggleSearchResultsView = async(modalBody, mappedModules, searchQuery) =>
             // Set the first result item to be focusable.
             toggleFocusableChooserOption(firstSearchResultItem, true);
             // Register keyboard events on the created search result items.
-            initChooserOptionsKeyboardNavigation(modalBody, mappedModules, searchResultItemsContainer);
+            initChooserOptionsKeyboardNavigation(modalBody, mappedModules, searchResultItemsContainer, modal);
         }
         // Display the "clear" search button in the activity chooser search bar.
         searchIcon.classList.add('d-none');
@@ -513,7 +548,7 @@ const setupKeyboardAccessibility = (modal, mappedModules) => {
             disableFocusAllChooserOptions(prevActiveSectionChooserOptions);
             // Enable the focus of the first chooser option in the current active section.
             toggleFocusableChooserOption(firstChooserOption, true);
-            initChooserOptionsKeyboardNavigation(body[0], mappedModules, activeSectionChooserOptions);
+            initChooserOptionsKeyboardNavigation(body[0], mappedModules, activeSectionChooserOptions, modal);
         });
         return;
     }).catch(Notification.exception);
@@ -539,8 +574,9 @@ const disableFocusAllChooserOptions = (sectionChooserOptions) => {
  * @param {Promise} modalPromise Our created modal for the section
  * @param {Array} sectionModules An array of all of the built module information
  * @param {Function} partialFavourite Partially applied function we need to manage favourite status
+ * @param {Object} footerData Our base footer object.
  */
-export const displayChooser = (modalPromise, sectionModules, partialFavourite) => {
+export const displayChooser = (modalPromise, sectionModules, partialFavourite, footerData) => {
     // Make a map so we can quickly fetch a specific module's object for either rendering or searching.
     const mappedModules = new Map();
     sectionModules.forEach((module) => {
@@ -549,7 +585,7 @@ export const displayChooser = (modalPromise, sectionModules, partialFavourite) =
 
     // Register event listeners.
     modalPromise.then(modal => {
-        registerListenerEvents(modal, mappedModules, partialFavourite);
+        registerListenerEvents(modal, mappedModules, partialFavourite, footerData);
 
         // We want to focus on the first chooser option element as soon as the modal is opened.
         setupKeyboardAccessibility(modal, mappedModules);