diff --git a/.changeset/rude-flies-reflect.md b/.changeset/rude-flies-reflect.md new file mode 100644 index 0000000000..6ca6c7fad5 --- /dev/null +++ b/.changeset/rude-flies-reflect.md @@ -0,0 +1,5 @@ +--- +'@lion/ui': patch +--- + +[overlays] no hiding of nested overlays having `hideOnEsc` configured diff --git a/packages/ui/components/overlays/src/OverlayController.js b/packages/ui/components/overlays/src/OverlayController.js index 39ffc26508..664969a86c 100644 --- a/packages/ui/components/overlays/src/OverlayController.js +++ b/packages/ui/components/overlays/src/OverlayController.js @@ -4,13 +4,13 @@ import { overlayShadowDomStyle } from './overlayShadowDomStyle.js'; import { _adoptStyleUtils } from './utils/adopt-styles.js'; /** - * @typedef {import('@lion/ui/types/overlays.js').OverlayConfig} OverlayConfig + * @typedef {'setup'|'init'|'teardown'|'before-show'|'show'|'hide'|'add'|'remove'} OverlayPhase * @typedef {import('@lion/ui/types/overlays.js').ViewportConfig} ViewportConfig - * @typedef {import('@popperjs/core').createPopper} Popper + * @typedef {import('@lion/ui/types/overlays.js').OverlayConfig} OverlayConfig * @typedef {import('@popperjs/core').Options} PopperOptions * @typedef {import('@popperjs/core').Placement} Placement + * @typedef {import('@popperjs/core').createPopper} Popper * @typedef {{ createPopper: Popper }} PopperModule - * @typedef {'setup'|'init'|'teardown'|'before-show'|'show'|'hide'|'add'|'remove'} OverlayPhase */ /** @@ -1148,7 +1148,20 @@ export class OverlayController extends EventTarget { /** @private */ __escKeyHandler(/** @type {KeyboardEvent} */ ev) { - return ev.key === 'Escape' && this.hide(); + // @ts-expect-error + if (ev.key !== 'Escape' || ev._hasAlreadyHandledEscape) return; + + this.hide(); + + const shouldNotCloseParents = this.hidesOnEsc && !this.hidesOnOutsideEsc; + if (shouldNotCloseParents) { + // We could do ev.stopPropagation() here, but we don't want to hide info for + // the outside world about user interactions. Instead, we piggyback + // on the event so our parent overlay can read it. + // @ts-expect-error + // eslint-disable-next-line no-param-reassign + ev._hasAlreadyHandledEscape = true; + } } /** diff --git a/packages/ui/components/overlays/test/OverlayController.test.js b/packages/ui/components/overlays/test/OverlayController.test.js index 3978d2fe98..81f8348325 100644 --- a/packages/ui/components/overlays/test/OverlayController.test.js +++ b/packages/ui/components/overlays/test/OverlayController.test.js @@ -1,24 +1,25 @@ /* eslint-disable no-new */ +import { OverlayController, overlays } from '@lion/ui/overlays.js'; +import { mimicClick } from '@lion/ui/overlays-test-helpers.js'; +import sinon from 'sinon'; import { - aTimeout, + unsafeStatic, + fixtureSync, defineCE, - expect, + aTimeout, fixture, + expect, html, - unsafeStatic, - fixtureSync, } from '@open-wc/testing'; -import sinon from 'sinon'; -import { OverlayController, overlays } from '@lion/ui/overlays.js'; -import { mimicClick } from '@lion/ui/overlays-test-helpers.js'; -import { keyCodes } from '../src/utils/key-codes.js'; -import { simulateTab } from '../src/utils/simulate-tab.js'; -import { _adoptStyleUtils } from '../src/utils/adopt-styles.js'; + import { createShadowHost } from '../test-helpers/createShadowHost.js'; +import { _adoptStyleUtils } from '../src/utils/adopt-styles.js'; +import { simulateTab } from '../src/utils/simulate-tab.js'; +import { keyCodes } from '../src/utils/key-codes.js'; /** - * @typedef {import('../types/OverlayConfig.js').OverlayConfig} OverlayConfig * @typedef {import('../types/OverlayConfig.js').ViewportPlacement} ViewportPlacement + * @typedef {import('../types/OverlayConfig.js').OverlayConfig} OverlayConfig */ const wrappingDialogNodeStyle = 'display: none; z-index: 9999; padding: 0px;'; @@ -617,6 +618,35 @@ describe('OverlayController', () => { await aTimeout(0); expect(ctrl.isShown).to.be.true; }); + + it('does not hide when [escape] while the overlay has a child open', async () => { + const childContent = /** @type {HTMLElement} */ ( + await fixture(html`