Skip to content

Commit

Permalink
fix: keep parent overlays open when not closing child hover overlays
Browse files Browse the repository at this point in the history
  • Loading branch information
Westbrook committed Nov 10, 2022
1 parent ebaf7b8 commit 643fcff
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 7 deletions.
24 changes: 18 additions & 6 deletions packages/overlay/src/overlay-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -605,16 +605,28 @@ export class OverlayStack {
return;
}
const overlaysToClose = [];
// Find the top most overlay that is not triggered by an
// element on the path of the current click event.
/**
* Find the first overlay that should be closed by this and include it in the
* array of overlays for closure.
*
* Event path dictates closure while the click event:
* - did not occur within the overlay content of the overlay
* AND was
* - not triggered by something in the click event's composed path
* OR
* - not a "hover" overlay
* Select the overlay for closure
*/
let index = this.overlays.length;
while (index && overlaysToClose.length === 0) {
index -= 1;
const overlay = this.overlays[index];
if (
!event.composedPath().includes(overlay.trigger) ||
overlay.interaction !== 'hover'
) {
const path = event.composedPath();
const eventPathDictatesClosure =
(!path.includes(overlay.trigger) ||
overlay.interaction !== 'hover') &&
!path.includes(overlay.overlayContent);
if (eventPathDictatesClosure) {
overlaysToClose.push(overlay);
}
}
Expand Down
35 changes: 35 additions & 0 deletions packages/overlay/stories/overlay.stories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,41 @@ export const replace = (): TemplateResult => {
`;
};

export const deep = (): TemplateResult => html`
<overlay-trigger>
<sp-button variant="primary" slot="trigger">
Open popover 1 with buttons + selfmanaged Tooltips
</sp-button>
<sp-popover dialog slot="click-content" direction="bottom" tip open>
<sp-action-button>
<sp-tooltip self-managed placement="bottom" offset="0">
My Tooltip 1
</sp-tooltip>
A
</sp-action-button>
<sp-action-button>
<sp-tooltip self-managed placement="bottom" offset="0">
My Tooltip 1
</sp-tooltip>
B
</sp-action-button>
</sp-popover>
</overlay-trigger>
<overlay-trigger>
<sp-button variant="primary" slot="trigger">
Open popover 2 with buttons without ToolTips
</sp-button>
<sp-popover dialog slot="click-content" direction="bottom" tip open>
<sp-action-button>X</sp-action-button>
<sp-action-button>Y</sp-action-button>
</sp-popover>
</overlay-trigger>
`;
deep.swc_vrt = {
skip: true,
};

export const modalLoose = (): TemplateResult => {
const closeEvent = new Event('close', { bubbles: true, composed: true });
return html`
Expand Down
48 changes: 47 additions & 1 deletion packages/overlay/test/overlay-trigger-hover-click.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ OF ANY KIND, either express or implied. See the License for the specific languag
governing permissions and limitations under the License.
*/
import {
aTimeout,
elementUpdated,
expect,
fixture,
Expand All @@ -27,8 +28,9 @@ import { TriggerInteractions } from '@spectrum-web-components/overlay/src/overla
import '@spectrum-web-components/overlay/overlay-trigger.js';
import { ActionButton } from '@spectrum-web-components/action-button';
import { sendMouse } from '../../../test/plugins/browser.js';
import { clickAndHoverTargets } from '../stories/overlay.stories.js';
import { clickAndHoverTargets, deep } from '../stories/overlay.stories.js';
import { ignoreResizeObserverLoopError } from '../../../test/testing-helpers.js';
import { Tooltip } from '@spectrum-web-components/tooltip/src/Tooltip.js';

ignoreResizeObserverLoopError(before, after);

Expand Down Expand Up @@ -198,4 +200,48 @@ describe('Overlay Trigger - Hover and Click', () => {
expect(overlayTrigger1.open).to.be.null;
expect(overlayTrigger2.open).to.equal('hover');
});
it('does not close ancestor "click" overlays on `click`', async () => {
const test = await fixture<HTMLDivElement>(html`
<div>${deep()}</div>
`);
const el = test.querySelector('overlay-trigger') as OverlayTrigger;
const button = el.querySelector('sp-action-button') as ActionButton;
const tooltip = button.querySelector('sp-tooltip') as Tooltip;

expect(el.open).to.be.undefined;
expect(tooltip.open).to.be.false;

let opened = oneEvent(el, 'sp-opened');
el.open = 'click';
await opened;

expect(el.open).to.equal('click');

opened = oneEvent(button, 'sp-opened');
button.focus();
await opened;

expect(tooltip.open).to.be.true;

button.click();

await aTimeout(200);

expect(el.open).to.equal('click');
expect(tooltip.open).to.be.true;

let closed = oneEvent(button, 'sp-closed');
button.blur();
await closed;

expect(el.open).to.equal('click');
expect(tooltip.open).to.be.false;

closed = oneEvent(el, 'sp-closed');
document.body.click();
await closed;

expect(el.open).to.be.null;
expect(tooltip.open).to.be.false;
});
});

0 comments on commit 643fcff

Please sign in to comment.