MDL-66890 forumreport_summary: Added the ability to close filters
authorMichael Hawkins <michaelh@moodle.com>
Tue, 22 Oct 2019 03:37:53 +0000 (11:37 +0800)
committerMichael Hawkins <michaelh@moodle.com>
Wed, 6 Nov 2019 07:27:51 +0000 (15:27 +0800)
Previously, filters could only be closed by saving/submitting them.
Now, you are able to close and reopen them by opening another filter,
using the escape key, or clicking outside of the filter.
As per UX recommendations, filters retain any changes until the page
is reloaded (either via refresh or by saving a different filter).

mod/forum/report/summary/amd/build/filters.min.js
mod/forum/report/summary/amd/build/filters.min.js.map
mod/forum/report/summary/amd/src/filters.js
mod/forum/report/summary/templates/filter_dates_popover.mustache
mod/forum/report/summary/templates/filter_groups.mustache

index 25eb61a..14c9f30 100644 (file)
Binary files a/mod/forum/report/summary/amd/build/filters.min.js and b/mod/forum/report/summary/amd/build/filters.min.js differ
index 54c31d6..68e2b67 100644 (file)
Binary files a/mod/forum/report/summary/amd/build/filters.min.js.map and b/mod/forum/report/summary/amd/build/filters.min.js.map differ
index 55fbf4a..63040c6 100644 (file)
@@ -28,6 +28,7 @@ import CustomEvents from 'core/custom_interaction_events';
 import Selectors from 'forumreport_summary/selectors';
 import Y from 'core/yui';
 import Ajax from 'core/ajax';
+import KeyCodes from 'core/key_codes';
 
 export const init = (root) => {
     let jqRoot = $(root);
@@ -73,6 +74,11 @@ export const init = (root) => {
 
     // Submit report via filter
     const submitWithFilter = (containerelement) => {
+        // Disable the dates filter mform checker to prevent any changes triggering a warning to the user.
+        Y.use('moodle-core-formchangechecker', function() {
+            M.core_formchangechecker.reset_form_dirty_state();
+        });
+
         // Close the container (eg popover).
         $(containerelement).addClass('hidden');
 
@@ -89,14 +95,14 @@ export const init = (root) => {
         new Popper(referenceElement, popperContent, {placement: 'bottom'});
     };
 
-    // Call when opening filter to ensure only one can be activated.
-    const canOpenFilter = (event) => {
-        if (document.querySelector('[data-openfilter="true"]')) {
-            return false;
-        }
+    // Close the relevant filter.
+    var closeOpenFilters = (openFilterButton, openFilter) => {
+        openFilter.classList.add('hidden');
+        openFilter.setAttribute('data-openfilter', 'false');
 
-        event.target.setAttribute('data-openfilter', "true");
-        return true;
+        openFilterButton.classList.add('btn-primary');
+        openFilterButton.classList.remove('btn-outline-primary');
+        openFilterButton.setAttribute('aria-expanded', false);
     };
 
     // Groups filter specific handlers.
@@ -119,11 +125,7 @@ export const init = (root) => {
     });
 
     // Event handler for showing groups filter popover.
-    jqRoot.on(CustomEvents.events.activate, Selectors.filters.group.trigger, function(event) {
-        if (!canOpenFilter(event)) {
-            return false;
-        }
-
+    jqRoot.on(CustomEvents.events.activate, Selectors.filters.group.trigger, function() {
         // Create popover.
         let referenceElement = root.querySelector(Selectors.filters.group.trigger),
             popperContent = root.querySelector(Selectors.filters.group.popover);
@@ -132,6 +134,7 @@ export const init = (root) => {
 
         // Show popover.
         popperContent.classList.remove('hidden');
+        popperContent.setAttribute('data-openfilter', 'true');
 
         // Change to outlined button.
         referenceElement.classList.add('btn-outline-primary');
@@ -139,21 +142,48 @@ export const init = (root) => {
 
         // Let screen readers know that it's now expanded.
         referenceElement.setAttribute('aria-expanded', true);
-        return true;
+
+        // Add listeners to handle closing filter.
+        const closeListener = e => {
+            if (e.target.id !== referenceElement.id && popperContent !== e.target.closest('[data-openfilter="true"]')) {
+                closeOpenFilters(referenceElement, popperContent);
+                document.removeEventListener('click', closeListener);
+                document.removeEventListener('keyup', escCloseListener);
+            }
+        };
+
+        document.addEventListener('click', closeListener);
+
+        const escCloseListener = e => {
+            if (e.keyCode === KeyCodes.escape) {
+                closeOpenFilters(referenceElement, popperContent);
+                document.removeEventListener('keyup', escCloseListener);
+                document.removeEventListener('click', closeListener);
+            }
+        };
+
+        document.addEventListener('keyup', escCloseListener);
     });
 
     // Event handler to click save groups filter.
     jqRoot.on(CustomEvents.events.activate, Selectors.filters.group.save, function() {
+        // Copy the saved values into the form before submitting.
+        let popcheckboxes = root.querySelectorAll(Selectors.filters.group.checkbox);
+
+        popcheckboxes.forEach(function(popcheckbox) {
+            let filtersform = document.forms.filtersform,
+                saveid = popcheckbox.getAttribute('data-saveid');
+
+            filtersform.querySelector(`#${saveid}`).checked = popcheckbox.checked;
+        });
+
         submitWithFilter('#filter-groups-popover');
     });
 
     // Dates filter specific handlers.
 
    // Event handler for showing dates filter popover.
-    jqRoot.on(CustomEvents.events.activate, Selectors.filters.date.trigger, function(event) {
-        if (!canOpenFilter(event)) {
-            return false;
-        }
+    jqRoot.on(CustomEvents.events.activate, Selectors.filters.date.trigger, function() {
 
         // Create popover.
         let referenceElement = root.querySelector(Selectors.filters.date.trigger),
@@ -163,6 +193,7 @@ export const init = (root) => {
 
         // Show popover and move focus.
         popperContent.classList.remove('hidden');
+        popperContent.setAttribute('data-openfilter', 'true');
         popperContent.querySelector('[name="filterdatefrompopover[enabled]"]').focus();
 
         // Change to outlined button.
@@ -171,7 +202,27 @@ export const init = (root) => {
 
         // Let screen readers know that it's now expanded.
         referenceElement.setAttribute('aria-expanded', true);
-        return true;
+
+        // Add listener to handle closing filter.
+        const closeListener = e => {
+            if (e.target.id !== referenceElement.id && popperContent !== e.target.closest('[data-openfilter="true"]')) {
+                closeOpenFilters(referenceElement, popperContent);
+                document.removeEventListener('click', closeListener);
+                document.removeEventListener('keyup', escCloseListener);
+            }
+        };
+
+        document.addEventListener('click', closeListener);
+
+        const escCloseListener = e => {
+            if (e.keyCode === KeyCodes.escape) {
+                closeOpenFilters(referenceElement, popperContent);
+                document.removeEventListener('keyup', escCloseListener);
+                document.removeEventListener('click', closeListener);
+            }
+        };
+
+        document.addEventListener('keyup', escCloseListener);
     });
 
     // Event handler to save dates filter.
@@ -182,11 +233,6 @@ export const init = (root) => {
         const fromEnabled = datesPopover.querySelector('[name="filterdatefrompopover[enabled]"]').checked ? 1 : 0;
         const toEnabled = datesPopover.querySelector('[name="filterdatetopopover[enabled]"]').checked ? 1 : 0;
 
-        // Disable the mform checker to prevent unsubmitted form warning to the user when closing the popover.
-        Y.use('moodle-core-formchangechecker', function() {
-            M.core_formchangechecker.reset_form_dirty_state();
-        });
-
         if (!fromEnabled && !toEnabled) {
             // Update the elements in the filter form.
             filtersForm.elements['datefrom[timestamp]'].value = 0;
index a95b450..e1d6329 100644 (file)
@@ -25,7 +25,7 @@
     }
 }}
 
-<div id="filter-dates-popover" class="popover filter-dates-popover mt-3 hidden">
+<div id="filter-dates-popover" class="popover filter-dates-popover mt-3 hidden" data-openfilter="false">
     <h3 class="popover-header">{{# str}} filter:datesname, forumreport_summary {{/ str}}</h3>
     <div class="popover-body" data-region="filter-dates">
         {{{filterdatesform}}}
index 939ed55..b2a0e03 100644 (file)
     {{filtergroupsname}}
 </button>
 
+{{! Groups filter values to submit }}
+<div class="hidden">
+    {{#filtergroups}}
+    <input id="filtergroups{{groupid}}" class="form-check-input" type="checkbox" name="filtergroups[]"
+        value="{{groupid}}" {{#checked}} checked="checked" {{/checked}}>
+    {{/filtergroups}}
+</div>
+
 {{! Groups filter popover }}
-<div id="filter-groups-popover" class="popover m-t-1 hidden">
+<div id="filter-groups-popover" class="popover m-t-1 hidden" data-openfilter="false">
     <h3 class="popover-header">{{# str}} filter:groupsname, forumreport_summary {{/ str}}</h3>
     <div class="popover-body" data-region="filter-groups">
         <div class="form-check filter-scrollable">
             {{#filtergroups}}
-            <input id="filtergroups{{groupid}}" class="form-check-input" type="checkbox" name="filtergroups[]"
-                value="{{groupid}}" {{#checked}} checked="checked" {{/checked}}>
-            <label class="form-check-label pt-1" for="filtergroups{{groupid}}">{{groupname}}</label>
+            <input id="popperfiltergroups{{groupid}}" class="form-check-input" type="checkbox" name="popperfiltergroups[]"
+                data-saveid="filtergroups{{groupid}}" value="{{groupid}}" {{#checked}} checked="checked" {{/checked}}>
+            <label class="form-check-label pt-1" for="popperfiltergroups{{groupid}}">{{groupname}}</label>
             <br>
             {{/filtergroups}}
         </div>