Fix modal dialogs being hidden from assistive technology by aria-hidden on the application root. #9865 (#10587)
This commit is contained in:
@@ -366,9 +366,11 @@ function ModalContainer({ id, title, content, dialogHeight, dialogWidth, onClose
|
||||
}
|
||||
};
|
||||
const [isFullScreen, setIsFullScreen] = useState(fullScreen);
|
||||
const dialogRef = useRef(null);
|
||||
|
||||
return (
|
||||
<StyleDialog
|
||||
ref={dialogRef}
|
||||
open={true}
|
||||
onClose={closeModal}
|
||||
PaperComponent={PaperComponent}
|
||||
@@ -380,6 +382,14 @@ function ModalContainer({ id, title, content, dialogHeight, dialogWidth, onClose
|
||||
fullScreen={isFullScreen}
|
||||
fullWidth={isFullWidth}
|
||||
disablePortal
|
||||
// The dialog is rendered in place (disablePortal), so it sits inside
|
||||
// the application root rather than directly under <body>. MUI's modal
|
||||
// manager sets aria-hidden on the children of its container, which
|
||||
// defaults to <body>, so it would hide the application root and the
|
||||
// dialog along with it, whilst focus is inside the dialog. Use the
|
||||
// dialog's own parent as the container so that only its siblings, the
|
||||
// background content, are hidden.
|
||||
container={() => dialogRef.current?.parentElement}
|
||||
{...props}
|
||||
>
|
||||
{ showTitle &&
|
||||
|
||||
@@ -66,7 +66,6 @@ describe('ModalProvider AlertContent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
describe('ModalProvider keeps focus in an open modal (#9784)', () => {
|
||||
let iframe;
|
||||
|
||||
@@ -166,3 +165,37 @@ describe('ModalProvider keeps focus in an open modal (#9784)', () => {
|
||||
expect(document.activeElement).toBe(input);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ModalProvider aria-hidden handling', () => {
|
||||
let modalApi;
|
||||
function CaptureModal() {
|
||||
modalApi = useModal();
|
||||
return <button>Background action</button>;
|
||||
}
|
||||
const ThemedProvider = withTheme(ModalProvider);
|
||||
|
||||
it('hides the background content but not the dialog itself',
|
||||
async () => {
|
||||
const ctrl = await act(async () => render(
|
||||
<ThemedProvider><CaptureModal /></ThemedProvider>
|
||||
));
|
||||
const background = screen.getByText('Background action');
|
||||
background.focus();
|
||||
|
||||
await act(async () => {
|
||||
modalApi.confirm('Reset statistics', 'Are you sure?', () => {});
|
||||
});
|
||||
|
||||
const dialog = screen.getByRole('dialog');
|
||||
expect(dialog.closest('[aria-hidden="true"]')).toBeNull();
|
||||
expect(dialog.contains(document.activeElement)).toBe(true);
|
||||
expect(background.closest('[aria-hidden="true"]')).not.toBeNull();
|
||||
expect(ctrl.container.closest('[aria-hidden="true"]')).toBeNull();
|
||||
|
||||
await act(async () => {
|
||||
screen.getByText('No').click();
|
||||
});
|
||||
expect(screen.queryByRole('dialog')).toBeNull();
|
||||
expect(background.closest('[aria-hidden="true"]')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user