diff --git a/cypress/e2e/webComponents.cy.ts b/cypress/e2e/webComponents.cy.ts index a07fb7ee..79a35b65 100644 --- a/cypress/e2e/webComponents.cy.ts +++ b/cypress/e2e/webComponents.cy.ts @@ -37,11 +37,11 @@ describe('Web Components', () => { const handlers = { show: event => { expect(event.detail.target.tagName).to.eq('MY-DIALOG') - expect(event.detail.composedPath()[0].tagName).to.eq('FANCY-BUTTON') + expect(event.detail.composedPath()[0].tagName).to.eq('SLOT') }, hide: event => { expect(event.detail.target.tagName).to.eq('MY-DIALOG') - expect(event.detail.composedPath()[0].tagName).to.eq('FANCY-BUTTON') + expect(event.detail.composedPath()[0].tagName).to.eq('SLOT') }, } @@ -49,10 +49,10 @@ describe('Web Components', () => { cy.spy(handlers, 'hide').as('hide') cy.window().its('instance').invoke('on', 'show', handlers.show) cy.window().its('instance').invoke('on', 'hide', handlers.hide) - cy.get('my-dialog').shadow().find('fancy-button').first().click() + cy.get('my-dialog').shadow().find('fancy-button').first().realClick() cy.get('@show').should('have.been.called') cy.get('my-dialog').then(shouldBeVisible) - cy.get('my-dialog').shadow().find('fancy-button').last().click() + cy.get('my-dialog').shadow().find('fancy-button').last().realClick() cy.get('@hide').should('have.been.called') cy.get('my-dialog').then(shouldBeHidden) cy.window().its('instance').invoke('off', 'show', handlers.show) diff --git a/src/a11y-dialog.ts b/src/a11y-dialog.ts index 31c570a2..b45f8c11 100644 --- a/src/a11y-dialog.ts +++ b/src/a11y-dialog.ts @@ -192,20 +192,19 @@ export default class A11yDialog { // target // See: https://github.com/KittyGiraudel/a11y-dialog/issues/582 const target = event.composedPath()[0] as HTMLElement - - // We use `.closest(..)` and not `.matches(..)` here so that clicking - // an element nested within a dialog opener does cause the dialog to open - if (target.closest(`[${SCOPE}-show="${this.id}"]`)) { - this.show(event) - } - - if ( - target.closest(`[${SCOPE}-hide="${this.id}"]`) || - (target.closest(`[${SCOPE}-hide]`) && - target.closest('[aria-modal="true"]') === this.$el) - ) { - this.hide(event) - } + const opener = closest(`[${SCOPE}-show="${this.id}"]`, target) + const explicitCloser = closest(`[${SCOPE}-hide="${this.id}"]`, target) + const implicitCloser = + closest(`[${SCOPE}-hide]`, target) && + closest('[aria-modal="true"]', target) === this.$el + + // We use `closest(..)` (instead of `matches(..)`) so that clicking an + // element nested within a dialog opener does cause the dialog to open, and + // we use our custom `closest(..)` function so that it can cross shadow + // boundaries + // See: https://github.com/KittyGiraudel/a11y-dialog/issues/712 + if (opener) this.show(event) + if (explicitCloser || implicitCloser) this.hide(event) } /**