]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Add `getNextActiveElement` helper function to utils, replacing custom implementation...
authorGeoSot <geo.sotis@gmail.com>
Tue, 18 May 2021 22:23:52 +0000 (01:23 +0300)
committerGitHub <noreply@github.com>
Tue, 18 May 2021 22:23:52 +0000 (01:23 +0300)
js/src/carousel.js
js/src/dropdown.js
js/src/util/index.js
js/tests/unit/util/index.spec.js

index bb894e9c3701b8e11a722933f2bf20771e106556..7d197ab1eef68ac90606f1d38ef59f9e2ff85504 100644 (file)
@@ -10,6 +10,7 @@ import {
   getElementFromSelector,
   isRTL,
   isVisible,
+  getNextActiveElement,
   reflow,
   triggerTransitionEnd,
   typeCheckConfig
@@ -337,21 +338,7 @@ class Carousel extends BaseComponent {
 
   _getItemByOrder(order, activeElement) {
     const isNext = order === ORDER_NEXT
-    const isPrev = order === ORDER_PREV
-    const activeIndex = this._getItemIndex(activeElement)
-    const lastItemIndex = this._items.length - 1
-    const isGoingToWrap = (isPrev && activeIndex === 0) || (isNext && activeIndex === lastItemIndex)
-
-    if (isGoingToWrap && !this._config.wrap) {
-      return activeElement
-    }
-
-    const delta = isPrev ? -1 : 1
-    const itemIndex = (activeIndex + delta) % this._items.length
-
-    return itemIndex === -1 ?
-      this._items[this._items.length - 1] :
-      this._items[itemIndex]
+    return getNextActiveElement(this._items, activeElement, isNext, this._config.wrap)
   }
 
   _triggerSlideEvent(relatedTarget, eventDirectionName) {
index 565edb8929e60aeca00ca3e43a7061cb7eceab0e..cab2d018bb030cfa7b275f02f0912503777c652a 100644 (file)
@@ -16,6 +16,7 @@ import {
   isVisible,
   isRTL,
   noop,
+  getNextActiveElement,
   typeCheckConfig
 } from './util/index'
 import Data from './dom/data'
@@ -354,28 +355,17 @@ class Dropdown extends BaseComponent {
   }
 
   _selectMenuItem(event) {
-    const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible)
-
-    if (!items.length) {
+    if (![ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)) {
       return
     }
 
-    let index = items.indexOf(event.target)
-
-    // Up
-    if (event.key === ARROW_UP_KEY && index > 0) {
-      index--
-    }
+    const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible)
 
-    // Down
-    if (event.key === ARROW_DOWN_KEY && index < items.length - 1) {
-      index++
+    if (!items.length) {
+      return
     }
 
-    // index is -1 if the first keydown is an ArrowUp
-    index = index === -1 ? 0 : index
-
-    items[index].focus()
+    getNextActiveElement(items, event.target, event.key === ARROW_DOWN_KEY, false).focus()
   }
 
   // Static
index 9441d0a44a506ceffe132f3a187b16fbefcbbcfb..6b38a05e94a53d17db11ceb7f39f6720df2f1218 100644 (file)
@@ -261,6 +261,34 @@ const execute = callback => {
   }
 }
 
+/**
+ * Return the previous/next element of a list.
+ *
+ * @param {array} list    The list of elements
+ * @param activeElement   The active element
+ * @param shouldGetNext   Choose to get next or previous element
+ * @param isCycleAllowed
+ * @return {Element|elem} The proper element
+ */
+const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {
+  let index = list.indexOf(activeElement)
+
+  // if the element does not exist in the list initialize it as the first element
+  if (index === -1) {
+    return list[0]
+  }
+
+  const listLength = list.length
+
+  index += shouldGetNext ? 1 : -1
+
+  if (isCycleAllowed) {
+    index = (index + listLength) % listLength
+  }
+
+  return list[Math.max(0, Math.min(index, listLength - 1))]
+}
+
 export {
   getElement,
   getUID,
@@ -275,6 +303,7 @@ export {
   isDisabled,
   findShadowRoot,
   noop,
+  getNextActiveElement,
   reflow,
   getjQuery,
   onDOMContentLoaded,
index b4010e0e0e25cc9d20f62e0977d6e342238d26e6..ca6430bee572eff993df07df98c662808670900e 100644 (file)
@@ -611,4 +611,43 @@ describe('Util', () => {
       expect(spy).toHaveBeenCalled()
     })
   })
+
+  describe('getNextActiveElement', () => {
+    it('should return first element if active not exists or not given', () => {
+      const array = ['a', 'b', 'c', 'd']
+
+      expect(Util.getNextActiveElement(array, '', true, true)).toEqual('a')
+      expect(Util.getNextActiveElement(array, 'g', true, true)).toEqual('a')
+    })
+
+    it('should return next element or same if is last', () => {
+      const array = ['a', 'b', 'c', 'd']
+
+      expect(Util.getNextActiveElement(array, 'a', true, true)).toEqual('b')
+      expect(Util.getNextActiveElement(array, 'b', true, true)).toEqual('c')
+      expect(Util.getNextActiveElement(array, 'd', true, false)).toEqual('d')
+    })
+
+    it('should return next element or first, if is last and "isCycleAllowed = true"', () => {
+      const array = ['a', 'b', 'c', 'd']
+
+      expect(Util.getNextActiveElement(array, 'c', true, true)).toEqual('d')
+      expect(Util.getNextActiveElement(array, 'd', true, true)).toEqual('a')
+    })
+
+    it('should return previous element or same if is first', () => {
+      const array = ['a', 'b', 'c', 'd']
+
+      expect(Util.getNextActiveElement(array, 'b', false, true)).toEqual('a')
+      expect(Util.getNextActiveElement(array, 'd', false, true)).toEqual('c')
+      expect(Util.getNextActiveElement(array, 'a', false, false)).toEqual('a')
+    })
+
+    it('should return next element or first, if is last and "isCycleAllowed = true"', () => {
+      const array = ['a', 'b', 'c', 'd']
+
+      expect(Util.getNextActiveElement(array, 'd', false, true)).toEqual('c')
+      expect(Util.getNextActiveElement(array, 'a', false, true)).toEqual('d')
+    })
+  })
 })