Skip to content

Commit

Permalink
fix(Modal): remove aria-hidden from siblings when unmounting the co…
Browse files Browse the repository at this point in the history
…mponent (v5) (#9110)

* fix(Modal): remove aria-hidden when unmount

The component adds the aria-hidden attribute to its siblings when open
and removes it when closed. However, if the component is mounted as open
but then directly unmounted, its siblings will remain hidden from the
accessibility API forever.

Thus, the logic for removing these attributes is now also triggered when
the component is unmounted.

* fix(Modal): remove trailing spaces in test file
  • Loading branch information
dgdavid authored Jul 31, 2023
1 parent 8f7da4f commit 3f11b91
Show file tree
Hide file tree
Showing 2 changed files with 67 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/react-core/src/components/Modal/Modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ export class Modal extends React.Component<ModalProps, ModalState> {
}
target.removeEventListener('keydown', this.handleEscKeyClick, false);
target.classList.remove(css(styles.backdropOpen));
this.toggleSiblingsFromScreenReaders(false);
}

render() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,26 @@ const props = {
children: 'modal content'
};

const target = document.createElement('div');

const ModalWithSiblings = () => {
const [isOpen, setIsOpen] = React.useState(true);
const [isModalMounted, setIsModalMounted] = React.useState(true);
const modalProps = { ...props, isOpen, appendTo: target, onClose: () => setIsOpen(false) };

return (
<>
<aside>Aside sibling</aside>
<article>Section sibling</article>
{isModalMounted && (
<Modal {...modalProps}>
<button onClick={() => setIsModalMounted(false)}>Unmount Modal</button>
</Modal>
)}
</>
);
};

describe('Modal', () => {
test('Modal creates a container element once for div', () => {
render(<Modal {...props} />);
Expand All @@ -27,7 +47,7 @@ describe('Modal', () => {

test('modal closes with escape', async () => {
const user = userEvent.setup();

render(<Modal {...props} isOpen appendTo={document.body} />);

await user.type(screen.getByText(props.title), `{${KeyTypes.Escape}}`);
Expand Down Expand Up @@ -89,6 +109,51 @@ describe('Modal', () => {
expect(consoleErrorMock).toHaveBeenCalled();
});

test('modal adds aria-hidden attribute to its siblings when open', () => {
render(<ModalWithSiblings />, { container: document.body.appendChild(target) });

const asideSibling = screen.getByRole('complementary', { hidden: true });
const articleSibling = screen.getByRole('article', { hidden: true });

expect(asideSibling).toHaveAttribute('aria-hidden');
expect(articleSibling).toHaveAttribute('aria-hidden');
});

test('modal removes the aria-hidden attribute from its siblings when closed', async () => {
const user = userEvent.setup();

render(<ModalWithSiblings />, { container: document.body.appendChild(target) });

const asideSibling = screen.getByRole('complementary', { hidden: true });
const articleSibling = screen.getByRole('article', { hidden: true });
const closeButton = screen.getByRole('button', { name: 'Close' });

expect(articleSibling).toHaveAttribute('aria-hidden');
expect(asideSibling).toHaveAttribute('aria-hidden');

await user.click(closeButton);

expect(articleSibling).not.toHaveAttribute('aria-hidden');
expect(asideSibling).not.toHaveAttribute('aria-hidden');
});

test('modal removes the aria-hidden attribute from its siblings when unmounted', async () => {
const user = userEvent.setup();

render(<ModalWithSiblings />, { container: document.body.appendChild(target) });

const asideSibling = screen.getByRole('complementary', { hidden: true });
const articleSibling = screen.getByRole('article', { hidden: true });
const unmountButton = screen.getByRole('button', { name: 'Unmount Modal' });

expect(asideSibling).toHaveAttribute('aria-hidden');
expect(articleSibling).toHaveAttribute('aria-hidden');

await user.click(unmountButton);

expect(asideSibling).not.toHaveAttribute('aria-hidden');
expect(articleSibling).not.toHaveAttribute('aria-hidden');
});
test('The modalBoxBody has no aria-label when bodyAriaLabel is not passed', () => {
const props = {
isOpen: true
Expand Down

0 comments on commit 3f11b91

Please sign in to comment.