**PR Summary**: Label the New Shortcut dialog for accessibility (Bug 1997388)
**Problem**:
The "Add Shortcut" dialog in Firefox’s New Tab page was not labeled for screen readers. This meant assistive technology users only heard "dialog" with no context, making the dialog inaccessible.
**Changes Made:**
Moved aria-labelledby to the <dialog> element in ModalOverlayWrapper:
**Why**: The accessible name for a dialog must be on the dialog element itself, not on a child. This ensures screen readers announce the dialog with a meaningful label.
What was changed: The aria-labelledby attribute was moved from an inner div to the <dialog> element, so the dialog is properly labeled.
Added an id to the TopSiteForm heading:
**Why**: The dialog’s aria-labelledby needs to reference a visible heading that describes the dialog’s purpose. Adding an id to the heading allows it to be referenced directly.
What was changed: The <h3> heading in TopSiteForm was given a unique id.
Passed the heading id as headerId to ModalOverlayWrapper:
**Why**: To connect the dialog’s aria-labelledby to the correct heading, the id was passed as a prop from TopSites to ModalOverlayWrapper.
What was changed: The headerId prop was threaded through the relevant components.
No code was deleted except for moving the aria-labelledby attribute:
**Why**: The only removal was to prevent duplicate or misplaced aria-labelledby attributes, ensuring there is a single, correct accessible label.
Testing Performed:
**Tested**:
Verified in the accessibility panel that the dialog now has an accessible name ("New Shortcut") when opened.
Confirmed that screen readers announce the dialog with the correct label.
Built and ran Firefox to ensure the dialog still functions as expected for all users.
Ensured only the three relevant source files were changed, with no changes to generated files or lockfiles.
**Result**:
The dialog is now properly labeled for assistive technology, improving accessibility and user experience for all users.
Differential Revision: https://phabricator.services.mozilla.com/D282220
62 lines
1.4 KiB
React
62 lines
1.4 KiB
React
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import React, { useEffect, useRef } from "react";
|
|
|
|
function ModalOverlayWrapper({
|
|
unstyled,
|
|
innerClassName,
|
|
onClose,
|
|
children,
|
|
headerId,
|
|
id,
|
|
}) {
|
|
const dialogRef = useRef(null);
|
|
|
|
let className = unstyled ? "" : "modalOverlayInner";
|
|
if (innerClassName) {
|
|
className += ` ${innerClassName}`;
|
|
}
|
|
|
|
useEffect(() => {
|
|
const dialogElement = dialogRef.current;
|
|
if (dialogElement && !dialogElement.open) {
|
|
dialogElement.showModal();
|
|
}
|
|
|
|
const handleCancel = e => {
|
|
e.preventDefault();
|
|
onClose(e);
|
|
};
|
|
|
|
dialogElement?.addEventListener("cancel", handleCancel);
|
|
|
|
return () => {
|
|
dialogElement?.removeEventListener("cancel", handleCancel);
|
|
if (dialogElement && dialogElement.open) {
|
|
dialogElement.close();
|
|
}
|
|
};
|
|
}, [onClose]);
|
|
|
|
return (
|
|
<dialog
|
|
ref={dialogRef}
|
|
className="modalOverlayOuter"
|
|
onClick={e => {
|
|
if (e.target === dialogRef.current) {
|
|
onClose(e);
|
|
}
|
|
}}
|
|
{...(headerId ? { "aria-labelledby": headerId } : {})}
|
|
>
|
|
<div className={className} id={id}>
|
|
{children}
|
|
</div>
|
|
</dialog>
|
|
);
|
|
}
|
|
|
|
export { ModalOverlayWrapper };
|