Skip to content

Commit 5363a31

Browse files
authored
fix(bv-tooltip): hide the tooltip when the title is set to empty (closes #5648) (#5677)
* fix(bv-tooltip): hide the tooltip when the title is set to empty * Update tooltip.spec.js
1 parent d673e31 commit 5363a31

File tree

2 files changed

+90
-16
lines changed

2 files changed

+90
-16
lines changed

src/components/tooltip/helpers/bv-tooltip.js

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,9 @@ const CONTAINER_SELECTOR = [MODAL_SELECTOR, SIDEBAR_SELECTOR].join(', ')
5656
const DROPDOWN_CLASS = 'dropdown'
5757
const DROPDOWN_OPEN_SELECTOR = '.dropdown-menu.show'
5858

59+
// Data attribute to temporary store the `title` attribute's value
60+
const DATA_TITLE_ATTR = 'data-original-title'
61+
5962
// Data specific to popper and template
6063
// We don't use props, as we need reactivity (we can't pass reactive props)
6164
const templateData = {
@@ -200,8 +203,18 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({
200203
// ensure that the template updates accordingly
201204
this.handleTemplateUpdate()
202205
},
203-
disabled(newVal) {
204-
newVal ? this.disable() : this.enable()
206+
title(newValue, oldValue) {
207+
// Make sure to hide the tooltip when the title is set empty
208+
if (newValue !== oldValue && !newValue) {
209+
this.hide()
210+
}
211+
},
212+
disabled(newValue) {
213+
if (newValue) {
214+
this.disable()
215+
} else {
216+
this.enable()
217+
}
205218
}
206219
},
207220
created() {
@@ -272,10 +285,10 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({
272285
}
273286
}
274287
})
288+
// If the title has updated, we may need to handle the `title`
289+
// attribute on the trigger target
290+
// We only do this while the template is open
275291
if (titleUpdated && this.localShow) {
276-
// If the title has updated, we may need to handle the title
277-
// attribute on the trigger target. We only do this while the
278-
// template is open
279292
this.fixTitle()
280293
}
281294
},
@@ -594,22 +607,19 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({
594607
}
595608
},
596609
fixTitle() {
597-
// If the target has a title attribute, null it out and
598-
// store on data-title
610+
// If the target has a `title` attribute, remove it and store it on a data attribute
599611
const target = this.getTarget()
600-
if (target && getAttr(target, 'title')) {
601-
// We only update title attribute if it has a value
602-
setAttr(target, 'data-original-title', getAttr(target, 'title') || '')
612+
if (target && hasAttr(target, 'title')) {
613+
setAttr(target, DATA_TITLE_ATTR, getAttr(target, 'title') || '')
603614
setAttr(target, 'title', '')
604615
}
605616
},
606617
restoreTitle() {
607-
// If target had a title, restore the title attribute
608-
// and remove the data-title attribute
618+
// If the target had a title, restore it and remove the data attribute
609619
const target = this.getTarget()
610-
if (target && hasAttr(target, 'data-original-title')) {
611-
setAttr(target, 'title', getAttr(target, 'data-original-title') || '')
612-
removeAttr(target, 'data-original-title')
620+
if (target && hasAttr(target, DATA_TITLE_ATTR)) {
621+
setAttr(target, 'title', getAttr(target, DATA_TITLE_ATTR) || '')
622+
removeAttr(target, DATA_TITLE_ATTR)
613623
}
614624
},
615625
// --- BvEvent helpers ---

src/components/tooltip/tooltip.spec.js

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1119,7 +1119,7 @@ describe('b-tooltip', () => {
11191119

11201120
// Tooltip element should not be in the document
11211121
expect(document.body.contains(tip)).toBe(false)
1122-
expect(document.getElementById(`adb`)).toBe(null)
1122+
expect(document.getElementById('adb')).toBe(null)
11231123

11241124
// Try and show element via root event (using ID of trigger button)
11251125
// Note that this generates a console warning
@@ -1157,6 +1157,70 @@ describe('b-tooltip', () => {
11571157
wrapper.destroy()
11581158
})
11591159

1160+
it('closes when title is set to empty', async () => {
1161+
jest.useFakeTimers()
1162+
const wrapper = mount(App, {
1163+
attachTo: createContainer(),
1164+
propsData: {
1165+
show: true,
1166+
title: 'hello'
1167+
}
1168+
})
1169+
1170+
expect(wrapper.vm).toBeDefined()
1171+
await waitNT(wrapper.vm)
1172+
await waitRAF()
1173+
await waitNT(wrapper.vm)
1174+
await waitRAF()
1175+
await waitNT(wrapper.vm)
1176+
await waitRAF()
1177+
jest.runOnlyPendingTimers()
1178+
await waitNT(wrapper.vm)
1179+
await waitRAF()
1180+
1181+
expect(wrapper.element.tagName).toBe('ARTICLE')
1182+
expect(wrapper.attributes('id')).toBeDefined()
1183+
expect(wrapper.attributes('id')).toEqual('wrapper')
1184+
1185+
// The trigger button
1186+
const $button = wrapper.find('button')
1187+
expect($button.exists()).toBe(true)
1188+
expect($button.attributes('id')).toBeDefined()
1189+
expect($button.attributes('id')).toEqual('foo')
1190+
expect($button.attributes('title')).not.toBeDefined()
1191+
expect($button.attributes('data-original-title')).not.toBeDefined()
1192+
expect($button.attributes('aria-describedby')).toBeDefined()
1193+
// ID of the tooltip that will be in the body
1194+
const adb = $button.attributes('aria-describedby')
1195+
1196+
// <b-tooltip> wrapper
1197+
const $tipHolder = wrapper.findComponent(BTooltip)
1198+
expect($tipHolder.exists()).toBe(true)
1199+
expect($tipHolder.element.nodeType).toEqual(Node.COMMENT_NODE)
1200+
1201+
// Find the tooltip element in the document
1202+
const tip = document.getElementById(adb)
1203+
expect(tip).not.toBe(null)
1204+
expect(tip).toBeInstanceOf(HTMLElement)
1205+
const $tip = createWrapper(tip)
1206+
expect($tip.element.tagName).toBe('DIV')
1207+
expect($tip.classes()).toContain('tooltip')
1208+
expect($tip.classes()).toContain('b-tooltip')
1209+
// Should contain our title prop value
1210+
expect($tip.text()).toContain('hello')
1211+
1212+
// Change the title prop
1213+
await wrapper.setProps({ title: '' })
1214+
await waitRAF()
1215+
await waitRAF()
1216+
1217+
// Tooltip element should not be in the document
1218+
expect(document.body.contains(tip)).toBe(false)
1219+
expect(document.getElementById('adb')).toBe(null)
1220+
1221+
wrapper.destroy()
1222+
})
1223+
11601224
it('applies noninteractive class based on noninteractive prop', async () => {
11611225
jest.useFakeTimers()
11621226
const wrapper = mount(App, {

0 commit comments

Comments
 (0)