Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/twbs/bootstrap.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorMartijn Cuppens <martijn.cuppens@gmail.com>2020-02-01 16:56:20 +0300
committerMartijn Cuppens <martijn.cuppens@gmail.com>2020-03-23 17:35:07 +0300
commit2e150e722a946f20ded62b39abe28f244f6ae050 (patch)
treeb8e23908d7a08583f56367aa5190f68747dd345e /js
parent85b12549ecff83f866588394ecff747e69567bc4 (diff)
Use next dropdown menu instead of first of the parent
Diffstat (limited to 'js')
-rw-r--r--js/src/dom/selector-engine.js14
-rw-r--r--js/src/dropdown.js37
-rw-r--r--js/tests/unit/dom/selector-engine.spec.js39
-rw-r--r--js/tests/unit/dropdown.spec.js54
4 files changed, 97 insertions, 47 deletions
diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js
index a8c14ae4c8..c18ec4136f 100644
--- a/js/src/dom/selector-engine.js
+++ b/js/src/dom/selector-engine.js
@@ -67,6 +67,20 @@ const SelectorEngine = {
}
return []
+ },
+
+ next(element, selector) {
+ let next = element.nextElementSibling
+
+ while (next) {
+ if (this.matches(next, selector)) {
+ return [next]
+ }
+
+ next = next.nextElementSibling
+ }
+
+ return []
}
}
diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index 9d6f8a3296..b8f8c22b8c 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -129,7 +129,7 @@ class Dropdown {
return
}
- const isActive = this._menu.classList.contains(CLASS_NAME_SHOW)
+ const isActive = this._element.classList.contains(CLASS_NAME_SHOW)
Dropdown.clearMenus()
@@ -150,7 +150,7 @@ class Dropdown {
relatedTarget: this._element
}
- const showEvent = EventHandler.trigger(parent, EVENT_SHOW, relatedTarget)
+ const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget)
if (showEvent.defaultPrevented) {
return
@@ -199,7 +199,7 @@ class Dropdown {
this._element.setAttribute('aria-expanded', true)
Manipulator.toggleClass(this._menu, CLASS_NAME_SHOW)
- Manipulator.toggleClass(parent, CLASS_NAME_SHOW)
+ Manipulator.toggleClass(this._element, CLASS_NAME_SHOW)
EventHandler.trigger(parent, EVENT_SHOWN, relatedTarget)
}
@@ -224,7 +224,7 @@ class Dropdown {
}
Manipulator.toggleClass(this._menu, CLASS_NAME_SHOW)
- Manipulator.toggleClass(parent, CLASS_NAME_SHOW)
+ Manipulator.toggleClass(this._element, CLASS_NAME_SHOW)
EventHandler.trigger(parent, EVENT_HIDDEN, relatedTarget)
}
@@ -273,9 +273,7 @@ class Dropdown {
}
_getMenuElement() {
- const parent = Dropdown.getParentFromElement(this._element)
-
- return SelectorEngine.findOne(SELECTOR_MENU, parent)
+ return SelectorEngine.next(this._element, SELECTOR_MENU)[0]
}
_getPlacement() {
@@ -397,14 +395,14 @@ class Dropdown {
}
const dropdownMenu = context._menu
- if (!parent.classList.contains(CLASS_NAME_SHOW)) {
+ if (!toggles[i].classList.contains(CLASS_NAME_SHOW)) {
continue
}
if (event && ((event.type === 'click' &&
/input|textarea/i.test(event.target.tagName)) ||
(event.type === 'keyup' && event.which === TAB_KEYCODE)) &&
- parent.contains(event.target)) {
+ dropdownMenu.contains(event.target)) {
continue
}
@@ -427,7 +425,7 @@ class Dropdown {
}
dropdownMenu.classList.remove(CLASS_NAME_SHOW)
- parent.classList.remove(CLASS_NAME_SHOW)
+ toggles[i].classList.remove(CLASS_NAME_SHOW)
EventHandler.trigger(parent, EVENT_HIDDEN, relatedTarget)
}
}
@@ -460,13 +458,16 @@ class Dropdown {
}
const parent = Dropdown.getParentFromElement(this)
- const isActive = parent.classList.contains(CLASS_NAME_SHOW)
+ const isActive = this.classList.contains(CLASS_NAME_SHOW)
- if (!isActive || (isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE))) {
- if (event.which === ESCAPE_KEYCODE) {
- SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, parent).focus()
- }
+ if (event.which === ESCAPE_KEYCODE) {
+ const button = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]
+ button.focus()
+ Dropdown.clearMenus()
+ return
+ }
+ if (!isActive || event.which === SPACE_KEYCODE) {
Dropdown.clearMenus()
return
}
@@ -478,7 +479,7 @@ class Dropdown {
return
}
- let index = items.indexOf(event.target)
+ let index = items.indexOf(event.target) || 0
if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up
index--
@@ -488,10 +489,6 @@ class Dropdown {
index++
}
- if (index < 0) {
- index = 0
- }
-
items[index].focus()
}
diff --git a/js/tests/unit/dom/selector-engine.spec.js b/js/tests/unit/dom/selector-engine.spec.js
index e140c6a3e8..727f106214 100644
--- a/js/tests/unit/dom/selector-engine.spec.js
+++ b/js/tests/unit/dom/selector-engine.spec.js
@@ -126,5 +126,44 @@ describe('SelectorEngine', () => {
expect(SelectorEngine.prev(btn, '.test')).toEqual([divTest])
})
})
+
+ describe('next', () => {
+ it('should return next element', () => {
+ fixtureEl.innerHTML = '<div class="test"></div><button class="btn"></button>'
+
+ const btn = fixtureEl.querySelector('.btn')
+ const divTest = fixtureEl.querySelector('.test')
+
+ expect(SelectorEngine.next(divTest, '.btn')).toEqual([btn])
+ })
+
+ it('should return next element with an extra element between', () => {
+ fixtureEl.innerHTML = [
+ '<div class="test"></div>',
+ '<span></span>',
+ '<button class="btn"></button>'
+ ].join('')
+
+ const btn = fixtureEl.querySelector('.btn')
+ const divTest = fixtureEl.querySelector('.test')
+
+ expect(SelectorEngine.next(divTest, '.btn')).toEqual([btn])
+ })
+
+ it('should return next element with comments or text nodes between', () => {
+ fixtureEl.innerHTML = [
+ '<div class="test"></div>',
+ '<!-- Comment-->',
+ 'Text',
+ '<button class="btn"></button>',
+ '<button class="btn"></button>'
+ ].join('')
+
+ const btn = fixtureEl.querySelector('.btn')
+ const divTest = fixtureEl.querySelector('.test')
+
+ expect(SelectorEngine.next(divTest, '.btn')).toEqual([btn])
+ })
+ })
})
diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js
index 2762ad21b9..e8a7e66ba9 100644
--- a/js/tests/unit/dropdown.spec.js
+++ b/js/tests/unit/dropdown.spec.js
@@ -139,7 +139,7 @@ describe('Dropdown', () => {
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
})
@@ -171,7 +171,7 @@ describe('Dropdown', () => {
const dropdown2 = new Dropdown(btnDropdown2)
firstDropdownEl.addEventListener('shown.bs.dropdown', () => {
- expect(firstDropdownEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown1.classList.contains('show')).toEqual(true)
spyOn(dropdown1._popper, 'destroy')
dropdown2.toggle()
})
@@ -204,7 +204,7 @@ describe('Dropdown', () => {
spyOn(EventHandler, 'off')
dropdownEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
expect(EventHandler.on).toHaveBeenCalled()
@@ -212,7 +212,7 @@ describe('Dropdown', () => {
})
dropdownEl.addEventListener('hidden.bs.dropdown', () => {
- expect(dropdownEl.classList.contains('show')).toEqual(false)
+ expect(btnDropdown.classList.contains('show')).toEqual(false)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
expect(EventHandler.off).toHaveBeenCalled()
@@ -238,7 +238,7 @@ describe('Dropdown', () => {
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
})
@@ -261,7 +261,7 @@ describe('Dropdown', () => {
const dropdown = new Dropdown(btnDropdown)
dropupEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropupEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
})
@@ -284,7 +284,7 @@ describe('Dropdown', () => {
const dropdown = new Dropdown(btnDropdown)
dropupEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropupEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
})
@@ -307,7 +307,7 @@ describe('Dropdown', () => {
const dropdown = new Dropdown(btnDropdown)
droprightEl.addEventListener('shown.bs.dropdown', () => {
- expect(droprightEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
})
@@ -330,7 +330,7 @@ describe('Dropdown', () => {
const dropdown = new Dropdown(btnDropdown)
dropleftEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropleftEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
})
@@ -355,7 +355,7 @@ describe('Dropdown', () => {
})
dropdownEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
})
@@ -380,7 +380,7 @@ describe('Dropdown', () => {
})
dropdownEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
})
@@ -405,7 +405,7 @@ describe('Dropdown', () => {
})
dropdownEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
done()
})
@@ -538,7 +538,7 @@ describe('Dropdown', () => {
const dropdown = new Dropdown(btnDropdown)
dropdownEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
done()
})
@@ -983,7 +983,7 @@ describe('Dropdown', () => {
})
dropdownEl.addEventListener('shown.bs.dropdown', e => {
- expect(dropdownEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
expect(showEventTriggered).toEqual(true)
expect(e.relatedTarget).toEqual(btnDropdown)
@@ -995,7 +995,7 @@ describe('Dropdown', () => {
})
dropdownEl.addEventListener('hidden.bs.dropdown', e => {
- expect(dropdownEl.classList.contains('show')).toEqual(false)
+ expect(btnDropdown.classList.contains('show')).toEqual(false)
expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
expect(hideEventTriggered).toEqual(true)
expect(e.relatedTarget).toEqual(btnDropdown)
@@ -1066,7 +1066,7 @@ describe('Dropdown', () => {
const dropdownEl = fixtureEl.querySelector('.dropdown')
dropdownEl.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownEl.classList.contains('show')).toEqual(true)
+ expect(btnDropdown.classList.contains('show')).toEqual(true)
const keyUp = createEvent('keyup')
@@ -1075,7 +1075,7 @@ describe('Dropdown', () => {
})
dropdownEl.addEventListener('hidden.bs.dropdown', () => {
- expect(dropdownEl.classList.contains('show')).toEqual(false)
+ expect(btnDropdown.classList.contains('show')).toEqual(false)
done()
})
@@ -1111,7 +1111,7 @@ describe('Dropdown', () => {
const btnGroup = last.parentNode
dropdownTestMenu.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownTestMenu.classList.contains('show')).toEqual(true)
+ expect(first.classList.contains('show')).toEqual(true)
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1)
document.body.click()
})
@@ -1122,7 +1122,7 @@ describe('Dropdown', () => {
})
btnGroup.addEventListener('shown.bs.dropdown', () => {
- expect(btnGroup.classList.contains('show')).toEqual(true)
+ expect(last.classList.contains('show')).toEqual(true)
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1)
document.body.click()
})
@@ -1162,7 +1162,7 @@ describe('Dropdown', () => {
const btnGroup = last.parentNode
dropdownTestMenu.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownTestMenu.classList.contains('show')).toEqual(true, '"show" class added on click')
+ expect(first.classList.contains('show')).toEqual(true, '"show" class added on click')
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1, 'only one dropdown is shown')
const keyUp = createEvent('keyup')
@@ -1177,7 +1177,7 @@ describe('Dropdown', () => {
})
btnGroup.addEventListener('shown.bs.dropdown', () => {
- expect(btnGroup.classList.contains('show')).toEqual(true, '"show" class added on click')
+ expect(last.classList.contains('show')).toEqual(true, '"show" class added on click')
expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1, 'only one dropdown is shown')
const keyUp = createEvent('keyup')
@@ -1382,12 +1382,12 @@ describe('Dropdown', () => {
const input = fixtureEl.querySelector('input')
input.addEventListener('click', () => {
- expect(dropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
+ expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
done()
})
dropdown.addEventListener('shown.bs.dropdown', () => {
- expect(dropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
+ expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
input.dispatchEvent(createEvent('click'))
})
@@ -1409,12 +1409,12 @@ describe('Dropdown', () => {
const textarea = fixtureEl.querySelector('textarea')
textarea.addEventListener('click', () => {
- expect(dropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
+ expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
done()
})
dropdown.addEventListener('shown.bs.dropdown', () => {
- expect(dropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
+ expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
textarea.dispatchEvent(createEvent('click'))
})
@@ -1492,7 +1492,7 @@ describe('Dropdown', () => {
input.focus()
input.dispatchEvent(keyDownEscape)
- expect(dropdown.classList.contains('show')).toEqual(false, 'dropdown menu is not shown')
+ expect(triggerDropdown.classList.contains('show')).toEqual(false, 'dropdown menu is not shown')
done()
})
@@ -1529,7 +1529,7 @@ describe('Dropdown', () => {
setTimeout(() => {
expect(dropdown.toggle).not.toHaveBeenCalled()
- expect(triggerDropdown.parentNode.classList.contains('show')).toEqual(false)
+ expect(triggerDropdown.classList.contains('show')).toEqual(false)
done()
}, 20)
})