Skip to content

Commit eab6121

Browse files
Merge pull request #652 from OneNoteDev/copilot/verify-accessibility-voice-access
A11y Bug 8780932: Fix Voice Access numbering background controls when clipper is open (A11y MAS 4.3.1)
2 parents fb7808e + 271fb4c commit eab6121

File tree

3 files changed

+32
-1
lines changed

3 files changed

+32
-1
lines changed

src/scripts/clipperUI/mainController.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -431,7 +431,8 @@ export class MainControllerClass extends ComponentBase<MainControllerState, Main
431431
<div
432432
id={Constants.Ids.mainController}
433433
{...this.onElementDraw(this.onMainControllerDraw)}
434-
role="main"
434+
role="dialog"
435+
aria-modal="true"
435436
aria-label={Localization.getLocalizedString("WebClipper.Label.OneNoteWebClipper")} >
436437
{closeButtonToRender}
437438
<div className="panelContent">

src/scripts/extensions/clipperInject.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,17 @@ export class ClipperInject extends FrameInjectBase<ClipperInjectOptions> {
8484
this.updateUiSizeAttributes();
8585
this.overrideTransformStyles(document.documentElement);
8686

87+
// The clipper frame is now visible; hide page body from assistive technologies
88+
// so that tools like Voice Access do not number background page controls.
89+
// The frame is appended to <html> (not <body>), so marking <body aria-hidden>
90+
// and inert only hides page content while keeping the clipper iframe accessible.
91+
// aria-hidden removes body from the AT tree; inert also disables pointer events
92+
// so Voice Access cannot enumerate or activate background controls.
93+
if (document.body) {
94+
document.body.setAttribute("aria-hidden", "true");
95+
document.body.setAttribute("inert", "");
96+
}
97+
8798
this.logger = new CommunicatorLoggerPure(this.uiCommunicator);
8899

89100
this.updatePageInfo();
@@ -316,6 +327,10 @@ export class ClipperInject extends FrameInjectBase<ClipperInjectOptions> {
316327

317328
this.uiCommunicator.registerFunction(Constants.FunctionKeys.hideUi, () => {
318329
this.frame.style.display = "none";
330+
if (document.body) {
331+
document.body.removeAttribute("aria-hidden");
332+
document.body.removeAttribute("inert");
333+
}
319334
});
320335

321336
this.uiCommunicator.registerFunction(Constants.FunctionKeys.refreshPage, () => {
@@ -375,6 +390,10 @@ export class ClipperInject extends FrameInjectBase<ClipperInjectOptions> {
375390
private toggleClipper() {
376391
if (this.frame.style.display === "none") {
377392
this.frame.style.display = "";
393+
if (document.body) {
394+
document.body.setAttribute("aria-hidden", "true");
395+
document.body.setAttribute("inert", "");
396+
}
378397
}
379398
this.uiCommunicator.callRemoteFunction(Constants.FunctionKeys.toggleClipper);
380399
}

src/tests/clipperUI/mainController_tests.tsx

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,17 @@ export class MainControllerTests extends TestModule {
511511
"The close button should render when the clipper is not clipping to OneNote API");
512512
}
513513
});
514+
515+
test("The main controller should have role='dialog' and aria-modal='true' for accessibility", () => {
516+
MithrilUtils.mountToFixture(this.defaultComponent);
517+
518+
let mainController = document.getElementById(Constants.Ids.mainController);
519+
ok(mainController, "The main controller element should exist");
520+
strictEqual(mainController.getAttribute("role"), "dialog",
521+
"The main controller should have role='dialog' to prevent Voice Access from numbering background controls");
522+
strictEqual(mainController.getAttribute("aria-modal"), "true",
523+
"The main controller should have aria-modal='true' to mark it as a modal dialog");
524+
});
514525
}
515526

516527
private getMockRequestError(): OneNoteApi.RequestError {

0 commit comments

Comments
 (0)