diff --git a/lib/amd/build/dynamic_tabs.min.js b/lib/amd/build/dynamic_tabs.min.js index 51bdd534519..c5aa179aecb 100644 --- a/lib/amd/build/dynamic_tabs.min.js +++ b/lib/amd/build/dynamic_tabs.min.js @@ -1,2 +1,2 @@ -define ("core/dynamic_tabs",["exports","jquery","core/templates","core/notification","core/pending","core/str","core/local/repository/dynamic_tabs","core_form/changechecker"],function(a,b,c,d,e,f,g,h){"use strict";Object.defineProperty(a,"__esModule",{value:!0});a.init=void 0;b=i(b);c=i(c);d=i(d);e=i(e);function i(a){return a&&a.__esModule?a:{default:a}}function j(a,b){var c=Object.keys(a);if(Object.getOwnPropertySymbols){var d=Object.getOwnPropertySymbols(a);if(b)d=d.filter(function(b){return Object.getOwnPropertyDescriptor(a,b).enumerable});c.push.apply(c,d)}return c}function k(a){for(var b=1,c;ba.length)b=a.length;for(var c=0,d=Array(b);ca.length)b=a.length;for(var c=0,d=Array(b);c.\n\n/**\n * Dynamic Tabs UI element with AJAX loading of tabs content\n *\n * @module core/dynamic_tabs\n * @copyright 2021 David Matamoros based on code from Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_strings as getStrings} from 'core/str';\nimport {getContent} from 'core/local/repository/dynamic_tabs';\nimport {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker';\n\nconst SELECTORS = {\n dynamicTabs: '.dynamictabs',\n activeTab: '.dynamictabs .nav-link.active',\n allActiveTabs: '.dynamictabs .nav-link[data-toggle=\"tab\"]:not(.disabled)',\n tabContent: '.dynamictabs .tab-pane [data-tab-content]',\n tabToggle: 'a[data-toggle=\"tab\"]',\n tabPane: '.dynamictabs .tab-pane',\n};\n\nSELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content=\"${tabName}\"]`;\nSELECTORS.forTabId = tabName => `.dynamictabs [data-toggle=\"tab\"][href=\"#${tabName}\"]`;\n\n/**\n * Initialises the tabs view on the page (only one tabs view per page is supported)\n */\nexport const init = () => {\n const tabToggle = $(SELECTORS.tabToggle);\n\n // Listen to click, warn user if they are navigating away with unsaved form changes.\n tabToggle.on('click', (event) => {\n if (!isAnyWatchedFormDirty()) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n getStrings([\n {key: 'changesmade', component: 'moodle'},\n {key: 'changesmadereallygoaway', component: 'moodle'},\n {key: 'confirm', component: 'moodle'},\n ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) =>\n // Reset form dirty state on confirmation, re-trigger the event.\n Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => {\n resetAllFormDirtyStates();\n $(event.target).trigger(event.type);\n })\n ).catch(Notification.exception);\n });\n\n // This code listens to Bootstrap event 'shown.bs.tab' which is triggered using JQuery and\n // can not be converted yet to native events.\n tabToggle.on('shown.bs.tab', function() {\n const tab = $($(this).attr('href'));\n if (tab.length !== 1) {\n return;\n }\n loadTab(tab.attr('id'));\n });\n\n if (!openTabFromHash()) {\n const tabs = document.querySelector(SELECTORS.allActiveTabs);\n if (tabs) {\n openTab(tabs.getAttribute('aria-controls'));\n } else {\n // We may hide tabs if there is only one available, just load the contents of the first tab.\n const tabPane = document.querySelector(SELECTORS.tabPane);\n if (tabPane) {\n tabPane.classList.add('active', 'show');\n loadTab(tabPane.getAttribute('id'));\n }\n }\n }\n};\n\n/**\n * Show \"loading\" template instead of a node\n *\n * @param {HTMLElement} node\n * @return {Promise}\n */\nconst indicateNodeIsLoading = (node) => {\n return Templates.render('core/loading', {})\n .then((html, js) => {\n return Templates.replaceNodeContents(node, html, js);\n }).catch(Notification.exception);\n};\n\n/**\n * Returns id/name of the currently active tab\n *\n * @return {String|null}\n */\nconst getActiveTabName = () => {\n const element = document.querySelector(SELECTORS.activeTab);\n return element?.getAttribute('aria-controls') || null;\n};\n\n/**\n * Returns the id/name of the first tab\n *\n * @return {String|null}\n */\nconst getFirstTabName = () => {\n const element = document.querySelector(SELECTORS.tabContent);\n return element?.dataset.tabContent || null;\n};\n\n/**\n * Loads contents of a tab using an AJAX request\n *\n * @param {String} tabName\n * @param {Object} additionalData additional data to pass to WS\n */\nconst loadTab = (tabName, additionalData = {}) => {\n // If tabName is not specified find the active tab, or if is not defined, the first available tab.\n tabName = tabName ?? getActiveTabName() ?? getFirstTabName();\n\n const tab = document.querySelector(SELECTORS.forTabName(tabName));\n if (!tab) {\n return;\n }\n\n const pendingPromise = new Pending('core/dynamic_tabs:loadTab:' + tabName);\n const tabdata = tab.closest(SELECTORS.dynamicTabs);\n const wsData = {\n 'reportid': tabdata.dataset.reportid,\n 'id': tabdata.dataset.id,\n ...additionalData\n };\n let tabjs = '';\n tab.textContent = '';\n\n indicateNodeIsLoading(tab)\n .then(() => {\n return getContent(tab.dataset.tabClass, JSON.stringify(wsData));\n })\n .then((data) => {\n tabjs = data.javascript;\n return Templates.render(data.template, JSON.parse(data.content));\n })\n .then((html, js) => {\n return Templates.replaceNodeContents(tab, html, js + tabjs);\n })\n .then(() => {\n pendingPromise.resolve();\n return null;\n })\n .catch(Notification.exception);\n};\n\n/**\n * Return the tab given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTab = (tabName) => {\n return document.querySelector(SELECTORS.forTabId(tabName));\n};\n\n/**\n * Return the tab pane given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTabPane = (tabName) => {\n return document.getElementById(tabName);\n};\n\n/**\n * Open the tab on page load. If this script loads before theme_boost/tab we need to open tab ourselves\n *\n * @param {String} tabName\n * @return {Boolean}\n */\nconst openTab = (tabName) => {\n const tab = getTab(tabName);\n if (!tab) {\n return false;\n }\n\n loadTab(tabName);\n tab.classList.add('active');\n getTabPane(tabName).classList.add('active', 'show');\n return true;\n};\n\n/**\n * If there is a location hash that is the same as the tab name - open this tab.\n *\n * @return {Boolean}\n */\nconst openTabFromHash = () => {\n const hash = document.location.hash;\n if (hash.match(/^#\\w+$/g)) {\n return openTab(hash.replace(/^#/g, ''));\n }\n\n return false;\n};\n"],"file":"dynamic_tabs.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/dynamic_tabs.js"],"names":["SELECTORS","dynamicTabs","activeTab","allActiveTabs","tabContent","tabToggle","tabPane","forTabName","tabName","forTabId","init","on","event","preventDefault","stopPropagation","key","component","then","strChangesMade","strChangesMadeReally","strConfirm","Notification","confirm","target","trigger","type","catch","exception","previousTabName","getActiveTabName","previousTab","document","querySelector","textContent","tab","attr","length","loadTab","openTabFromHash","tabs","openTab","getAttribute","classList","add","element","getFirstTabName","dataset","additionalData","pendingPromise","Pending","tabdata","closest","wsData","reportid","id","tabjs","tabClass","JSON","stringify","data","javascript","Templates","render","template","parse","content","html","js","replaceNodeContents","resolve","getTab","getTabPane","getElementById","hash","location","match","replace"],"mappings":"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","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 .\n\n/**\n * Dynamic Tabs UI element with AJAX loading of tabs content\n *\n * @module core/dynamic_tabs\n * @copyright 2021 David Matamoros based on code from Marina Glancy\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport $ from 'jquery';\nimport Templates from 'core/templates';\nimport {addIconToContainer} from 'core/loadingicon';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport {get_strings as getStrings} from 'core/str';\nimport {getContent} from 'core/local/repository/dynamic_tabs';\nimport {isAnyWatchedFormDirty, resetAllFormDirtyStates} from 'core_form/changechecker';\n\nconst SELECTORS = {\n dynamicTabs: '.dynamictabs',\n activeTab: '.dynamictabs .nav-link.active',\n allActiveTabs: '.dynamictabs .nav-link[data-toggle=\"tab\"]:not(.disabled)',\n tabContent: '.dynamictabs .tab-pane [data-tab-content]',\n tabToggle: 'a[data-toggle=\"tab\"]',\n tabPane: '.dynamictabs .tab-pane',\n};\n\nSELECTORS.forTabName = tabName => `.dynamictabs [data-tab-content=\"${tabName}\"]`;\nSELECTORS.forTabId = tabName => `.dynamictabs [data-toggle=\"tab\"][href=\"#${tabName}\"]`;\n\n/**\n * Initialises the tabs view on the page (only one tabs view per page is supported)\n */\nexport const init = () => {\n const tabToggle = $(SELECTORS.tabToggle);\n\n // Listen to click, warn user if they are navigating away with unsaved form changes.\n tabToggle.on('click', (event) => {\n if (!isAnyWatchedFormDirty()) {\n return;\n }\n\n event.preventDefault();\n event.stopPropagation();\n\n getStrings([\n {key: 'changesmade', component: 'moodle'},\n {key: 'changesmadereallygoaway', component: 'moodle'},\n {key: 'confirm', component: 'moodle'},\n ]).then(([strChangesMade, strChangesMadeReally, strConfirm]) =>\n // Reset form dirty state on confirmation, re-trigger the event.\n Notification.confirm(strChangesMade, strChangesMadeReally, strConfirm, null, () => {\n resetAllFormDirtyStates();\n $(event.target).trigger(event.type);\n })\n ).catch(Notification.exception);\n });\n\n // This code listens to Bootstrap events 'show.bs.tab' and 'shown.bs.tab' which is triggered using JQuery and\n // can not be converted yet to native events.\n tabToggle\n .on('show.bs.tab', function() {\n // Clean content from previous tab.\n const previousTabName = getActiveTabName();\n if (previousTabName) {\n const previousTab = document.querySelector(SELECTORS.forTabName(previousTabName));\n previousTab.textContent = '';\n }\n })\n .on('shown.bs.tab', function() {\n const tab = $($(this).attr('href'));\n if (tab.length !== 1) {\n return;\n }\n loadTab(tab.attr('id'));\n });\n\n if (!openTabFromHash()) {\n const tabs = document.querySelector(SELECTORS.allActiveTabs);\n if (tabs) {\n openTab(tabs.getAttribute('aria-controls'));\n } else {\n // We may hide tabs if there is only one available, just load the contents of the first tab.\n const tabPane = document.querySelector(SELECTORS.tabPane);\n if (tabPane) {\n tabPane.classList.add('active', 'show');\n loadTab(tabPane.getAttribute('id'));\n }\n }\n }\n};\n\n/**\n * Returns id/name of the currently active tab\n *\n * @return {String|null}\n */\nconst getActiveTabName = () => {\n const element = document.querySelector(SELECTORS.activeTab);\n return element?.getAttribute('aria-controls') || null;\n};\n\n/**\n * Returns the id/name of the first tab\n *\n * @return {String|null}\n */\nconst getFirstTabName = () => {\n const element = document.querySelector(SELECTORS.tabContent);\n return element?.dataset.tabContent || null;\n};\n\n/**\n * Loads contents of a tab using an AJAX request\n *\n * @param {String} tabName\n * @param {Object} additionalData additional data to pass to WS\n */\nconst loadTab = (tabName, additionalData = {}) => {\n // If tabName is not specified find the active tab, or if is not defined, the first available tab.\n tabName = tabName ?? getActiveTabName() ?? getFirstTabName();\n\n const tab = document.querySelector(SELECTORS.forTabName(tabName));\n if (!tab) {\n return;\n }\n\n const pendingPromise = new Pending('core/dynamic_tabs:loadTab:' + tabName);\n const tabdata = tab.closest(SELECTORS.dynamicTabs);\n const wsData = {\n 'reportid': tabdata.dataset.reportid,\n 'id': tabdata.dataset.id,\n ...additionalData\n };\n let tabjs = '';\n\n addIconToContainer(tab)\n .then(() => {\n return getContent(tab.dataset.tabClass, JSON.stringify(wsData));\n })\n .then((data) => {\n tabjs = data.javascript;\n return Templates.render(data.template, JSON.parse(data.content));\n })\n .then((html, js) => {\n return Templates.replaceNodeContents(tab, html, js + tabjs);\n })\n .then(() => {\n pendingPromise.resolve();\n return null;\n })\n .catch(Notification.exception);\n};\n\n/**\n * Return the tab given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTab = (tabName) => {\n return document.querySelector(SELECTORS.forTabId(tabName));\n};\n\n/**\n * Return the tab pane given the tab name\n *\n * @param {String} tabName\n * @return {HTMLElement}\n */\nconst getTabPane = (tabName) => {\n return document.getElementById(tabName);\n};\n\n/**\n * Open the tab on page load. If this script loads before theme_boost/tab we need to open tab ourselves\n *\n * @param {String} tabName\n * @return {Boolean}\n */\nconst openTab = (tabName) => {\n const tab = getTab(tabName);\n if (!tab) {\n return false;\n }\n\n loadTab(tabName);\n tab.classList.add('active');\n getTabPane(tabName).classList.add('active', 'show');\n return true;\n};\n\n/**\n * If there is a location hash that is the same as the tab name - open this tab.\n *\n * @return {Boolean}\n */\nconst openTabFromHash = () => {\n const hash = document.location.hash;\n if (hash.match(/^#\\w+$/g)) {\n return openTab(hash.replace(/^#/g, ''));\n }\n\n return false;\n};\n"],"file":"dynamic_tabs.min.js"} \ No newline at end of file diff --git a/lib/amd/src/dynamic_tabs.js b/lib/amd/src/dynamic_tabs.js index dfe57829e55..9e213dec9ee 100644 --- a/lib/amd/src/dynamic_tabs.js +++ b/lib/amd/src/dynamic_tabs.js @@ -23,6 +23,7 @@ import $ from 'jquery'; import Templates from 'core/templates'; +import {addIconToContainer} from 'core/loadingicon'; import Notification from 'core/notification'; import Pending from 'core/pending'; import {get_strings as getStrings} from 'core/str'; @@ -69,15 +70,24 @@ export const init = () => { ).catch(Notification.exception); }); - // This code listens to Bootstrap event 'shown.bs.tab' which is triggered using JQuery and + // This code listens to Bootstrap events 'show.bs.tab' and 'shown.bs.tab' which is triggered using JQuery and // can not be converted yet to native events. - tabToggle.on('shown.bs.tab', function() { - const tab = $($(this).attr('href')); - if (tab.length !== 1) { - return; - } - loadTab(tab.attr('id')); - }); + tabToggle + .on('show.bs.tab', function() { + // Clean content from previous tab. + const previousTabName = getActiveTabName(); + if (previousTabName) { + const previousTab = document.querySelector(SELECTORS.forTabName(previousTabName)); + previousTab.textContent = ''; + } + }) + .on('shown.bs.tab', function() { + const tab = $($(this).attr('href')); + if (tab.length !== 1) { + return; + } + loadTab(tab.attr('id')); + }); if (!openTabFromHash()) { const tabs = document.querySelector(SELECTORS.allActiveTabs); @@ -94,19 +104,6 @@ export const init = () => { } }; -/** - * Show "loading" template instead of a node - * - * @param {HTMLElement} node - * @return {Promise} - */ -const indicateNodeIsLoading = (node) => { - return Templates.render('core/loading', {}) - .then((html, js) => { - return Templates.replaceNodeContents(node, html, js); - }).catch(Notification.exception); -}; - /** * Returns id/name of the currently active tab * @@ -150,9 +147,8 @@ const loadTab = (tabName, additionalData = {}) => { ...additionalData }; let tabjs = ''; - tab.textContent = ''; - indicateNodeIsLoading(tab) + addIconToContainer(tab) .then(() => { return getContent(tab.dataset.tabClass, JSON.stringify(wsData)); }) diff --git a/reportbuilder/tests/behat/audience.feature b/reportbuilder/tests/behat/audience.feature index fb8c7ea90e9..f62789da82a 100644 --- a/reportbuilder/tests/behat/audience.feature +++ b/reportbuilder/tests/behat/audience.feature @@ -31,17 +31,16 @@ Feature: Configure access to reports based on intended audience Then I click on "Add audience 'Manually added users'" "link" And I should see "Added audience 'Manually added users'" And I set the field "Add users manually" to "User 1,User 3" - # It would be better to reference the report table directly, but we can't because of MDL-73011. - And I click on "Save changes" "button" in the "[role=tabpanel].active" "css_element" + And I press "Save changes" And I should see "Audience saved" And I should see "User 1" And I should not see "User 2" And I should see "User 3" And I should not see "Add an audience to this report" And I click on the "Access" dynamic tab - And I should see "User 1" in the "[role=tabpanel].active" "css_element" - And I should not see "User 2" in the "[role=tabpanel].active" "css_element" - And I should see "User 3" in the "[role=tabpanel].active" "css_element" + And I should see "User 1" in the "reportbuilder-table" "table" + And I should not see "User 2" in the "reportbuilder-table" "table" + And I should see "User 3" in the "reportbuilder-table" "table" Scenario: Configure report audience with has system role audience type Given the following "roles" exist: @@ -55,15 +54,14 @@ Feature: Configure access to reports based on intended audience When I click on "Add audience 'Assigned system role'" "link" And I should see "Added audience 'Assigned system role'" And I set the field "Select a role" to "Test role" - # It would be better to reference the report table directly, but we can't because of MDL-73011. - And I click on "Save changes" "button" in the "[role=tabpanel].active" "css_element" + And I press "Save changes" Then I should see "Audience saved" And I should see "Test role" And I should not see "Add an audience to this report" And I click on the "Access" dynamic tab - And I should not see "User 1" in the "[role=tabpanel].active" "css_element" - And I should see "User 2" in the "[role=tabpanel].active" "css_element" - And I should not see "User 3" in the "[role=tabpanel].active" "css_element" + And I should not see "User 1" in the "reportbuilder-table" "table" + And I should see "User 2" in the "reportbuilder-table" "table" + And I should not see "User 3" in the "reportbuilder-table" "table" Scenario: Configure report audience with Member of cohort audience type Given the following "cohorts" exist: @@ -77,19 +75,18 @@ Feature: Configure access to reports based on intended audience When I click on "Add audience 'Member of cohort'" "link" And I should see "Added audience 'Member of cohort'" And I set the field "Select members from cohort" to "Cohort1" - # It would be better to reference the report table directly, but we can't because of MDL-73011. - And I click on "Save changes" "button" in the "[role=tabpanel].active" "css_element" + And I press "Save changes" Then I should see "Audience saved" And I should see "Cohort1" And I should not see "Add an audience to this report" And I click on the "Access" dynamic tab - And I should not see "User 1" in the "[role=tabpanel].active" "css_element" - And I should not see "User 2" in the "[role=tabpanel].active" "css_element" - And I should see "User 3" in the "[role=tabpanel].active" "css_element" + And I should not see "User 1" in the "reportbuilder-table" "table" + And I should not see "User 2" in the "reportbuilder-table" "table" + And I should see "User 3" in the "reportbuilder-table" "table" Scenario: Configure report audience with Member of cohort audience type with no cohorts available Given I am on the "My report" "reportbuilder > Editor" page logged in as "admin" - And I click on the "Audience" dynamic tab + When I click on the "Audience" dynamic tab Then "Add audience 'All users'" "link" should exist # This audience type should be disabled because there are no cohorts available. And "Add audience 'Member of cohort'" "link" should not exist @@ -98,8 +95,7 @@ Feature: Configure access to reports based on intended audience Given I am on the "My report" "reportbuilder > Editor" page logged in as "admin" And I click on the "Audience" dynamic tab And I click on "Add audience 'All users'" "link" - # It would be better to reference the report table directly, but we can't because of MDL-73011. - And I click on "Save changes" "button" in the "[role=tabpanel].active" "css_element" + And I press "Save changes" When I click on "Delete audience 'All users'" "button" And I click on "Delete" "button" in the "Delete audience 'All users'" "dialogue" Then I should see "Deleted audience 'All users'" @@ -111,21 +107,20 @@ Feature: Configure access to reports based on intended audience And I should see "Nothing to display" And I click on the "Audience" dynamic tab And I should see "Add an audience to this report" - Then I click on "Add audience 'Manually added users'" "link" + And I click on "Add audience 'Manually added users'" "link" And I set the field "Add users manually" to "User 1,User 3" - # It would be better to reference the report table directly, but we can't because of MDL-73011. - And I click on "Save changes" "button" in the "[role=tabpanel].active" "css_element" - And I press "Edit audience 'Manually added users'" + And I press "Save changes" + When I press "Edit audience 'Manually added users'" And I set the field "Add users manually" to "User 2" - And I click on "Save changes" "button" in the "[role=tabpanel].active" "css_element" - And I should see "Audience saved" - And I should not see "User 1" in the "[role=tabpanel].active" "css_element" - And I should see "User 2" in the "[role=tabpanel].active" "css_element" - And I should not see "User 3" in the "[role=tabpanel].active" "css_element" + And I press "Save changes" + Then I should see "Audience saved" + And I should not see "User 1" + And I should see "User 2" + And I should not see "User 3" And I click on the "Access" dynamic tab - And I should not see "User 1" in the "[role=tabpanel].active" "css_element" - And I should see "User 2" in the "[role=tabpanel].active" "css_element" - And I should not see "User 3" in the "[role=tabpanel].active" "css_element" + And I should not see "User 1" in the "reportbuilder-table" "table" + And I should see "User 2" in the "reportbuilder-table" "table" + And I should not see "User 3" in the "reportbuilder-table" "table" Scenario: View report as a user with no edit capability and set in the report audience Given the following "core_reportbuilder > Reports" exist: @@ -156,8 +151,7 @@ Feature: Configure access to reports based on intended audience And I should see "Add an audience to this report" Then I click on "Add audience 'Manually added users'" "link" And I set the field "Add users manually" to "User 1" - # It would be better to reference the report table directly, but we can't because of MDL-73011. - And I click on "Save changes" "button" in the "[role=tabpanel].active" "css_element" + And I press "Save changes" And I log out And I log in as "user1" And I navigate to "Reports > Report builder > Custom reports" in site administration @@ -201,8 +195,7 @@ Feature: Configure access to reports based on intended audience And I should see "Add an audience to this report" Then I click on "Add audience 'Manually added users'" "link" And I set the field "Add users manually" to "User 1" - # It would be better to reference the report table directly, but we can't because of MDL-73011. - And I click on "Save changes" "button" in the "[role=tabpanel].active" "css_element" + And I press "Save changes" And I log out And I log in as "user1" And I navigate to "Reports > Report builder > Custom reports" in site administration