diff --git a/grade/amd/build/searchwidget/basewidget.min.js b/grade/amd/build/searchwidget/basewidget.min.js
index bdeff4f616a..572ebd934cb 100644
--- a/grade/amd/build/searchwidget/basewidget.min.js
+++ b/grade/amd/build/searchwidget/basewidget.min.js
@@ -5,6 +5,6 @@ define("core_grades/searchwidget/basewidget",["exports","core/utils","core/templ
    * @module    core_grades/searchwidget/basewidget
    * @copyright 2022 Mathew May <mathew.solutions>
    * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
-   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showLoader=_exports.registerListenerEvents=_exports.promisesAndResolvers=_exports.init=void 0,Templates=_interopRequireWildcard(Templates),Selectors=_interopRequireWildcard(Selectors),_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};_exports.init=async function(widgetContentContainer,bodyPromise,data,searchFunc){let unsearchableContent=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;bodyPromise.then((async bodyContent=>{if(widgetContentContainer.innerHTML=bodyContent,unsearchableContent){widgetContentContainer.querySelector(Selectors.regions.unsearchableContent).innerHTML+=unsearchableContent}const searchResultsContainer=widgetContentContainer.querySelector(Selectors.regions.searchResults);await showLoader(searchResultsContainer),await renderSearchResults(searchResultsContainer,data),registerListenerEvents(widgetContentContainer,data,searchFunc)})).catch(_notification.default.exception)};const registerListenerEvents=(widgetContentContainer,data,searchFunc)=>{const searchResultsContainer=widgetContentContainer.querySelector(Selectors.regions.searchResults),searchInput=widgetContentContainer.querySelector(Selectors.actions.search);searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}),300)),(0,_aria.comboBox)(searchInput)};_exports.registerListenerEvents=registerListenerEvents;const showLoader=async container=>{const{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/loading",{});Templates.replaceNodeContents(container,html,js)};_exports.showLoader=showLoader;const debounceCallee=(searchValue,data,searchFunction)=>searchValue.length>0?searchFunction(data,searchValue):data,renderSearchResults=async(searchResultsContainer,searchResultsData)=>{const templateData={searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/searchresults",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)};_exports.promisesAndResolvers=()=>{let bodyPromiseResolver;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve}));return{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}}}));
+   */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showLoader=_exports.registerListenerEvents=_exports.promisesAndResolvers=_exports.init=void 0,Templates=_interopRequireWildcard(Templates),Selectors=_interopRequireWildcard(Selectors),_notification=(obj=_notification)&&obj.__esModule?obj:{default:obj};_exports.init=async function(widgetContentContainer,bodyPromise,data,searchFunc){let unsearchableContent=arguments.length>4&&void 0!==arguments[4]?arguments[4]:null;bodyPromise.then((async bodyContent=>{if(widgetContentContainer.innerHTML=bodyContent,unsearchableContent){widgetContentContainer.querySelector(Selectors.regions.unsearchableContent).innerHTML+=unsearchableContent}const searchResultsContainer=widgetContentContainer.querySelector(Selectors.regions.searchResults);await showLoader(searchResultsContainer),await renderSearchResults(searchResultsContainer,data),registerListenerEvents(widgetContentContainer,data,searchFunc)})).catch(_notification.default.exception)};const registerListenerEvents=(widgetContentContainer,data,searchFunc)=>{const searchResultsContainer=widgetContentContainer.querySelector(Selectors.regions.searchResults),searchInput=widgetContentContainer.querySelector(Selectors.actions.search),clearSearchButton=widgetContentContainer.querySelector(Selectors.actions.clearSearch);searchInput.addEventListener("input",(0,_utils.debounce)((async()=>{searchInput.value.length>0?clearSearchButton.classList.remove("d-none"):clearSearchButton.classList.add("d-none"),await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))}),300)),clearSearchButton.addEventListener("click",(async e=>{e.stopPropagation(),searchInput.value="",searchInput.focus(),clearSearchButton.classList.add("d-none"),await renderSearchResults(searchResultsContainer,debounceCallee(searchInput.value,data,searchFunc()))})),(0,_aria.comboBox)(searchInput)};_exports.registerListenerEvents=registerListenerEvents;const showLoader=async container=>{const{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/loading",{});Templates.replaceNodeContents(container,html,js)};_exports.showLoader=showLoader;const debounceCallee=(searchValue,data,searchFunction)=>searchValue.length>0?searchFunction(data,searchValue):data,renderSearchResults=async(searchResultsContainer,searchResultsData)=>{const templateData={searchresults:searchResultsData},{html:html,js:js}=await Templates.renderForPromise("core_grades/searchwidget/searchresults",templateData);await Templates.replaceNodeContents(searchResultsContainer,html,js)};_exports.promisesAndResolvers=()=>{let bodyPromiseResolver;const bodyPromise=new Promise((resolve=>{bodyPromiseResolver=resolve}));return{bodyPromiseResolver:bodyPromiseResolver,bodyPromise:bodyPromise}}}));
 
 //# sourceMappingURL=basewidget.min.js.map
\ No newline at end of file
diff --git a/grade/amd/build/searchwidget/basewidget.min.js.map b/grade/amd/build/searchwidget/basewidget.min.js.map
index 336ad75119c..0a24d4c0875 100644
--- a/grade/amd/build/searchwidget/basewidget.min.js.map
+++ b/grade/amd/build/searchwidget/basewidget.min.js.map
@@ -1 +1 @@
-{"version":3,"file":"basewidget.min.js","sources":["../../src/searchwidget/basewidget.js"],"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 * A widget to search users or grade items within the gradebook.\n *\n * @module    core_grades/searchwidget/basewidget\n * @copyright 2022 Mathew May <mathew.solutions>\n * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {debounce} from 'core/utils';\nimport * as Templates from 'core/templates';\nimport {comboBox} from 'core/aria';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\nimport Notification from 'core/notification';\n\n/**\n * Build the base searching widget.\n *\n * @method init\n * @param {HTMLElement} widgetContentContainer The selector for the widget container element.\n * @param {Promise} bodyPromise The promise from the callee of the contents to place in the widget container.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.\n * @param {string|null} unsearchableContent The content rendered in a non-searchable area.\n */\nexport const init = async(widgetContentContainer, bodyPromise, data, searchFunc, unsearchableContent = null) => {\n    bodyPromise.then(async(bodyContent) => {\n        // Render the body content.\n        widgetContentContainer.innerHTML = bodyContent;\n\n        // Render the unsearchable content if defined.\n        if (unsearchableContent) {\n            const unsearchableContentContainer = widgetContentContainer.querySelector(Selectors.regions.unsearchableContent);\n            unsearchableContentContainer.innerHTML += unsearchableContent;\n        }\n\n        const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);\n        // Display a loader until the search results are rendered.\n        await showLoader(searchResultsContainer);\n        // Render the search results.\n        await renderSearchResults(searchResultsContainer, data);\n\n        registerListenerEvents(widgetContentContainer, data, searchFunc);\n\n    }).catch(Notification.exception);\n};\n\n/**\n * Register the event listeners for the search widget.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} widgetContentContainer The selector for the widget container element.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.\n */\nexport const registerListenerEvents = (widgetContentContainer, data, searchFunc) => {\n    const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);\n    const searchInput = widgetContentContainer.querySelector(Selectors.actions.search);\n\n    // The search input is triggered.\n    searchInput.addEventListener('input', debounce(async() => {\n        // Display the search results.\n        await renderSearchResults(\n            searchResultsContainer,\n            debounceCallee(\n                searchInput.value,\n                data,\n                searchFunc()\n            )\n        );\n    }, 300));\n    // Trigger event handling for the results in line with aria guidelines.\n    comboBox(searchInput);\n};\n\n/**\n * Renders the loading placeholder for the search widget.\n *\n * @method showLoader\n * @param {HTMLElement} container The DOM node where we'll render the loading placeholder.\n */\nexport const showLoader = async(container) => {\n    const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/loading', {});\n    Templates.replaceNodeContents(container, html, js);\n};\n\n/**\n * We have a small helper that'll call the curried search function allowing callers to filter\n * the data set however we want rather than defining how data must be filtered.\n *\n * @method debounceCallee\n * @param {String} searchValue The input from the user that we'll search against.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunction Partially applied function we need to manage search the passed dataset.\n * @return {Array} The filtered subset of the provided data that we'll then render into the results.\n */\nconst debounceCallee = (searchValue, data, searchFunction) => {\n    if (searchValue.length > 0) { // Search query is present.\n        return searchFunction(data, searchValue);\n    }\n    return data;\n};\n\n/**\n * Given the output of the callers' search function, render out the results into the search results container.\n *\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The DOM node of the widget where we'll render the provided results.\n * @param {Array} searchResultsData The filtered subset of the provided data that we'll then render into the results.\n */\nconst renderSearchResults = async(searchResultsContainer, searchResultsData) => {\n    const templateData = {\n        'searchresults': searchResultsData,\n    };\n    // Build up the html & js ready to place into the help section.\n    const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/searchresults', templateData);\n    await Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * We want to create the basic promises and hooks that the caller will implement, so we can build the search widget\n * ahead of time and allow the caller to resolve their promises once complete.\n *\n * @method promisesAndResolvers\n * @returns {{bodyPromise: Promise, bodyPromiseResolver}}\n */\nexport const promisesAndResolvers = () => {\n    // We want to show the widget instantly but loading whilst waiting for our data.\n    let bodyPromiseResolver;\n    const bodyPromise = new Promise(resolve => {\n        bodyPromiseResolver = resolve;\n    });\n\n    return {bodyPromiseResolver, bodyPromise};\n};\n"],"names":["async","widgetContentContainer","bodyPromise","data","searchFunc","unsearchableContent","then","innerHTML","bodyContent","querySelector","Selectors","regions","searchResultsContainer","searchResults","showLoader","renderSearchResults","registerListenerEvents","catch","Notification","exception","searchInput","actions","search","addEventListener","debounceCallee","value","html","js","Templates","renderForPromise","replaceNodeContents","container","searchValue","searchFunction","length","searchResultsData","templateData","bodyPromiseResolver","Promise","resolve"],"mappings":";;;;;;;gVAsCoBA,eAAMC,uBAAwBC,YAAaC,KAAMC,gBAAYC,2EAAsB,KACnGH,YAAYI,MAAKN,MAAAA,iBAEbC,uBAAuBM,UAAYC,YAG/BH,oBAAqB,CACgBJ,uBAAuBQ,cAAcC,UAAUC,QAAQN,qBAC/DE,WAAaF,0BAGxCO,uBAAyBX,uBAAuBQ,cAAcC,UAAUC,QAAQE,qBAEhFC,WAAWF,8BAEXG,oBAAoBH,uBAAwBT,MAElDa,uBAAuBf,uBAAwBE,KAAMC,eAEtDa,MAAMC,sBAAaC,kBAWbH,uBAAyB,CAACf,uBAAwBE,KAAMC,oBAC3DQ,uBAAyBX,uBAAuBQ,cAAcC,UAAUC,QAAQE,eAChFO,YAAcnB,uBAAuBQ,cAAcC,UAAUW,QAAQC,QAG3EF,YAAYG,iBAAiB,SAAS,oBAASvB,gBAErCe,oBACFH,uBACAY,eACIJ,YAAYK,MACZtB,KACAC,iBAGT,yBAEMgB,2EASAN,WAAad,MAAAA,kBAChB0B,KAACA,KAADC,GAAOA,UAAYC,UAAUC,iBAAiB,mCAAoC,IACxFD,UAAUE,oBAAoBC,UAAWL,KAAMC,0CAa7CH,eAAiB,CAACQ,YAAa7B,KAAM8B,iBACnCD,YAAYE,OAAS,EACdD,eAAe9B,KAAM6B,aAEzB7B,KAULY,oBAAsBf,MAAMY,uBAAwBuB,2BAChDC,aAAe,eACAD,oBAGfT,KAACA,KAADC,GAAOA,UAAYC,UAAUC,iBAAiB,yCAA0CO,oBACxFR,UAAUE,oBAAoBlB,uBAAwBc,KAAMC,mCAUlC,SAE5BU,0BACEnC,YAAc,IAAIoC,SAAQC,UAC5BF,oBAAsBE,iBAGnB,CAACF,oBAAAA,oBAAqBnC,YAAAA"}
\ No newline at end of file
+{"version":3,"file":"basewidget.min.js","sources":["../../src/searchwidget/basewidget.js"],"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 * A widget to search users or grade items within the gradebook.\n *\n * @module    core_grades/searchwidget/basewidget\n * @copyright 2022 Mathew May <mathew.solutions>\n * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {debounce} from 'core/utils';\nimport * as Templates from 'core/templates';\nimport {comboBox} from 'core/aria';\nimport * as Selectors from 'core_grades/searchwidget/selectors';\nimport Notification from 'core/notification';\n\n/**\n * Build the base searching widget.\n *\n * @method init\n * @param {HTMLElement} widgetContentContainer The selector for the widget container element.\n * @param {Promise} bodyPromise The promise from the callee of the contents to place in the widget container.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.\n * @param {string|null} unsearchableContent The content rendered in a non-searchable area.\n */\nexport const init = async(widgetContentContainer, bodyPromise, data, searchFunc, unsearchableContent = null) => {\n    bodyPromise.then(async(bodyContent) => {\n        // Render the body content.\n        widgetContentContainer.innerHTML = bodyContent;\n\n        // Render the unsearchable content if defined.\n        if (unsearchableContent) {\n            const unsearchableContentContainer = widgetContentContainer.querySelector(Selectors.regions.unsearchableContent);\n            unsearchableContentContainer.innerHTML += unsearchableContent;\n        }\n\n        const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);\n        // Display a loader until the search results are rendered.\n        await showLoader(searchResultsContainer);\n        // Render the search results.\n        await renderSearchResults(searchResultsContainer, data);\n\n        registerListenerEvents(widgetContentContainer, data, searchFunc);\n\n    }).catch(Notification.exception);\n};\n\n/**\n * Register the event listeners for the search widget.\n *\n * @method registerListenerEvents\n * @param {HTMLElement} widgetContentContainer The selector for the widget container element.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunc Partially applied function we need to manage search the passed dataset.\n */\nexport const registerListenerEvents = (widgetContentContainer, data, searchFunc) => {\n    const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);\n    const searchInput = widgetContentContainer.querySelector(Selectors.actions.search);\n    const clearSearchButton = widgetContentContainer.querySelector(Selectors.actions.clearSearch);\n\n    // The search input is triggered.\n    searchInput.addEventListener('input', debounce(async() => {\n        // If search query is present display the 'clear search' button, otherwise hide it.\n        if (searchInput.value.length > 0) {\n            clearSearchButton.classList.remove('d-none');\n        } else {\n            clearSearchButton.classList.add('d-none');\n        }\n        // Display the search results.\n        await renderSearchResults(\n            searchResultsContainer,\n            debounceCallee(\n                searchInput.value,\n                data,\n                searchFunc()\n            )\n        );\n    }, 300));\n\n    // Clear search is triggered.\n    clearSearchButton.addEventListener('click', async(e) => {\n        e.stopPropagation();\n        // Clear the entered search query in the search bar.\n        searchInput.value = \"\";\n        searchInput.focus();\n        clearSearchButton.classList.add('d-none');\n\n        // Display all results.\n        await renderSearchResults(\n            searchResultsContainer,\n            debounceCallee(\n                searchInput.value,\n                data,\n                searchFunc()\n            )\n        );\n    });\n\n    // Trigger event handling for the results in line with aria guidelines.\n    comboBox(searchInput);\n};\n\n/**\n * Renders the loading placeholder for the search widget.\n *\n * @method showLoader\n * @param {HTMLElement} container The DOM node where we'll render the loading placeholder.\n */\nexport const showLoader = async(container) => {\n    const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/loading', {});\n    Templates.replaceNodeContents(container, html, js);\n};\n\n/**\n * We have a small helper that'll call the curried search function allowing callers to filter\n * the data set however we want rather than defining how data must be filtered.\n *\n * @method debounceCallee\n * @param {String} searchValue The input from the user that we'll search against.\n * @param {Array} data An array of all the data generated by the callee.\n * @param {Function} searchFunction Partially applied function we need to manage search the passed dataset.\n * @return {Array} The filtered subset of the provided data that we'll then render into the results.\n */\nconst debounceCallee = (searchValue, data, searchFunction) => {\n    if (searchValue.length > 0) { // Search query is present.\n        return searchFunction(data, searchValue);\n    }\n    return data;\n};\n\n/**\n * Given the output of the callers' search function, render out the results into the search results container.\n *\n * @method renderSearchResults\n * @param {HTMLElement} searchResultsContainer The DOM node of the widget where we'll render the provided results.\n * @param {Array} searchResultsData The filtered subset of the provided data that we'll then render into the results.\n */\nconst renderSearchResults = async(searchResultsContainer, searchResultsData) => {\n    const templateData = {\n        'searchresults': searchResultsData,\n    };\n    // Build up the html & js ready to place into the help section.\n    const {html, js} = await Templates.renderForPromise('core_grades/searchwidget/searchresults', templateData);\n    await Templates.replaceNodeContents(searchResultsContainer, html, js);\n};\n\n/**\n * We want to create the basic promises and hooks that the caller will implement, so we can build the search widget\n * ahead of time and allow the caller to resolve their promises once complete.\n *\n * @method promisesAndResolvers\n * @returns {{bodyPromise: Promise, bodyPromiseResolver}}\n */\nexport const promisesAndResolvers = () => {\n    // We want to show the widget instantly but loading whilst waiting for our data.\n    let bodyPromiseResolver;\n    const bodyPromise = new Promise(resolve => {\n        bodyPromiseResolver = resolve;\n    });\n\n    return {bodyPromiseResolver, bodyPromise};\n};\n"],"names":["async","widgetContentContainer","bodyPromise","data","searchFunc","unsearchableContent","then","innerHTML","bodyContent","querySelector","Selectors","regions","searchResultsContainer","searchResults","showLoader","renderSearchResults","registerListenerEvents","catch","Notification","exception","searchInput","actions","search","clearSearchButton","clearSearch","addEventListener","value","length","classList","remove","add","debounceCallee","e","stopPropagation","focus","html","js","Templates","renderForPromise","replaceNodeContents","container","searchValue","searchFunction","searchResultsData","templateData","bodyPromiseResolver","Promise","resolve"],"mappings":";;;;;;;gVAsCoBA,eAAMC,uBAAwBC,YAAaC,KAAMC,gBAAYC,2EAAsB,KACnGH,YAAYI,MAAKN,MAAAA,iBAEbC,uBAAuBM,UAAYC,YAG/BH,oBAAqB,CACgBJ,uBAAuBQ,cAAcC,UAAUC,QAAQN,qBAC/DE,WAAaF,0BAGxCO,uBAAyBX,uBAAuBQ,cAAcC,UAAUC,QAAQE,qBAEhFC,WAAWF,8BAEXG,oBAAoBH,uBAAwBT,MAElDa,uBAAuBf,uBAAwBE,KAAMC,eAEtDa,MAAMC,sBAAaC,kBAWbH,uBAAyB,CAACf,uBAAwBE,KAAMC,oBAC3DQ,uBAAyBX,uBAAuBQ,cAAcC,UAAUC,QAAQE,eAChFO,YAAcnB,uBAAuBQ,cAAcC,UAAUW,QAAQC,QACrEC,kBAAoBtB,uBAAuBQ,cAAcC,UAAUW,QAAQG,aAGjFJ,YAAYK,iBAAiB,SAAS,oBAASzB,UAEvCoB,YAAYM,MAAMC,OAAS,EAC3BJ,kBAAkBK,UAAUC,OAAO,UAEnCN,kBAAkBK,UAAUE,IAAI,gBAG9Bf,oBACFH,uBACAmB,eACIX,YAAYM,MACZvB,KACAC,iBAGT,MAGHmB,kBAAkBE,iBAAiB,SAASzB,MAAAA,IACxCgC,EAAEC,kBAEFb,YAAYM,MAAQ,GACpBN,YAAYc,QACZX,kBAAkBK,UAAUE,IAAI,gBAG1Bf,oBACFH,uBACAmB,eACIX,YAAYM,MACZvB,KACAC,qCAMHgB,2EASAN,WAAad,MAAAA,kBAChBmC,KAACA,KAADC,GAAOA,UAAYC,UAAUC,iBAAiB,mCAAoC,IACxFD,UAAUE,oBAAoBC,UAAWL,KAAMC,0CAa7CL,eAAiB,CAACU,YAAatC,KAAMuC,iBACnCD,YAAYd,OAAS,EACde,eAAevC,KAAMsC,aAEzBtC,KAULY,oBAAsBf,MAAMY,uBAAwB+B,2BAChDC,aAAe,eACAD,oBAGfR,KAACA,KAADC,GAAOA,UAAYC,UAAUC,iBAAiB,yCAA0CM,oBACxFP,UAAUE,oBAAoB3B,uBAAwBuB,KAAMC,mCAUlC,SAE5BS,0BACE3C,YAAc,IAAI4C,SAAQC,UAC5BF,oBAAsBE,iBAGnB,CAACF,oBAAAA,oBAAqB3C,YAAAA"}
\ No newline at end of file
diff --git a/grade/amd/src/searchwidget/basewidget.js b/grade/amd/src/searchwidget/basewidget.js
index cb593bc349b..e9cb47cbcae 100644
--- a/grade/amd/src/searchwidget/basewidget.js
+++ b/grade/amd/src/searchwidget/basewidget.js
@@ -69,9 +69,16 @@ export const init = async(widgetContentContainer, bodyPromise, data, searchFunc,
 export const registerListenerEvents = (widgetContentContainer, data, searchFunc) => {
     const searchResultsContainer = widgetContentContainer.querySelector(Selectors.regions.searchResults);
     const searchInput = widgetContentContainer.querySelector(Selectors.actions.search);
+    const clearSearchButton = widgetContentContainer.querySelector(Selectors.actions.clearSearch);
 
     // The search input is triggered.
     searchInput.addEventListener('input', debounce(async() => {
+        // If search query is present display the 'clear search' button, otherwise hide it.
+        if (searchInput.value.length > 0) {
+            clearSearchButton.classList.remove('d-none');
+        } else {
+            clearSearchButton.classList.add('d-none');
+        }
         // Display the search results.
         await renderSearchResults(
             searchResultsContainer,
@@ -82,6 +89,26 @@ export const registerListenerEvents = (widgetContentContainer, data, searchFunc)
             )
         );
     }, 300));
+
+    // Clear search is triggered.
+    clearSearchButton.addEventListener('click', async(e) => {
+        e.stopPropagation();
+        // Clear the entered search query in the search bar.
+        searchInput.value = "";
+        searchInput.focus();
+        clearSearchButton.classList.add('d-none');
+
+        // Display all results.
+        await renderSearchResults(
+            searchResultsContainer,
+            debounceCallee(
+                searchInput.value,
+                data,
+                searchFunc()
+            )
+        );
+    });
+
     // Trigger event handling for the results in line with aria guidelines.
     comboBox(searchInput);
 };