Skip to content

Commit 4255353

Browse files
authored
feat: Hide tooltip on window resize and scroll events (#28)
1 parent 34dd4fd commit 4255353

File tree

5 files changed

+104
-22
lines changed

5 files changed

+104
-22
lines changed

jest/jest.setup.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,13 @@ import '@testing-library/jest-dom/extend-expect'
33

44
expect.extend({ toBeInTheDocument, toHaveAttribute, toHaveStyle })
55

6-
global._createElement = (id = 'foo', attrs) => {
6+
global._createElement = (id = 'foo', parent, attrs) => {
77
const el = document.createElement('div')
88
el.setAttribute('id', id)
99
for (let key in attrs) {
1010
el.setAttribute(key, attrs[key])
1111
}
12-
document.body.appendChild(el)
12+
;(parent || document.body).appendChild(el)
1313
return el
1414
}
1515

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
"@semantic-release/changelog": "^6.0.1",
3636
"@semantic-release/git": "^10.0.1",
3737
"@semantic-release/github": "^8.0.2",
38-
"@testing-library/dom": "^8.11.0",
38+
"@testing-library/dom": "^8.11.3",
3939
"@testing-library/jest-dom": "^5.11.6",
4040
"@testing-library/svelte": "^3.0.3",
4141
"babel-jest": "^27.3.1",

src/Tooltip.js

Lines changed: 42 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class Tooltip {
1313

1414
#boundEnterHandler = null
1515
#boundLeaveHandler = null
16-
#boundKeyDownHandler = null
16+
#boundWindowChangeHandler = null
1717

1818
#target = null
1919
#content = null
@@ -72,7 +72,7 @@ class Tooltip {
7272
this.#target.setAttribute('style', 'position: relative')
7373
this.#target.setAttribute('aria-describedby', 'tooltip')
7474

75-
disabled ? this.#disableTarget() : this.#enableTarget()
75+
disabled ? this.#disable() : this.#enable()
7676

7777
Tooltip.#instances.push(this)
7878
}
@@ -122,14 +122,14 @@ class Tooltip {
122122
}
123123

124124
if (hasToDisableTarget) {
125-
this.#disableTarget()
125+
this.#disable()
126126
} else if (hasToEnableTarget) {
127-
this.#enableTarget()
127+
this.#enable()
128128
}
129129
}
130130

131-
destroy() {
132-
this.#removeTooltipFromTarget()
131+
async destroy() {
132+
await this.#removeTooltipFromTarget()
133133

134134
this.#disableTarget()
135135

@@ -139,28 +139,50 @@ class Tooltip {
139139
this.#observer = null
140140
}
141141

142+
#enable() {
143+
this.#enableTarget()
144+
this.#enableWindow()
145+
}
146+
142147
#enableTarget() {
143148
this.#boundEnterHandler = this.#onTargetEnter.bind(this)
144149
this.#boundLeaveHandler = this.#onTargetLeave.bind(this)
145-
this.#boundKeyDownHandler = this.#onTargetKeyDown.bind(this)
146150

147151
this.#target.addEventListener('mouseenter', this.#boundEnterHandler)
148152
this.#target.addEventListener('mouseleave', this.#boundLeaveHandler)
149153
this.#target.addEventListener('focusin', this.#boundEnterHandler)
150154
this.#target.addEventListener('focusout', this.#boundLeaveHandler)
151-
window.addEventListener('keydown', this.#boundKeyDownHandler)
155+
}
156+
157+
#enableWindow() {
158+
this.#boundWindowChangeHandler = this.#onWindowChange.bind(this)
159+
160+
window.addEventListener('keydown', this.#boundWindowChangeHandler)
161+
window.addEventListener('resize', this.#boundWindowChangeHandler)
162+
window.addEventListener('scroll', this.#boundWindowChangeHandler)
163+
}
164+
165+
#disable() {
166+
this.#disableTarget()
167+
this.#disableWindow()
152168
}
153169

154170
#disableTarget() {
155171
this.#target.removeEventListener('mouseenter', this.#boundEnterHandler)
156172
this.#target.removeEventListener('mouseleave', this.#boundLeaveHandler)
157173
this.#target.removeEventListener('focusin', this.#boundEnterHandler)
158174
this.#target.removeEventListener('focusout', this.#boundLeaveHandler)
159-
window.removeEventListener('keydown', this.#boundKeyDownHandler)
160175

161176
this.#boundEnterHandler = null
162177
this.#boundLeaveHandler = null
163-
this.#boundKeyDownHandler = null
178+
}
179+
180+
#disableWindow() {
181+
window.removeEventListener('keydown', this.#boundWindowChangeHandler)
182+
window.removeEventListener('resize', this.#boundWindowChangeHandler)
183+
window.removeEventListener('scroll', this.#boundWindowChangeHandler)
184+
185+
this.#boundWindowChangeHandler = null
164186
}
165187

166188
#createTooltip() {
@@ -345,9 +367,16 @@ class Tooltip {
345367
await this.#removeTooltipFromTarget()
346368
}
347369

348-
async #onTargetKeyDown(e) {
349-
if (e.key === 'Escape' || e.key === 'Esc' || e.keyCode === 27) {
350-
await this.#onTargetLeave()
370+
async #onWindowChange(e) {
371+
if (
372+
this.#tooltip &&
373+
this.#tooltip.parentNode &&
374+
(e.type !== 'keydown' ||
375+
(e.type === 'keydown' && e.key === 'Escape') ||
376+
e.key === 'Esc' ||
377+
e.keyCode === 27)
378+
) {
379+
await this.#removeTooltipFromTarget()
351380
}
352381
}
353382
}

src/__tests__/useTooltip.test.js

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,27 @@ describe('useTooltip', () => {
5858

5959
const _keyDown = async (key) =>
6060
new Promise(async (resolve) => {
61-
await fireEvent.keyDown(target, key || { key: 'Escape', code: 'Escape', charCode: 27 })
61+
await fireEvent.keyDown(target, key || { key: 'Escape', code: 'Escape', keyCode: 27 })
62+
await _sleep(1)
63+
resolve()
64+
})
65+
66+
const _scroll = async () =>
67+
new Promise(async (resolve) => {
68+
await fireEvent.scroll(window)
69+
await _sleep(1)
70+
resolve()
71+
})
72+
73+
const _resize = async () =>
74+
new Promise(async (resolve) => {
75+
await fireEvent(window, new Event('resize'))
6276
await _sleep(1)
6377
resolve()
6478
})
6579

6680
beforeEach(() => {
67-
target = _createElement('target', { class: 'bar' })
81+
target = _createElement('target', null, { class: 'bar' })
6882
template = _createElement('template')
6983
options = {
7084
contentSelector: '#template',
@@ -110,9 +124,26 @@ describe('useTooltip', () => {
110124
it('Hides tooltip on escape key down', async () => {
111125
action = useTooltip(target, options)
112126
await _enter()
127+
expect(template).toBeInTheDocument()
113128
await _keyDown()
114129
expect(template).not.toBeInTheDocument()
115130
})
131+
132+
it('Hides tooltip on scroll', async () => {
133+
action = useTooltip(target, options)
134+
await _enter()
135+
expect(template).toBeInTheDocument()
136+
await _scroll()
137+
expect(template).not.toBeInTheDocument()
138+
})
139+
140+
it('Hides tooltip on resize', async () => {
141+
action = useTooltip(target, options)
142+
await _enter()
143+
expect(template).toBeInTheDocument()
144+
await _resize()
145+
expect(template).not.toBeInTheDocument()
146+
})
116147
})
117148

118149
describe('update', () => {
@@ -213,6 +244,28 @@ describe('useTooltip', () => {
213244
expect(template).toBeInTheDocument()
214245
})
215246

247+
it('Triggers callback on element click within tooltip', async () => {
248+
const newTemplate = _createElement('new-template')
249+
const clickableElement = _createElement('clickable-element', newTemplate)
250+
const contentActions = {
251+
'#clickable-element': {
252+
eventType: 'click',
253+
callback: jest.fn(),
254+
callbackParams: ['foo'],
255+
},
256+
}
257+
action = useTooltip(target, {
258+
...options,
259+
contentSelector: '#new-template',
260+
contentActions,
261+
})
262+
const contentAction = contentActions['#clickable-element']
263+
await _enter()
264+
await fireEvent.click(clickableElement)
265+
expect(contentAction.callback).toHaveBeenCalledWith(contentAction.callbackParams[0], expect.any(Event))
266+
expect(newTemplate).toBeInTheDocument()
267+
})
268+
216269
it('Closes tooltip after triggering callback', async () => {
217270
action = useTooltip(target, options)
218271
options.contentActions['*'].closeOnCallback = true

yarn.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1776,10 +1776,10 @@
17761776
lz-string "^1.4.4"
17771777
pretty-format "^26.6.2"
17781778

1779-
"@testing-library/dom@^8.11.0":
1780-
version "8.11.0"
1781-
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.11.0.tgz#3679dfb4db58e0d2b95e4b0929eaf45237b60d94"
1782-
integrity sha512-8Ay4UDiMlB5YWy+ZvCeRyFFofs53ebxrWnOFvCoM1HpMAX4cHyuSrCuIM9l2lVuUWUt+Gr3loz/nCwdrnG6ShQ==
1779+
"@testing-library/dom@^8.11.3":
1780+
version "8.11.3"
1781+
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.11.3.tgz#38fd63cbfe14557021e88982d931e33fb7c1a808"
1782+
integrity sha512-9LId28I+lx70wUiZjLvi1DB/WT2zGOxUh46glrSNMaWVx849kKAluezVzZrXJfTKKoQTmEOutLes/bHg4Bj3aA==
17831783
dependencies:
17841784
"@babel/code-frame" "^7.10.4"
17851785
"@babel/runtime" "^7.12.5"

0 commit comments

Comments
 (0)