Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix incorrect handling of nested dialogs #399

Merged
merged 5 commits into from
Aug 17, 2022
Merged

Conversation

KittyGiraudel
Copy link
Owner

@KittyGiraudel KittyGiraudel commented Aug 11, 2022

It turns out that the demo for nested dialogs was not working correctly with VoiceOver on Safari. Nothing gets announced passed the first open dialog.

That’s because the dialogs’ DOM tree were laid out flat (as an implementation artifact from the old system from version 6). As a result, “lower“ dialogs ended up outside of the aria-modal="true" container of the “higher” dialogs. Because of this, VoiceOver on Safari would be unable to pick up their content.

This problem can be solved by properly nesting the nested dialogs’ DOM tree so that “lower” dialogs live within the aria-modal="true" container of their “higher” dialog. This way, an open dialog is never outside of an element with aria-modal="true".

<!-- Before -->
<div data-a11y-dialog="dialog-1" aria-labelledby="dialog-title-1"></div>
<div data-a11y-dialog="dialog-2" aria-labelledby="dialog-title-2"></div>
<div data-a11y-dialog="dialog-3" aria-labelledby="dialog-title-3"></div>

<!-- Now -->
<div data-a11y-dialog="dialog-1" aria-labelledby="dialog-title-1"><div data-a11y-dialog="dialog-2" aria-labelledby="dialog-title-2"><div data-a11y-dialog="dialog-3" aria-labelledby="dialog-title-3"></div>
  </div>
</div>

Unfortunately, that makes the code to retrieve the elements that can close the dialog a bit more cumbersome since it needs to account for potentially nested dialogs. This code is not quite bulletproof, but it should do the trick in most scenarios, especially when coupled with better documentation. It will no longer be an issue in version 8 thanks to event delegation.

Kind thanks to @NanneWD for reporting that issue.

@KittyGiraudel KittyGiraudel changed the title Fix incorrect hangling of nested dialogs Fix incorrect handling of nested dialogs Aug 11, 2022
@@ -13,15 +13,15 @@ describe('Nested dialogs', () => {
})

it('should close only the top most dialog when pressing ESC', () => {
cy.get('body').trigger('keydown', { keyCode: 27, which: 27 })
cy.get('html').trigger('keydown', { key: 'Escape', keyCode: 27, which: 27 })
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

First , the keydown event is bound to the document, and not the body element (although I’m pretty sure it doesn’t matter in that case). Secondly, the event listener looks at the key property of the event since this commit, so this was just not working.

cy.get('[data-a11y-dialog="dialog-2"]').then(shouldBeVisible)
cy.get('[data-a11y-dialog="dialog-3"]').then(shouldBeHidden)
})

it('should close only the top most dialog when clicking the backdrop', () => {
cy.get('body').trigger('keydown', { keyCode: 27, which: 27 })
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea why we performed an additional ESC here, but this is wrong.

cy.get('[data-a11y-dialog="dialog-2"]')
.find('.dialog-overlay')
.first()
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because a dialog container now contains multiple overlay (since the DOM trees are now properly nested), we need to find the first one.

@@ -236,7 +249,8 @@ A11yDialog.prototype._fire = function (type, event) {
A11yDialog.prototype._bindKeypress = function (event) {
// This is an escape hatch in case there are nested dialogs, so the keypresses
// are only reacted to for the most recent one
if (!this.$el.contains(document.activeElement)) return
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that the dialog are properly nested, this escape hatch no longer works since the active element is always within all the “higher” dialogs.

We need to flip that logic and check whether the active element is directly within the current dialog.

@KittyGiraudel KittyGiraudel merged commit dc82d46 into v7 Aug 17, 2022
@KittyGiraudel KittyGiraudel deleted the nested-dialogs-v7 branch August 17, 2022 07:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant