Skip to content

Commit 48a41c9

Browse files
committed
fix(tooltip): make tooltip hide with what triggered it
1 parent 73e2305 commit 48a41c9

File tree

2 files changed

+93
-21
lines changed

2 files changed

+93
-21
lines changed

packages/tooltip/src/LionTooltip.js

Lines changed: 47 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@ import { LionPopup } from '@lion/popup';
22
import { overlays, LocalOverlayController } from '@lion/overlays';
33

44
export class LionTooltip extends LionPopup {
5+
constructor() {
6+
super();
7+
this.mouseActive = false;
8+
this.keyActive = false;
9+
}
10+
511
connectedCallback() {
612
super.connectedCallback();
713
this.contentNode = this.querySelector('[slot="content"]');
@@ -17,20 +23,51 @@ export class LionTooltip extends LionPopup {
1723
invokerNode: this.invokerNode,
1824
}),
1925
);
20-
this._show = () => this._controller.show();
21-
this._hide = () => this._controller.hide();
2226

23-
this.addEventListener('mouseenter', this._show);
24-
this.addEventListener('mouseleave', this._hide);
25-
this.invokerNode.addEventListener('focusin', this._show);
26-
this.invokerNode.addEventListener('focusout', this._hide);
27+
this.__resetActive = () => {
28+
this.mouseActive = false;
29+
this.keyActive = false;
30+
};
31+
32+
this.__showMouse = () => {
33+
if (!this.keyActive) {
34+
this.mouseActive = true;
35+
this._controller.show();
36+
}
37+
};
38+
39+
this.__hideMouse = () => {
40+
if (!this.keyActive) {
41+
this._controller.hide();
42+
}
43+
};
44+
45+
this.__showKey = () => {
46+
if (!this.mouseActive) {
47+
this.keyActive = true;
48+
this._controller.show();
49+
}
50+
};
51+
52+
this.__hideKey = () => {
53+
if (!this.mouseActive) {
54+
this._controller.hide();
55+
}
56+
};
57+
58+
this._controller.addEventListener('hide', this.__resetActive);
59+
this.addEventListener('mouseenter', this.__showMouse);
60+
this.addEventListener('mouseleave', this.__hideMouse);
61+
this.invokerNode.addEventListener('focusin', this.__showKey);
62+
this.invokerNode.addEventListener('focusout', this.__hideKey);
2763
}
2864

2965
disconnectedCallback() {
3066
super.disconnectedCallback();
31-
this.removeEventListener('mouseenter', this._show);
32-
this.removeEventListener('mouseleave', this._hide);
33-
this.invokerNode.removeEventListener('focusin', this._show);
34-
this.invokerNode.removeEventListener('focusout', this._hide);
67+
this._controller.removeEventListener('hide', this.__resetActive);
68+
this.removeEventListener('mouseenter', this.__showMouse);
69+
this.removeEventListener('mouseleave', this._hideMouse);
70+
this.invokerNode.removeEventListener('focusin', this._showKey);
71+
this.invokerNode.removeEventListener('focusout', this._hideKey);
3572
}
3673
}

packages/tooltip/test/lion-tooltip.test.js

Lines changed: 46 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,25 +31,24 @@ describe('lion-tooltip', () => {
3131
expect(el.querySelector('[slot="content"]').style.display).to.be.equal('none');
3232
});
3333

34-
it('should tooltip contains html when specified in tooltip content body', async () => {
34+
it('should show content on mouseenter and remain shown on focusout', async () => {
3535
const el = await fixture(html`
3636
<lion-tooltip>
37-
<div slot="content">
38-
This is Tooltip using <strong id="click_overlay">overlay</strong>
39-
</div>
37+
<div slot="content">Hey there</div>
4038
<lion-button slot="invoker">Tooltip button</lion-button>
4139
</lion-tooltip>
4240
`);
43-
const invoker = el.querySelector('[slot="invoker"]');
44-
const event = new Event('mouseenter');
45-
invoker.dispatchEvent(event);
41+
const eventMouseEnter = new Event('mouseenter');
42+
el.dispatchEvent(eventMouseEnter);
4643
await el.updateComplete;
47-
expect(el.querySelector('strong')).to.not.be.undefined;
44+
expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block');
45+
const eventFocusOut = new Event('focusout');
46+
el.dispatchEvent(eventFocusOut);
47+
await el.updateComplete;
48+
expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block');
4849
});
49-
});
5050

51-
describe('Accessibility', () => {
52-
it('should visible on focusin and hide on focusout', async () => {
51+
it('should show content on focusin and hide on focusout', async () => {
5352
const el = await fixture(html`
5453
<lion-tooltip>
5554
<div slot="content">Hey there</div>
@@ -67,6 +66,42 @@ describe('lion-tooltip', () => {
6766
expect(el.querySelector('[slot="content"]').style.display).to.be.equal('none');
6867
});
6968

69+
it('should show content on focusin and remain shown on mouseleave', async () => {
70+
const el = await fixture(html`
71+
<lion-tooltip>
72+
<div slot="content">Hey there</div>
73+
<lion-button slot="invoker">Tooltip button</lion-button>
74+
</lion-tooltip>
75+
`);
76+
const invoker = el.querySelector('[slot="invoker"]');
77+
const eventFocusIn = new Event('focusin');
78+
invoker.dispatchEvent(eventFocusIn);
79+
await el.updateComplete;
80+
expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block');
81+
const eventMouseLeave = new Event('mouseleave');
82+
invoker.dispatchEvent(eventMouseLeave);
83+
await el.updateComplete;
84+
expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block');
85+
});
86+
87+
it('should tooltip contains html when specified in tooltip content body', async () => {
88+
const el = await fixture(html`
89+
<lion-tooltip>
90+
<div slot="content">
91+
This is Tooltip using <strong id="click_overlay">overlay</strong>
92+
</div>
93+
<lion-button slot="invoker">Tooltip button</lion-button>
94+
</lion-tooltip>
95+
`);
96+
const invoker = el.querySelector('[slot="invoker"]');
97+
const event = new Event('mouseenter');
98+
invoker.dispatchEvent(event);
99+
await el.updateComplete;
100+
expect(el.querySelector('strong')).to.not.be.undefined;
101+
});
102+
});
103+
104+
describe('Accessibility', () => {
70105
it('should have a tooltip role set on the tooltip', async () => {
71106
const el = await fixture(html`
72107
<lion-tooltip>

0 commit comments

Comments
 (0)