},
{
"path": "./dist/js/bootstrap.js",
- "maxSize": "54.5 kB"
+ "maxSize": "55.0 kB"
},
{
"path": "./dist/js/bootstrap.min.js",
continue
}
- if (instance._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {
+ // Don't auto-close when interacting with a form inside the menu — clicks
+ // on a form's labels, buttons, etc. (not just inputs) should keep it open.
+ const formAncestor = event.target.closest?.('form')
+ const isInsideMenuForm = Boolean(formAncestor) && instance._menu.contains(formAncestor)
+ if (instance._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName) || isInsideMenuForm)) {
continue
}
// Overrides
_isWithContent() {
- return this._getTitle() || this._getContent()
+ return Boolean(this._getTitle() || this._getContent()) || this._hasNewContent()
}
// Private
// Protected
_isWithContent() {
- return Boolean(this._getTitle())
+ return Boolean(this._getTitle()) || this._hasNewContent()
+ }
+
+ // Content supplied via setContent() (a `{ selector: content }` map) overrides
+ // the configured title/content when rendering, so it should also satisfy the
+ // show() gate — otherwise a tip whose content is only set via setContent()
+ // can never be shown.
+ _hasNewContent() {
+ return Boolean(this._newContent) && Object.values(this._newContent).some(Boolean)
}
_getTipElement() {
})
})
+ it('should not close the menu if the user clicks a label inside a form within the menu', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div>',
+ ' <button class="btn" data-bs-toggle="menu">Menu</button>',
+ ' <div class="menu">',
+ ' <form>',
+ ' <label for="menu-email">Email</label>',
+ ' <input type="text" id="menu-email">',
+ ' </form>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const triggerMenu = fixtureEl.querySelector('[data-bs-toggle="menu"]')
+ const label = fixtureEl.querySelector('label')
+
+ triggerMenu.addEventListener('shown.bs.menu', () => {
+ expect(triggerMenu).toHaveClass('show')
+ // clearMenus runs synchronously while this bubbling click reaches the
+ // document, so assert right after dispatch returns.
+ label.dispatchEvent(createEvent('click', { bubbles: true }))
+ expect(triggerMenu).toHaveClass('show')
+ resolve()
+ })
+
+ triggerMenu.click()
+ })
+ })
+
it('should close the menu if the user clicks on a text field that is not contained within menu', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = [
})
})
+ it('should show a popover whose content was set only via setContent()', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#">Nice link</a>'
+
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl, {})
+
+ popover.setContent({ '.popover-header': 'Late header', '.popover-body': 'Late content' })
+
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ const popoverDisplayed = document.querySelector('.popover')
+
+ expect(popoverDisplayed).not.toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Late content')
+ resolve()
+ })
+
+ popover.show()
+ })
+ })
+
it('should show a popover with just title without having body', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = '<a href="#">Nice link</a>'