From b8e9926056f28c8d8c7a62148f6503019ff58419 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 2 Sep 2019 15:00:19 +0200 Subject: [PATCH 1/4] fix(tooltip): add aria role --- packages/tooltip/src/LionTooltip.js | 1 + packages/tooltip/test/lion-tooltip.test.js | 11 +++++++++++ 2 files changed, 12 insertions(+) diff --git a/packages/tooltip/src/LionTooltip.js b/packages/tooltip/src/LionTooltip.js index 87f5ab7ac..54baaac5a 100644 --- a/packages/tooltip/src/LionTooltip.js +++ b/packages/tooltip/src/LionTooltip.js @@ -6,6 +6,7 @@ export class LionTooltip extends LionPopup { super.connectedCallback(); this.contentNode = this.querySelector('[slot="content"]'); this.invokerNode = this.querySelector('[slot="invoker"]'); + this.contentNode.setAttribute('role', 'tooltip'); this._controller = overlays.add( new LocalOverlayController({ diff --git a/packages/tooltip/test/lion-tooltip.test.js b/packages/tooltip/test/lion-tooltip.test.js index 14a4702bb..d3b09ac73 100644 --- a/packages/tooltip/test/lion-tooltip.test.js +++ b/packages/tooltip/test/lion-tooltip.test.js @@ -68,6 +68,17 @@ describe('lion-tooltip', () => { expect(el.querySelector('[slot="content"]').style.display).to.be.equal('none'); }); + it('should have a tooltip role set on the tooltip', async () => { + const el = await fixture(html` + +
Hey there
+ Tooltip button +
+ `); + const invoker = el.querySelector('[slot="content"]'); + expect(invoker.getAttribute('role')).to.be.equal('tooltip'); + }); + it('should have aria-controls attribute set to the invoker', async () => { const el = await fixture(html` From 73e2305164a58754b99b329ef52e3878a29de8b6 Mon Sep 17 00:00:00 2001 From: erik Date: Mon, 2 Sep 2019 15:17:31 +0200 Subject: [PATCH 2/4] fix(tooltip): make tooltip hoverable --- packages/tooltip/src/LionTooltip.js | 8 ++++---- packages/tooltip/test/lion-tooltip.test.js | 5 ++--- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/packages/tooltip/src/LionTooltip.js b/packages/tooltip/src/LionTooltip.js index 54baaac5a..b08680ca6 100644 --- a/packages/tooltip/src/LionTooltip.js +++ b/packages/tooltip/src/LionTooltip.js @@ -20,16 +20,16 @@ export class LionTooltip extends LionPopup { this._show = () => this._controller.show(); this._hide = () => this._controller.hide(); - this.invokerNode.addEventListener('mouseenter', this._show); - this.invokerNode.addEventListener('mouseleave', this._hide); + this.addEventListener('mouseenter', this._show); + this.addEventListener('mouseleave', this._hide); this.invokerNode.addEventListener('focusin', this._show); this.invokerNode.addEventListener('focusout', this._hide); } disconnectedCallback() { super.disconnectedCallback(); - this.invokerNode.removeEventListener('mouseenter', this._show); - this.invokerNode.removeEventListener('mouseleave', this._hide); + this.removeEventListener('mouseenter', this._show); + this.removeEventListener('mouseleave', this._hide); this.invokerNode.removeEventListener('focusin', this._show); this.invokerNode.removeEventListener('focusout', this._hide); } diff --git a/packages/tooltip/test/lion-tooltip.test.js b/packages/tooltip/test/lion-tooltip.test.js index d3b09ac73..693964a94 100644 --- a/packages/tooltip/test/lion-tooltip.test.js +++ b/packages/tooltip/test/lion-tooltip.test.js @@ -21,13 +21,12 @@ describe('lion-tooltip', () => { Tooltip button `); - const invoker = el.querySelector('[slot="invoker"]'); const eventMouseEnter = new Event('mouseenter'); - invoker.dispatchEvent(eventMouseEnter); + el.dispatchEvent(eventMouseEnter); await el.updateComplete; expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block'); const eventMouseLeave = new Event('mouseleave'); - invoker.dispatchEvent(eventMouseLeave); + el.dispatchEvent(eventMouseLeave); await el.updateComplete; expect(el.querySelector('[slot="content"]').style.display).to.be.equal('none'); }); From 48a41c97bf8c54512b4324894b61d8f2d9ba2fb1 Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 3 Sep 2019 11:29:47 +0200 Subject: [PATCH 3/4] fix(tooltip): make tooltip hide with what triggered it --- packages/tooltip/src/LionTooltip.js | 57 ++++++++++++++--- packages/tooltip/test/lion-tooltip.test.js | 71 ++++++++++++++++------ 2 files changed, 100 insertions(+), 28 deletions(-) diff --git a/packages/tooltip/src/LionTooltip.js b/packages/tooltip/src/LionTooltip.js index b08680ca6..598efba08 100644 --- a/packages/tooltip/src/LionTooltip.js +++ b/packages/tooltip/src/LionTooltip.js @@ -2,6 +2,12 @@ import { LionPopup } from '@lion/popup'; import { overlays, LocalOverlayController } from '@lion/overlays'; export class LionTooltip extends LionPopup { + constructor() { + super(); + this.mouseActive = false; + this.keyActive = false; + } + connectedCallback() { super.connectedCallback(); this.contentNode = this.querySelector('[slot="content"]'); @@ -17,20 +23,51 @@ export class LionTooltip extends LionPopup { invokerNode: this.invokerNode, }), ); - this._show = () => this._controller.show(); - this._hide = () => this._controller.hide(); - this.addEventListener('mouseenter', this._show); - this.addEventListener('mouseleave', this._hide); - this.invokerNode.addEventListener('focusin', this._show); - this.invokerNode.addEventListener('focusout', this._hide); + this.__resetActive = () => { + this.mouseActive = false; + this.keyActive = false; + }; + + this.__showMouse = () => { + if (!this.keyActive) { + this.mouseActive = true; + this._controller.show(); + } + }; + + this.__hideMouse = () => { + if (!this.keyActive) { + this._controller.hide(); + } + }; + + this.__showKey = () => { + if (!this.mouseActive) { + this.keyActive = true; + this._controller.show(); + } + }; + + this.__hideKey = () => { + if (!this.mouseActive) { + this._controller.hide(); + } + }; + + this._controller.addEventListener('hide', this.__resetActive); + this.addEventListener('mouseenter', this.__showMouse); + this.addEventListener('mouseleave', this.__hideMouse); + this.invokerNode.addEventListener('focusin', this.__showKey); + this.invokerNode.addEventListener('focusout', this.__hideKey); } disconnectedCallback() { super.disconnectedCallback(); - this.removeEventListener('mouseenter', this._show); - this.removeEventListener('mouseleave', this._hide); - this.invokerNode.removeEventListener('focusin', this._show); - this.invokerNode.removeEventListener('focusout', this._hide); + this._controller.removeEventListener('hide', this.__resetActive); + this.removeEventListener('mouseenter', this.__showMouse); + this.removeEventListener('mouseleave', this._hideMouse); + this.invokerNode.removeEventListener('focusin', this._showKey); + this.invokerNode.removeEventListener('focusout', this._hideKey); } } diff --git a/packages/tooltip/test/lion-tooltip.test.js b/packages/tooltip/test/lion-tooltip.test.js index 693964a94..249f247ac 100644 --- a/packages/tooltip/test/lion-tooltip.test.js +++ b/packages/tooltip/test/lion-tooltip.test.js @@ -31,6 +31,59 @@ describe('lion-tooltip', () => { expect(el.querySelector('[slot="content"]').style.display).to.be.equal('none'); }); + it('should show content on mouseenter and remain shown on focusout', async () => { + const el = await fixture(html` + +
Hey there
+ Tooltip button +
+ `); + const eventMouseEnter = new Event('mouseenter'); + el.dispatchEvent(eventMouseEnter); + await el.updateComplete; + expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block'); + const eventFocusOut = new Event('focusout'); + el.dispatchEvent(eventFocusOut); + await el.updateComplete; + expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block'); + }); + + it('should show content on focusin and hide on focusout', async () => { + const el = await fixture(html` + +
Hey there
+ Tooltip button +
+ `); + const invoker = el.querySelector('[slot="invoker"]'); + const eventFocusIn = new Event('focusin'); + invoker.dispatchEvent(eventFocusIn); + await el.updateComplete; + expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block'); + const eventFocusOut = new Event('focusout'); + invoker.dispatchEvent(eventFocusOut); + await el.updateComplete; + expect(el.querySelector('[slot="content"]').style.display).to.be.equal('none'); + }); + + it('should show content on focusin and remain shown on mouseleave', async () => { + const el = await fixture(html` + +
Hey there
+ Tooltip button +
+ `); + const invoker = el.querySelector('[slot="invoker"]'); + const eventFocusIn = new Event('focusin'); + invoker.dispatchEvent(eventFocusIn); + await el.updateComplete; + expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block'); + const eventMouseLeave = new Event('mouseleave'); + invoker.dispatchEvent(eventMouseLeave); + await el.updateComplete; + expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block'); + }); + it('should tooltip contains html when specified in tooltip content body', async () => { const el = await fixture(html` @@ -49,24 +102,6 @@ describe('lion-tooltip', () => { }); describe('Accessibility', () => { - it('should visible on focusin and hide on focusout', async () => { - const el = await fixture(html` - -
Hey there
- Tooltip button -
- `); - const invoker = el.querySelector('[slot="invoker"]'); - const eventFocusIn = new Event('focusin'); - invoker.dispatchEvent(eventFocusIn); - await el.updateComplete; - expect(el.querySelector('[slot="content"]').style.display).to.be.equal('inline-block'); - const eventFocusOut = new Event('focusout'); - invoker.dispatchEvent(eventFocusOut); - await el.updateComplete; - expect(el.querySelector('[slot="content"]').style.display).to.be.equal('none'); - }); - it('should have a tooltip role set on the tooltip', async () => { const el = await fixture(html` From 58030bf3efdd87e52a0ba0b2d9c686e6d93e06ad Mon Sep 17 00:00:00 2001 From: erik Date: Tue, 3 Sep 2019 11:38:15 +0200 Subject: [PATCH 4/4] chore(popup/tooltip): improve DOM order in demo's --- packages/popup/stories/index.stories.js | 12 ++++++------ packages/tooltip/stories/index.stories.js | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/packages/popup/stories/index.stories.js b/packages/popup/stories/index.stories.js index 6fa0bb8c6..c22745006 100644 --- a/packages/popup/stories/index.stories.js +++ b/packages/popup/stories/index.stories.js @@ -59,8 +59,8 @@ storiesOf('Local Overlay System|Popup', module)
- Popup +
`, @@ -73,20 +73,20 @@ storiesOf('Local Overlay System|Popup', module)
- Top + - Right + - Bottom + - Left +
`, @@ -123,8 +123,8 @@ storiesOf('Local Overlay System|Popup', module) }, })}" > - ${text('Invoker text', 'Click me!')} + `, diff --git a/packages/tooltip/stories/index.stories.js b/packages/tooltip/stories/index.stories.js index 0d7e4c1ac..9be5424ce 100644 --- a/packages/tooltip/stories/index.stories.js +++ b/packages/tooltip/stories/index.stories.js @@ -59,8 +59,8 @@ storiesOf('Local Overlay System|Tooltip', module)
-
Hello there!
Tooltip +
Hello there!
`, @@ -73,20 +73,20 @@ storiesOf('Local Overlay System|Tooltip', module)
-
Its top placement
Top +
Its top placement
-
Its right placement
Right +
Its right placement
-
Its bottom placement
Bottom +
Its bottom placement
-
Its left placement
Left +
Its left placement
`, @@ -123,8 +123,8 @@ storiesOf('Local Overlay System|Tooltip', module) }, })}" > -
${text('Content text', 'Hello, World!')}
${text('Invoker text', 'Click me!')} +
${text('Content text', 'Hello, World!')}
`,