Skip to content

Commit 0b7de29

Browse files
authored
fix(v-b-tooltip, v-b-popover): don't show if no title/content provided (closes #4064) (#4076)
1 parent 9ddd115 commit 0b7de29

File tree

7 files changed

+93
-13
lines changed

7 files changed

+93
-13
lines changed

src/components/popover/helpers/bv-popover-template.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ export const BVPopoverTemplate = /*#__PURE__*/ Vue.extend({
3535
},
3636
[
3737
h('div', { ref: 'arrow', staticClass: 'arrow' }),
38-
isUndefinedOrNull($title)
38+
isUndefinedOrNull($title) || $title === ''
3939
? h()
4040
: h('h3', { staticClass: 'popover-header', domProps: titleDomProps }, [$title]),
41-
isUndefinedOrNull($content)
41+
isUndefinedOrNull($content) || $content === ''
4242
? h()
4343
: h('div', { staticClass: 'popover-body', domProps: contentDomProps }, [$content])
4444
]

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

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,14 @@ import {
2323
eventOn,
2424
eventOff
2525
} from '../../../utils/dom'
26-
import { isFunction, isNumber, isPlainObject, isString, isUndefined } from '../../../utils/inspect'
26+
import {
27+
isFunction,
28+
isNumber,
29+
isPlainObject,
30+
isString,
31+
isUndefined,
32+
isUndefinedOrNull
33+
} from '../../../utils/inspect'
2734
import { keys } from '../../../utils/object'
2835
import { warn } from '../../../utils/warn'
2936
import { BvEvent } from '../../../utils/bv-event.class'
@@ -358,9 +365,13 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({
358365
!target ||
359366
!contains(document.body, target) ||
360367
!isVisible(target) ||
361-
this.dropdownOpen()
368+
this.dropdownOpen() ||
369+
((isUndefinedOrNull(this.title) || this.title === '') &&
370+
(isUndefinedOrNull(this.content) || this.content === ''))
362371
) {
363-
// If trigger element isn't in the DOM or is not visible, or is on an open dropdown toggle
372+
// If trigger element isn't in the DOM or is not visible, or
373+
// is on an open dropdown toggle, or has no content, then
374+
// we exit without showing
364375
return
365376
}
366377

src/directives/popover/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,11 @@ to appear.
1818

1919
## Overview
2020

21-
Things to know when using popovers:
21+
Things to know when using the popover directive:
2222

2323
- Popovers rely on the 3rd party library [Popper.js](https://popper.js.org/) for positioning.
2424
- Popovers require BootstrapVue's custom SCSS/CSS for transitions and color variants.
25+
- If both title and content is not provided (or are an empty string), the popover will not show.
2526
- Specify container: 'body' (default) to avoid rendering problems in more complex components (like
2627
input groups, button groups, etc).
2728
- Triggering popovers on hidden elements will not work.

src/directives/popover/popover.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ import looseEqual from '../../utils/loose-equal'
33
import { concat } from '../../utils/array'
44
import { getComponentConfig } from '../../utils/config'
55
import { isBrowser } from '../../utils/env'
6-
import { isFunction, isObject, isString, isUndefined } from '../../utils/inspect'
6+
import {
7+
isFunction,
8+
isObject,
9+
isNumber,
10+
isString,
11+
isUndefined,
12+
isUndefinedOrNull
13+
} from '../../utils/inspect'
714
import { keys } from '../../utils/object'
815
import { BVPopover } from '../../components/popover/helpers/bv-popover'
916

@@ -58,7 +65,7 @@ const parseBindings = (bindings, vnode) => /* istanbul ignore next: not easy to
5865
}
5966

6067
// Process `bindings.value`
61-
if (isString(bindings.value)) {
68+
if (isString(bindings.value) || isNumber(bindings.value)) {
6269
// Value is popover content (html optionally supported)
6370
config.content = bindings.value
6471
} else if (isFunction(bindings.value)) {
@@ -80,7 +87,7 @@ const parseBindings = (bindings, vnode) => /* istanbul ignore next: not easy to
8087
if (isUndefined(config.title)) {
8188
// Try attribute
8289
const data = vnode.data || {}
83-
config.title = data.attrs && data.attrs.title ? data.attrs.title : undefined
90+
config.title = data.attrs && !isUndefinedOrNull(data.attrs.title) ? data.attrs.title : undefined
8491
}
8592

8693
// Normalize delay

src/directives/tooltip/README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,11 @@ appear.
1616

1717
## Overview
1818

19-
Things to know when using tooltips:
19+
Things to know when using the tooltip directive:
2020

2121
- Tooltips rely on the 3rd party library [Popper.js](https://popper.js.org/) for positioning.
2222
- Tooltips require BootstrapVue's custom SCSS/CSS for transitions and color variants.
23+
- If a title is not provided (or is an empty string), the tooltip will not show.
2324
- Specify container: 'body' (the default) to avoid rendering problems in more complex components
2425
(like input groups, button groups, etc).
2526
- Triggering tooltips on hidden elements will not work.

src/directives/tooltip/tooltip.js

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ import looseEqual from '../../utils/loose-equal'
33
import { concat } from '../../utils/array'
44
import { getComponentConfig } from '../../utils/config'
55
import { isBrowser } from '../../utils/env'
6-
import { isFunction, isObject, isString, isUndefined } from '../../utils/inspect'
6+
import {
7+
isFunction,
8+
isNumber,
9+
isObject,
10+
isString,
11+
isUndefined,
12+
isUndefinedOrNull
13+
} from '../../utils/inspect'
714
import { keys } from '../../utils/object'
815
import { BVTooltip } from '../../components/tooltip/helpers/bv-tooltip'
916

@@ -58,7 +65,7 @@ const parseBindings = (bindings, vnode) => /* istanbul ignore next: not easy to
5865
}
5966

6067
// Process `bindings.value`
61-
if (isString(bindings.value)) {
68+
if (isString(bindings.value) || isNumber(bindings.value)) {
6269
// Value is tooltip content (HTML optionally supported)
6370
config.title = bindings.value
6471
} else if (isFunction(bindings.value)) {
@@ -73,7 +80,7 @@ const parseBindings = (bindings, vnode) => /* istanbul ignore next: not easy to
7380
if (isUndefined(config.title)) {
7481
// Try attribute
7582
const data = vnode.data || {}
76-
config.title = data.attrs && data.attrs.title ? data.attrs.title : ''
83+
config.title = data.attrs && !isUndefinedOrNull(data.attrs.title) ? data.attrs.title : undefined
7784
}
7885

7986
// Normalize delay

src/directives/tooltip/tooltip.spec.js

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,59 @@ describe('v-b-tooltip directive', () => {
136136
wrapper.destroy()
137137
})
138138

139+
it('should nowshow tooltip when title is empty', async () => {
140+
jest.useFakeTimers()
141+
const localVue = new CreateLocalVue()
142+
143+
const App = localVue.extend({
144+
directives: {
145+
bTooltip: VBTooltip
146+
},
147+
template: '<button v-b-tooltip.click title="">button</button>'
148+
})
149+
150+
const wrapper = mount(App, {
151+
localVue: localVue,
152+
attachToDocument: true
153+
})
154+
155+
expect(wrapper.isVueInstance()).toBe(true)
156+
await waitNT(wrapper.vm)
157+
await waitRAF()
158+
await waitNT(wrapper.vm)
159+
await waitRAF()
160+
await waitNT(wrapper.vm)
161+
await waitRAF()
162+
jest.runOnlyPendingTimers()
163+
await waitNT(wrapper.vm)
164+
await waitRAF()
165+
166+
expect(wrapper.is('button')).toBe(true)
167+
const $button = wrapper.find('button')
168+
169+
// Should have instance of popover class on it
170+
expect($button.element[BV_TOOLTIP]).toBeDefined()
171+
expect($button.element[BV_TOOLTIP]).toBeInstanceOf(BVTooltip)
172+
173+
expect($button.attributes('aria-describedby')).not.toBeDefined()
174+
175+
// Trigger click
176+
$button.trigger('click')
177+
await waitNT(wrapper.vm)
178+
await waitRAF()
179+
await waitNT(wrapper.vm)
180+
await waitRAF()
181+
await waitNT(wrapper.vm)
182+
await waitRAF()
183+
jest.runOnlyPendingTimers()
184+
await waitNT(wrapper.vm)
185+
await waitRAF()
186+
187+
expect($button.attributes('aria-describedby')).not.toBeDefined()
188+
189+
wrapper.destroy()
190+
})
191+
139192
it('variant and customClass should work', async () => {
140193
jest.useFakeTimers()
141194
const localVue = new CreateLocalVue()

0 commit comments

Comments
 (0)