MDL-85769 AI: Trap focus within AI drawer on smaller screens

Co-authored-by: Laurent David <[email protected]>
This commit is contained in:
meirzamoodle
2025-06-28 10:59:41 +07:00
co-authored by Laurent David
parent 97ed6b7083
commit c080b4bf58
5 changed files with 28 additions and 3 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -31,6 +31,8 @@ import AIHelper from 'core_ai/helper';
import DrawerEvents from 'core/drawer_events';
import {subscribe} from 'core/pubsub';
import * as MessageDrawerHelper from 'core_message/message_drawer_helper';
import * as FocusLock from 'core/local/aria/focuslock';
import {isSmall} from "core/pagehelpers";
const AICourseAssist = class {
@@ -60,6 +62,7 @@ const AICourseAssist = class {
this.jumpToElement = document.querySelector(Selectors.ELEMENTS.JUMPTO);
this.summaryActionElement = document.querySelector(Selectors.ACTIONS.SUMMARY);
this.aiDrawerCloseElement = this.aiDrawerElement.querySelector(Selectors.ELEMENTS.AIDRAWER_CLOSE);
this.isDrawerFocusLocked = false;
this.registerEventListeners();
}
@@ -85,6 +88,12 @@ const AICourseAssist = class {
}
});
document.addEventListener('keydown', e => {
if (this.isAIDrawerOpen() && e.key === 'Escape') {
this.closeAIDrawer();
}
});
// Close AI drawer if message drawer is shown.
subscribe(DrawerEvents.DRAWER_SHOWN, () => {
if (this.isAIDrawerOpen()) {
@@ -198,12 +207,27 @@ const AICourseAssist = class {
}
this.jumpToElement.setAttribute('tabindex', 0);
this.jumpToElement.focus();
// If the AI drawer is opened on a small screen, we need to trap the focus tab within the AI drawer.
if (isSmall()) {
FocusLock.trapFocus(this.aiDrawerElement);
this.aiDrawerElement.setAttribute('aria-modal', 'true');
this.aiDrawerElement.setAttribute('role', 'dialog');
this.isDrawerFocusLocked = true;
}
}
/**
* Close the AI drawer.
*/
closeAIDrawer() {
// Untrap focus if it was locked.
if (this.isDrawerFocusLocked) {
FocusLock.untrapFocus();
this.aiDrawerElement.removeAttribute('aria-modal');
this.aiDrawerElement.setAttribute('role', 'region');
}
this.aiDrawerElement.classList.remove('show');
this.aiDrawerElement.setAttribute('tabindex', '-1');
this.aiDrawerBodyElement.removeAttribute('aria-live');
@@ -22,6 +22,7 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['aidrawerlabel'] = 'AI-generated content drawer';
$string['aisummary'] = 'AI summary';
$string['courseassist:summarise_text'] = 'Summarise text';
$string['copy'] = 'Copy';
@@ -31,7 +31,7 @@
"content": "<p>Content to display</p>"
}
}}
<div class="ai-drawer" id="ai-drawer" tabindex="-1">
<div class="ai-drawer" id="ai-drawer" aria-label={{#quote}}{{#str}} aidrawerlabel, aiplacement_courseassist {{/str}}{{/quote}} tabindex="-1" role="region">
<div class="ai-drawer-header">
<button id="ai-drawer-close" class="btn ai-drawer-button" type="button" data-action="course-summarise">
{{#pix}} e/cancel, core {{/pix}}