Add `getNextActiveElement` helper function to utils, replacing custom implementation through components (#33608)

This commit is contained in:
GeoSot 2021-05-19 01:23:52 +03:00 committed by GitHub
parent 372890b67b
commit df72a21fa8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 76 additions and 31 deletions

View File

@ -10,6 +10,7 @@ import {
getElementFromSelector, getElementFromSelector,
isRTL, isRTL,
isVisible, isVisible,
getNextActiveElement,
reflow, reflow,
triggerTransitionEnd, triggerTransitionEnd,
typeCheckConfig typeCheckConfig
@ -337,21 +338,7 @@ class Carousel extends BaseComponent {
_getItemByOrder(order, activeElement) { _getItemByOrder(order, activeElement) {
const isNext = order === ORDER_NEXT const isNext = order === ORDER_NEXT
const isPrev = order === ORDER_PREV return getNextActiveElement(this._items, activeElement, isNext, this._config.wrap)
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]
} }
_triggerSlideEvent(relatedTarget, eventDirectionName) { _triggerSlideEvent(relatedTarget, eventDirectionName) {

View File

@ -16,6 +16,7 @@ import {
isVisible, isVisible,
isRTL, isRTL,
noop, noop,
getNextActiveElement,
typeCheckConfig typeCheckConfig
} from './util/index' } from './util/index'
import Data from './dom/data' import Data from './dom/data'
@ -354,28 +355,17 @@ class Dropdown extends BaseComponent {
} }
_selectMenuItem(event) { _selectMenuItem(event) {
if (![ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)) {
return
}
const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible) const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible)
if (!items.length) { if (!items.length) {
return return
} }
let index = items.indexOf(event.target) getNextActiveElement(items, event.target, event.key === ARROW_DOWN_KEY, false).focus()
// Up
if (event.key === ARROW_UP_KEY && index > 0) {
index--
}
// Down
if (event.key === ARROW_DOWN_KEY && index < items.length - 1) {
index++
}
// index is -1 if the first keydown is an ArrowUp
index = index === -1 ? 0 : index
items[index].focus()
} }
// Static // Static

View 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 { export {
getElement, getElement,
getUID, getUID,
@ -275,6 +303,7 @@ export {
isDisabled, isDisabled,
findShadowRoot, findShadowRoot,
noop, noop,
getNextActiveElement,
reflow, reflow,
getjQuery, getjQuery,
onDOMContentLoaded, onDOMContentLoaded,

View File

@ -611,4 +611,43 @@ describe('Util', () => {
expect(spy).toHaveBeenCalled() 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')
})
})
}) })