Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions src/components/tooltip/helpers/bv-tooltip.js
Original file line number Diff line number Diff line change
Expand Up @@ -391,12 +391,10 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({
const showEvt = this.buildEvent('show', { cancelable: true })
this.emitEvent(showEvt)
// Don't show if event cancelled
/* istanbul ignore next: ignore for now */
/* istanbul ignore if */
if (showEvt.defaultPrevented) {
// Destroy the template (if for some reason it was created)
/* istanbul ignore next */
this.destroyTemplate()
/* istanbul ignore next */
return
}
// Fix the title attribute on target
Expand All @@ -409,21 +407,19 @@ export const BVTooltip = /*#__PURE__*/ Vue.extend({
hide(force = false) {
// Hide the tooltip
const tip = this.getTemplateElement()
/* istanbul ignore if */
if (!tip || !this.localShow) {
/* istanbul ignore next */
this.restoreTitle()
/* istanbul ignore next */
return
}

// Emit cancelable BvEvent 'hide'
// We disable cancelling if `force` is true
const hideEvt = this.buildEvent('hide', { cancelable: !force })
this.emitEvent(hideEvt)
/* istanbul ignore next: ignore for now */
/* istanbul ignore if: ignore for now */
if (hideEvt.defaultPrevented) {
// Don't hide if event cancelled
/* istanbul ignore next */
return
}

Expand Down
160 changes: 158 additions & 2 deletions src/components/tooltip/tooltip.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { BTooltip } from './tooltip'

const localVue = new CreateLocalVue()

const MODAL_CLOSE_EVENT = 'bv::modal::hidden'

// Our test application definition
const appDef = {
props: [
Expand All @@ -17,7 +19,8 @@ const appDef = {
'btnDisabled',
'variant',
'customClass',
'delay'
'delay',
'isModal'
],
render(h) {
const tipProps = {
Expand All @@ -32,7 +35,12 @@ const appDef = {
customClass: this.customClass,
delay: this.delay
}
return h('article', { attrs: { id: 'wrapper' } }, [
const wrapperData = {
attrs: { id: 'wrapper' },
// Class to simulate being in a modal
class: { 'modal-content': !!this.isModal }
}
return h('article', wrapperData, [
h(
'button',
{
Expand Down Expand Up @@ -108,6 +116,7 @@ describe('b-tooltip', () => {
expect(wrapper.is('article')).toBe(true)
expect(wrapper.attributes('id')).toBeDefined()
expect(wrapper.attributes('id')).toEqual('wrapper')
expect(wrapper.classes()).not.toContain('modal-content')

// The trigger button
const $button = wrapper.find('button')
Expand Down Expand Up @@ -954,6 +963,153 @@ describe('b-tooltip', () => {
wrapper.destroy()
})

it('does not close on $root modal hidden event by default', async () => {
jest.useFakeTimers()
const App = localVue.extend(appDef)
const wrapper = mount(App, {
attachToDocument: true,
localVue: localVue,
propsData: {
triggers: 'click',
show: true,
disabled: false,
titleAttr: 'ignored'
},
slots: {
default: 'title'
}
})

expect(wrapper.isVueInstance()).toBe(true)
await waitNT(wrapper.vm)
await waitRAF()
await waitNT(wrapper.vm)
await waitRAF()
jest.runOnlyPendingTimers()
await waitNT(wrapper.vm)
await waitRAF()

expect(wrapper.is('article')).toBe(true)
expect(wrapper.attributes('id')).toBeDefined()
expect(wrapper.attributes('id')).toEqual('wrapper')
expect(wrapper.classes()).not.toContain('modal-content')

// The trigger button
const $button = wrapper.find('button')
expect($button.exists()).toBe(true)
expect($button.attributes('id')).toBeDefined()
expect($button.attributes('id')).toEqual('foo')
expect($button.attributes('title')).toBeDefined()
expect($button.attributes('title')).toEqual('')
expect($button.attributes('data-original-title')).toBeDefined()
expect($button.attributes('data-original-title')).toEqual('ignored')
expect($button.attributes('aria-describedby')).toBeDefined()
// ID of the tooltip that will be in the body
const adb = $button.attributes('aria-describedby')

// b-tooltip wrapper
const $tipHolder = wrapper.find(BTooltip)
expect($tipHolder.exists()).toBe(true)

// Find the tooltip element in the document
const tip = document.getElementById(adb)
expect(tip).not.toBe(null)
expect(tip).toBeInstanceOf(HTMLElement)
expect(tip.tagName).toEqual('DIV')
expect(tip.classList.contains('tooltip')).toBe(true)

// Tooltip should ignore when ID is not its own
wrapper.vm.$root.$emit(MODAL_CLOSE_EVENT, 'some-modal')
await waitNT(wrapper.vm)
await waitRAF()
await waitNT(wrapper.vm)
await waitRAF()
jest.runOnlyPendingTimers()
await waitNT(wrapper.vm)
await waitRAF()

expect($button.attributes('aria-describedby')).toBeDefined()

// Tooltip element should still be in the document
expect(document.body.contains(tip)).toBe(true)
expect(document.getElementById(adb)).not.toBe(null)

wrapper.destroy()
})

it('closes on $root modal hidden event when inside a modal', async () => {
jest.useFakeTimers()
const App = localVue.extend(appDef)
const wrapper = mount(App, {
attachToDocument: true,
localVue: localVue,
propsData: {
triggers: 'click',
show: true,
disabled: false,
titleAttr: 'ignored',
isModal: true
},
slots: {
default: 'title'
}
})

expect(wrapper.isVueInstance()).toBe(true)
await waitNT(wrapper.vm)
await waitRAF()
await waitNT(wrapper.vm)
await waitRAF()
jest.runOnlyPendingTimers()
await waitNT(wrapper.vm)
await waitRAF()

expect(wrapper.is('article')).toBe(true)
expect(wrapper.attributes('id')).toBeDefined()
expect(wrapper.attributes('id')).toEqual('wrapper')
expect(wrapper.classes()).toContain('modal-content')

// The trigger button
const $button = wrapper.find('button')
expect($button.exists()).toBe(true)
expect($button.attributes('id')).toBeDefined()
expect($button.attributes('id')).toEqual('foo')
expect($button.attributes('title')).toBeDefined()
expect($button.attributes('title')).toEqual('')
expect($button.attributes('data-original-title')).toBeDefined()
expect($button.attributes('data-original-title')).toEqual('ignored')
expect($button.attributes('aria-describedby')).toBeDefined()
// ID of the tooltip that will be in the body
const adb = $button.attributes('aria-describedby')

// b-tooltip wrapper
const $tipHolder = wrapper.find(BTooltip)
expect($tipHolder.exists()).toBe(true)

// Find the tooltip element in the document
const tip = document.getElementById(adb)
expect(tip).not.toBe(null)
expect(tip).toBeInstanceOf(HTMLElement)
expect(tip.tagName).toEqual('DIV')
expect(tip.classList.contains('tooltip')).toBe(true)

// Tooltip should ignore when ID is not its own
wrapper.vm.$root.$emit(MODAL_CLOSE_EVENT, 'some-modal')
await waitNT(wrapper.vm)
await waitRAF()
await waitNT(wrapper.vm)
await waitRAF()
jest.runOnlyPendingTimers()
await waitNT(wrapper.vm)
await waitRAF()

// Tooltip element should not be in the document
expect(document.body.contains(tip)).toBe(false)
expect(document.getElementById(adb)).toBe(null)

wrapper.destroy()
})

it('closes when trigger element is no longer visible', async () => {
jest.useFakeTimers()
// Prevent warns from appearing in the test logs
Expand Down