mirror of
https://github.com/tenrok/bootstrap.git
synced 2026-06-14 18:42:30 +03:00
Merge branch 'main' into prepare-523
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"extends": "../.eslintrc.json",
|
||||
"env": {
|
||||
"es2022": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": "latest",
|
||||
"sourceType": "module"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"./*.js",
|
||||
"./src/**/*.js"
|
||||
],
|
||||
"rules": {
|
||||
"import/extensions": [
|
||||
2,
|
||||
{
|
||||
"js": "always"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+12
-12
@@ -5,15 +5,15 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
export { default as Alert } from './src/alert'
|
||||
export { default as Button } from './src/button'
|
||||
export { default as Carousel } from './src/carousel'
|
||||
export { default as Collapse } from './src/collapse'
|
||||
export { default as Dropdown } from './src/dropdown'
|
||||
export { default as Modal } from './src/modal'
|
||||
export { default as Offcanvas } from './src/offcanvas'
|
||||
export { default as Popover } from './src/popover'
|
||||
export { default as ScrollSpy } from './src/scrollspy'
|
||||
export { default as Tab } from './src/tab'
|
||||
export { default as Toast } from './src/toast'
|
||||
export { default as Tooltip } from './src/tooltip'
|
||||
export { default as Alert } from './src/alert.js'
|
||||
export { default as Button } from './src/button.js'
|
||||
export { default as Carousel } from './src/carousel.js'
|
||||
export { default as Collapse } from './src/collapse.js'
|
||||
export { default as Dropdown } from './src/dropdown.js'
|
||||
export { default as Modal } from './src/modal.js'
|
||||
export { default as Offcanvas } from './src/offcanvas.js'
|
||||
export { default as Popover } from './src/popover.js'
|
||||
export { default as ScrollSpy } from './src/scrollspy.js'
|
||||
export { default as Tab } from './src/tab.js'
|
||||
export { default as Toast } from './src/toast.js'
|
||||
export { default as Tooltip } from './src/tooltip.js'
|
||||
|
||||
+12
-12
@@ -5,18 +5,18 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import Alert from './src/alert'
|
||||
import Button from './src/button'
|
||||
import Carousel from './src/carousel'
|
||||
import Collapse from './src/collapse'
|
||||
import Dropdown from './src/dropdown'
|
||||
import Modal from './src/modal'
|
||||
import Offcanvas from './src/offcanvas'
|
||||
import Popover from './src/popover'
|
||||
import ScrollSpy from './src/scrollspy'
|
||||
import Tab from './src/tab'
|
||||
import Toast from './src/toast'
|
||||
import Tooltip from './src/tooltip'
|
||||
import Alert from './src/alert.js'
|
||||
import Button from './src/button.js'
|
||||
import Carousel from './src/carousel.js'
|
||||
import Collapse from './src/collapse.js'
|
||||
import Dropdown from './src/dropdown.js'
|
||||
import Modal from './src/modal.js'
|
||||
import Offcanvas from './src/offcanvas.js'
|
||||
import Popover from './src/popover.js'
|
||||
import ScrollSpy from './src/scrollspy.js'
|
||||
import Tab from './src/tab.js'
|
||||
import Toast from './src/toast.js'
|
||||
import Tooltip from './src/tooltip.js'
|
||||
|
||||
export default {
|
||||
Alert,
|
||||
|
||||
+4
-4
@@ -5,10 +5,10 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin } from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import BaseComponent from './base-component'
|
||||
import { enableDismissTrigger } from './util/component-functions'
|
||||
import { defineJQueryPlugin } from './util/index.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
import { enableDismissTrigger } from './util/component-functions.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import Data from './dom/data'
|
||||
import { executeAfterTransition, getElement } from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import Config from './util/config'
|
||||
import Data from './dom/data.js'
|
||||
import { executeAfterTransition, getElement } from './util/index.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import Config from './util/config.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
|
||||
+3
-3
@@ -5,9 +5,9 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin } from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import BaseComponent from './base-component'
|
||||
import { defineJQueryPlugin } from './util/index.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
|
||||
+7
-8
@@ -7,18 +7,17 @@
|
||||
|
||||
import {
|
||||
defineJQueryPlugin,
|
||||
getElementFromSelector,
|
||||
getNextActiveElement,
|
||||
isRTL,
|
||||
isVisible,
|
||||
reflow,
|
||||
triggerTransitionEnd
|
||||
} from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import Manipulator from './dom/manipulator'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import Swipe from './util/swipe'
|
||||
import BaseComponent from './base-component'
|
||||
} from './util/index.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import Manipulator from './dom/manipulator.js'
|
||||
import SelectorEngine from './dom/selector-engine.js'
|
||||
import Swipe from './util/swipe.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@@ -431,7 +430,7 @@ class Carousel extends BaseComponent {
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {
|
||||
const target = getElementFromSelector(this)
|
||||
const target = SelectorEngine.getElementFromSelector(this)
|
||||
|
||||
if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
|
||||
return
|
||||
|
||||
+8
-13
@@ -8,13 +8,11 @@
|
||||
import {
|
||||
defineJQueryPlugin,
|
||||
getElement,
|
||||
getElementFromSelector,
|
||||
getSelectorFromElement,
|
||||
reflow
|
||||
} from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import BaseComponent from './base-component'
|
||||
} from './util/index.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import SelectorEngine from './dom/selector-engine.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@@ -68,7 +66,7 @@ class Collapse extends BaseComponent {
|
||||
const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)
|
||||
|
||||
for (const elem of toggleList) {
|
||||
const selector = getSelectorFromElement(elem)
|
||||
const selector = SelectorEngine.getSelectorFromElement(elem)
|
||||
const filterElement = SelectorEngine.find(selector)
|
||||
.filter(foundElement => foundElement === this._element)
|
||||
|
||||
@@ -185,7 +183,7 @@ class Collapse extends BaseComponent {
|
||||
this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)
|
||||
|
||||
for (const trigger of this._triggerArray) {
|
||||
const element = getElementFromSelector(trigger)
|
||||
const element = SelectorEngine.getElementFromSelector(trigger)
|
||||
|
||||
if (element && !this._isShown(element)) {
|
||||
this._addAriaAndCollapsedClass([trigger], false)
|
||||
@@ -229,7 +227,7 @@ class Collapse extends BaseComponent {
|
||||
const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)
|
||||
|
||||
for (const element of children) {
|
||||
const selected = getElementFromSelector(element)
|
||||
const selected = SelectorEngine.getElementFromSelector(element)
|
||||
|
||||
if (selected) {
|
||||
this._addAriaAndCollapsedClass([element], this._isShown(selected))
|
||||
@@ -285,10 +283,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
|
||||
event.preventDefault()
|
||||
}
|
||||
|
||||
const selector = getSelectorFromElement(this)
|
||||
const selectorElements = SelectorEngine.find(selector)
|
||||
|
||||
for (const element of selectorElements) {
|
||||
for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {
|
||||
Collapse.getOrCreateInstance(element, { toggle: false }).toggle()
|
||||
}
|
||||
})
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { getjQuery } from '../util/index'
|
||||
import { getjQuery } from '../util/index.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@@ -198,9 +198,8 @@ function removeHandler(element, events, typeEvent, handler, delegationSelector)
|
||||
function removeNamespacedHandlers(element, events, typeEvent, namespace) {
|
||||
const storeElementEvent = events[typeEvent] || {}
|
||||
|
||||
for (const handlerKey of Object.keys(storeElementEvent)) {
|
||||
for (const [handlerKey, event] of Object.entries(storeElementEvent)) {
|
||||
if (handlerKey.includes(namespace)) {
|
||||
const event = storeElementEvent[handlerKey]
|
||||
removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)
|
||||
}
|
||||
}
|
||||
@@ -248,11 +247,10 @@ const EventHandler = {
|
||||
}
|
||||
}
|
||||
|
||||
for (const keyHandlers of Object.keys(storeElementEvent)) {
|
||||
for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {
|
||||
const handlerKey = keyHandlers.replace(stripUidRegex, '')
|
||||
|
||||
if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
|
||||
const event = storeElementEvent[keyHandlers]
|
||||
removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)
|
||||
}
|
||||
}
|
||||
@@ -300,8 +298,8 @@ const EventHandler = {
|
||||
}
|
||||
}
|
||||
|
||||
function hydrateObj(obj, meta) {
|
||||
for (const [key, value] of Object.entries(meta || {})) {
|
||||
function hydrateObj(obj, meta = {}) {
|
||||
for (const [key, value] of Object.entries(meta)) {
|
||||
try {
|
||||
obj[key] = value
|
||||
} catch {
|
||||
|
||||
@@ -5,11 +5,32 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { isDisabled, isVisible } from '../util/index'
|
||||
import { isDisabled, isVisible, parseSelector } from '../util/index.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
*/
|
||||
const getSelector = element => {
|
||||
let selector = element.getAttribute('data-bs-target')
|
||||
|
||||
if (!selector || selector === '#') {
|
||||
let hrefAttribute = element.getAttribute('href')
|
||||
|
||||
// The only valid content that could double as a selector are IDs or classes,
|
||||
// so everything starting with `#` or `.`. If a "real" URL is used as the selector,
|
||||
// `document.querySelector` will rightfully complain it is invalid.
|
||||
// See https://github.com/twbs/bootstrap/issues/32273
|
||||
if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Just in case some CMS puts out a full URL with the anchor appended
|
||||
if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {
|
||||
hrefAttribute = `#${hrefAttribute.split('#')[1]}`
|
||||
}
|
||||
|
||||
selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null
|
||||
}
|
||||
|
||||
return parseSelector(selector)
|
||||
}
|
||||
|
||||
const SelectorEngine = {
|
||||
find(selector, element = document.documentElement) {
|
||||
@@ -77,6 +98,28 @@ const SelectorEngine = {
|
||||
].map(selector => `${selector}:not([tabindex^="-"])`).join(',')
|
||||
|
||||
return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))
|
||||
},
|
||||
|
||||
getSelectorFromElement(element) {
|
||||
const selector = getSelector(element)
|
||||
|
||||
if (selector) {
|
||||
return SelectorEngine.findOne(selector) ? selector : null
|
||||
}
|
||||
|
||||
return null
|
||||
},
|
||||
|
||||
getElementFromSelector(element) {
|
||||
const selector = getSelector(element)
|
||||
|
||||
return selector ? SelectorEngine.findOne(selector) : null
|
||||
},
|
||||
|
||||
getMultipleElementsFromSelector(element) {
|
||||
const selector = getSelector(element)
|
||||
|
||||
return selector ? SelectorEngine.find(selector) : []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-6
@@ -8,6 +8,7 @@
|
||||
import * as Popper from '@popperjs/core'
|
||||
import {
|
||||
defineJQueryPlugin,
|
||||
execute,
|
||||
getElement,
|
||||
getNextActiveElement,
|
||||
isDisabled,
|
||||
@@ -15,11 +16,11 @@ import {
|
||||
isRTL,
|
||||
isVisible,
|
||||
noop
|
||||
} from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import Manipulator from './dom/manipulator'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import BaseComponent from './base-component'
|
||||
} from './util/index.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import Manipulator from './dom/manipulator.js'
|
||||
import SelectorEngine from './dom/selector-engine.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@@ -319,7 +320,7 @@ class Dropdown extends BaseComponent {
|
||||
|
||||
return {
|
||||
...defaultBsPopperConfig,
|
||||
...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
|
||||
...execute(this._config.popperConfig, [defaultBsPopperConfig])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-9
@@ -5,14 +5,14 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin, getElementFromSelector, isRTL, isVisible, reflow } from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import ScrollBarHelper from './util/scrollbar'
|
||||
import BaseComponent from './base-component'
|
||||
import Backdrop from './util/backdrop'
|
||||
import FocusTrap from './util/focustrap'
|
||||
import { enableDismissTrigger } from './util/component-functions'
|
||||
import { defineJQueryPlugin, isRTL, isVisible, reflow } from './util/index.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import SelectorEngine from './dom/selector-engine.js'
|
||||
import ScrollBarHelper from './util/scrollbar.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
import Backdrop from './util/backdrop.js'
|
||||
import FocusTrap from './util/focustrap.js'
|
||||
import { enableDismissTrigger } from './util/component-functions.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@@ -336,7 +336,7 @@ class Modal extends BaseComponent {
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
const target = getElementFromSelector(this)
|
||||
const target = SelectorEngine.getElementFromSelector(this)
|
||||
|
||||
if (['A', 'AREA'].includes(this.tagName)) {
|
||||
event.preventDefault()
|
||||
|
||||
+9
-10
@@ -7,17 +7,16 @@
|
||||
|
||||
import {
|
||||
defineJQueryPlugin,
|
||||
getElementFromSelector,
|
||||
isDisabled,
|
||||
isVisible
|
||||
} from './util/index'
|
||||
import ScrollBarHelper from './util/scrollbar'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import BaseComponent from './base-component'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import Backdrop from './util/backdrop'
|
||||
import FocusTrap from './util/focustrap'
|
||||
import { enableDismissTrigger } from './util/component-functions'
|
||||
} from './util/index.js'
|
||||
import ScrollBarHelper from './util/scrollbar.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
import SelectorEngine from './dom/selector-engine.js'
|
||||
import Backdrop from './util/backdrop.js'
|
||||
import FocusTrap from './util/focustrap.js'
|
||||
import { enableDismissTrigger } from './util/component-functions.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@@ -231,7 +230,7 @@ class Offcanvas extends BaseComponent {
|
||||
*/
|
||||
|
||||
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
|
||||
const target = getElementFromSelector(this)
|
||||
const target = SelectorEngine.getElementFromSelector(this)
|
||||
|
||||
if (['A', 'AREA'].includes(this.tagName)) {
|
||||
event.preventDefault()
|
||||
|
||||
+2
-2
@@ -5,8 +5,8 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin } from './util/index'
|
||||
import Tooltip from './tooltip'
|
||||
import { defineJQueryPlugin } from './util/index.js'
|
||||
import Tooltip from './tooltip.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
|
||||
+4
-4
@@ -5,10 +5,10 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin, getElement, isDisabled, isVisible } from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import BaseComponent from './base-component'
|
||||
import { defineJQueryPlugin, getElement, isDisabled, isVisible } from './util/index.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import SelectorEngine from './dom/selector-engine.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
|
||||
+7
-7
@@ -5,10 +5,10 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin, getElementFromSelector, getNextActiveElement, isDisabled } from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import SelectorEngine from './dom/selector-engine'
|
||||
import BaseComponent from './base-component'
|
||||
import { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import SelectorEngine from './dom/selector-engine.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@@ -106,7 +106,7 @@ class Tab extends BaseComponent {
|
||||
|
||||
element.classList.add(CLASS_NAME_ACTIVE)
|
||||
|
||||
this._activate(getElementFromSelector(element)) // Search and activate/show the proper section
|
||||
this._activate(SelectorEngine.getElementFromSelector(element)) // Search and activate/show the proper section
|
||||
|
||||
const complete = () => {
|
||||
if (element.getAttribute('role') !== 'tab') {
|
||||
@@ -133,7 +133,7 @@ class Tab extends BaseComponent {
|
||||
element.classList.remove(CLASS_NAME_ACTIVE)
|
||||
element.blur()
|
||||
|
||||
this._deactivate(getElementFromSelector(element)) // Search and deactivate the shown section too
|
||||
this._deactivate(SelectorEngine.getElementFromSelector(element)) // Search and deactivate the shown section too
|
||||
|
||||
const complete = () => {
|
||||
if (element.getAttribute('role') !== 'tab') {
|
||||
@@ -203,7 +203,7 @@ class Tab extends BaseComponent {
|
||||
}
|
||||
|
||||
_setInitialAttributesOnTargetPanel(child) {
|
||||
const target = getElementFromSelector(child)
|
||||
const target = SelectorEngine.getElementFromSelector(child)
|
||||
|
||||
if (!target) {
|
||||
return
|
||||
|
||||
+4
-4
@@ -5,10 +5,10 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { defineJQueryPlugin, reflow } from './util/index'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import BaseComponent from './base-component'
|
||||
import { enableDismissTrigger } from './util/component-functions'
|
||||
import { defineJQueryPlugin, reflow } from './util/index.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
import { enableDismissTrigger } from './util/component-functions.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
|
||||
+12
-14
@@ -6,12 +6,12 @@
|
||||
*/
|
||||
|
||||
import * as Popper from '@popperjs/core'
|
||||
import { defineJQueryPlugin, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index'
|
||||
import { DefaultAllowlist } from './util/sanitizer'
|
||||
import EventHandler from './dom/event-handler'
|
||||
import Manipulator from './dom/manipulator'
|
||||
import BaseComponent from './base-component'
|
||||
import TemplateFactory from './util/template-factory'
|
||||
import { defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop } from './util/index.js'
|
||||
import { DefaultAllowlist } from './util/sanitizer.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import Manipulator from './dom/manipulator.js'
|
||||
import BaseComponent from './base-component.js'
|
||||
import TemplateFactory from './util/template-factory.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@@ -370,9 +370,7 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
|
||||
_createPopper(tip) {
|
||||
const placement = typeof this._config.placement === 'function' ?
|
||||
this._config.placement.call(this, tip, this._element) :
|
||||
this._config.placement
|
||||
const placement = execute(this._config.placement, [this, tip, this._element])
|
||||
const attachment = AttachmentMap[placement.toUpperCase()]
|
||||
return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
|
||||
}
|
||||
@@ -392,7 +390,7 @@ class Tooltip extends BaseComponent {
|
||||
}
|
||||
|
||||
_resolvePossibleFunction(arg) {
|
||||
return typeof arg === 'function' ? arg.call(this._element) : arg
|
||||
return execute(arg, [this._element])
|
||||
}
|
||||
|
||||
_getPopperConfig(attachment) {
|
||||
@@ -438,7 +436,7 @@ class Tooltip extends BaseComponent {
|
||||
|
||||
return {
|
||||
...defaultBsPopperConfig,
|
||||
...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
|
||||
...execute(this._config.popperConfig, [defaultBsPopperConfig])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -579,9 +577,9 @@ class Tooltip extends BaseComponent {
|
||||
_getDelegateConfig() {
|
||||
const config = {}
|
||||
|
||||
for (const key in this._config) {
|
||||
if (this.constructor.Default[key] !== this._config[key]) {
|
||||
config[key] = this._config[key]
|
||||
for (const [key, value] of Object.entries(this._config)) {
|
||||
if (this.constructor.Default[key] !== value) {
|
||||
config[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import EventHandler from '../dom/event-handler'
|
||||
import { execute, executeAfterTransition, getElement, reflow } from './index'
|
||||
import Config from './config'
|
||||
import EventHandler from '../dom/event-handler.js'
|
||||
import { execute, executeAfterTransition, getElement, reflow } from './index.js'
|
||||
import Config from './config.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import EventHandler from '../dom/event-handler'
|
||||
import { getElementFromSelector, isDisabled } from './index'
|
||||
import EventHandler from '../dom/event-handler.js'
|
||||
import { isDisabled } from './index.js'
|
||||
import SelectorEngine from '../dom/selector-engine.js'
|
||||
|
||||
const enableDismissTrigger = (component, method = 'hide') => {
|
||||
const clickEvent = `click.dismiss${component.EVENT_KEY}`
|
||||
@@ -21,7 +22,7 @@ const enableDismissTrigger = (component, method = 'hide') => {
|
||||
return
|
||||
}
|
||||
|
||||
const target = getElementFromSelector(this) || this.closest(`.${name}`)
|
||||
const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`)
|
||||
const instance = component.getOrCreateInstance(target)
|
||||
|
||||
// Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { isElement, toType } from './index'
|
||||
import Manipulator from '../dom/manipulator'
|
||||
import { isElement, toType } from './index.js'
|
||||
import Manipulator from '../dom/manipulator.js'
|
||||
|
||||
/**
|
||||
* Class definition
|
||||
@@ -49,8 +49,7 @@ class Config {
|
||||
}
|
||||
|
||||
_typeCheckConfig(config, configTypes = this.constructor.DefaultType) {
|
||||
for (const property of Object.keys(configTypes)) {
|
||||
const expectedTypes = configTypes[property]
|
||||
for (const [property, expectedTypes] of Object.entries(configTypes)) {
|
||||
const value = config[property]
|
||||
const valueType = isElement(value) ? 'element' : toType(value)
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import EventHandler from '../dom/event-handler'
|
||||
import SelectorEngine from '../dom/selector-engine'
|
||||
import Config from './config'
|
||||
import EventHandler from '../dom/event-handler.js'
|
||||
import SelectorEngine from '../dom/selector-engine.js'
|
||||
import Config from './config.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
|
||||
+18
-48
@@ -9,6 +9,20 @@ const MAX_UID = 1_000_000
|
||||
const MILLISECONDS_MULTIPLIER = 1000
|
||||
const TRANSITION_END = 'transitionend'
|
||||
|
||||
/**
|
||||
* Properly escape IDs selectors to handle weird IDs
|
||||
* @param {string} selector
|
||||
* @returns {string}
|
||||
*/
|
||||
const parseSelector = selector => {
|
||||
if (selector && window.CSS && window.CSS.escape) {
|
||||
// document.querySelector needs escaping to handle IDs (html5+) containing for instance /
|
||||
selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`)
|
||||
}
|
||||
|
||||
return selector
|
||||
}
|
||||
|
||||
// Shout-out Angus Croll (https://goo.gl/pxwQGp)
|
||||
const toType = object => {
|
||||
if (object === null || object === undefined) {
|
||||
@@ -30,47 +44,6 @@ const getUID = prefix => {
|
||||
return prefix
|
||||
}
|
||||
|
||||
const getSelector = element => {
|
||||
let selector = element.getAttribute('data-bs-target')
|
||||
|
||||
if (!selector || selector === '#') {
|
||||
let hrefAttribute = element.getAttribute('href')
|
||||
|
||||
// The only valid content that could double as a selector are IDs or classes,
|
||||
// so everything starting with `#` or `.`. If a "real" URL is used as the selector,
|
||||
// `document.querySelector` will rightfully complain it is invalid.
|
||||
// See https://github.com/twbs/bootstrap/issues/32273
|
||||
if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Just in case some CMS puts out a full URL with the anchor appended
|
||||
if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {
|
||||
hrefAttribute = `#${hrefAttribute.split('#')[1]}`
|
||||
}
|
||||
|
||||
selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null
|
||||
}
|
||||
|
||||
return selector
|
||||
}
|
||||
|
||||
const getSelectorFromElement = element => {
|
||||
const selector = getSelector(element)
|
||||
|
||||
if (selector) {
|
||||
return document.querySelector(selector) ? selector : null
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const getElementFromSelector = element => {
|
||||
const selector = getSelector(element)
|
||||
|
||||
return selector ? document.querySelector(selector) : null
|
||||
}
|
||||
|
||||
const getTransitionDurationFromElement = element => {
|
||||
if (!element) {
|
||||
return 0
|
||||
@@ -117,7 +90,7 @@ const getElement = object => {
|
||||
}
|
||||
|
||||
if (typeof object === 'string' && object.length > 0) {
|
||||
return document.querySelector(object)
|
||||
return document.querySelector(parseSelector(object))
|
||||
}
|
||||
|
||||
return null
|
||||
@@ -249,10 +222,8 @@ const defineJQueryPlugin = plugin => {
|
||||
})
|
||||
}
|
||||
|
||||
const execute = callback => {
|
||||
if (typeof callback === 'function') {
|
||||
callback()
|
||||
}
|
||||
const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {
|
||||
return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue
|
||||
}
|
||||
|
||||
const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
|
||||
@@ -318,10 +289,8 @@ export {
|
||||
executeAfterTransition,
|
||||
findShadowRoot,
|
||||
getElement,
|
||||
getElementFromSelector,
|
||||
getjQuery,
|
||||
getNextActiveElement,
|
||||
getSelectorFromElement,
|
||||
getTransitionDurationFromElement,
|
||||
getUID,
|
||||
isDisabled,
|
||||
@@ -330,6 +299,7 @@ export {
|
||||
isVisible,
|
||||
noop,
|
||||
onDOMContentLoaded,
|
||||
parseSelector,
|
||||
reflow,
|
||||
triggerTransitionEnd,
|
||||
toType
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import SelectorEngine from '../dom/selector-engine'
|
||||
import Manipulator from '../dom/manipulator'
|
||||
import { isElement } from './index'
|
||||
import SelectorEngine from '../dom/selector-engine.js'
|
||||
import Manipulator from '../dom/manipulator.js'
|
||||
import { isElement } from './index.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import Config from './config'
|
||||
import EventHandler from '../dom/event-handler'
|
||||
import { execute } from './index'
|
||||
import Config from './config.js'
|
||||
import EventHandler from '../dom/event-handler.js'
|
||||
import { execute } from './index.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
* --------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
import { DefaultAllowlist, sanitizeHtml } from './sanitizer'
|
||||
import { getElement, isElement } from '../util/index'
|
||||
import SelectorEngine from '../dom/selector-engine'
|
||||
import Config from './config'
|
||||
import { DefaultAllowlist, sanitizeHtml } from './sanitizer.js'
|
||||
import { execute, getElement, isElement } from './index.js'
|
||||
import SelectorEngine from '../dom/selector-engine.js'
|
||||
import Config from './config.js'
|
||||
|
||||
/**
|
||||
* Constants
|
||||
@@ -143,7 +143,7 @@ class TemplateFactory extends Config {
|
||||
}
|
||||
|
||||
_resolvePossibleFunction(arg) {
|
||||
return typeof arg === 'function' ? arg(this) : arg
|
||||
return execute(arg, [this])
|
||||
}
|
||||
|
||||
_putElementInTemplate(element, templateElement) {
|
||||
|
||||
@@ -105,7 +105,7 @@ if (BROWSERSTACK) {
|
||||
config.browserStack = {
|
||||
username: ENV.BROWSER_STACK_USERNAME,
|
||||
accessKey: ENV.BROWSER_STACK_ACCESS_KEY,
|
||||
build: `bootstrap-${ENV.GITHUB_SHA ? ENV.GITHUB_SHA.slice(0, 7) + '-' : ''}${new Date().toISOString()}`,
|
||||
build: `bootstrap-${ENV.GITHUB_SHA ? `${ENV.GITHUB_SHA.slice(0, 7)}-` : ''}${new Date().toISOString()}`,
|
||||
project: 'Bootstrap',
|
||||
retryLimit: 2
|
||||
}
|
||||
|
||||
@@ -278,14 +278,14 @@ describe('Collapse', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="parentGroup" class="accordion">',
|
||||
' <div id="parentHeader" class="accordion-header">',
|
||||
' <button data-bs-target="#parentContent" data-bs-toggle="collapse" role="button" class="accordion-toggle">Parent</button>',
|
||||
' <button data-bs-target="#parentContent" data-bs-toggle="collapse" class="accordion-toggle">Parent</button>',
|
||||
' </div>',
|
||||
' <div id="parentContent" class="accordion-collapse collapse" aria-labelledby="parentHeader" data-bs-parent="#parentGroup">',
|
||||
' <div class="accordion-body">',
|
||||
' <div id="childGroup" class="accordion">',
|
||||
' <div class="accordion-item">',
|
||||
' <div id="childHeader1" class="accordion-header">',
|
||||
' <button data-bs-target="#childContent1" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 1</button>',
|
||||
' <button data-bs-target="#childContent1" data-bs-toggle="collapse" class="accordion-toggle">Child 1</button>',
|
||||
' </div>',
|
||||
' <div id="childContent1" class="accordion-collapse collapse" aria-labelledby="childHeader1" data-bs-parent="#childGroup">',
|
||||
' <div>content</div>',
|
||||
@@ -293,7 +293,7 @@ describe('Collapse', () => {
|
||||
' </div>',
|
||||
' <div class="accordion-item">',
|
||||
' <div id="childHeader2" class="accordion-header">',
|
||||
' <button data-bs-target="#childContent2" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 2</button>',
|
||||
' <button data-bs-target="#childContent2" data-bs-toggle="collapse" class="accordion-toggle">Child 2</button>',
|
||||
' </div>',
|
||||
' <div id="childContent2" class="accordion-collapse collapse" aria-labelledby="childHeader2" data-bs-parent="#childGroup">',
|
||||
' <div>content</div>',
|
||||
@@ -887,17 +887,17 @@ describe('Collapse', () => {
|
||||
return new Promise(resolve => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="trigger1" role="button" data-bs-toggle="collapse" href="#test1"></a>',
|
||||
'<a id="trigger2" role="button" data-bs-toggle="collapse" href="#test2"></a>',
|
||||
'<a id="trigger2" role="button" data-bs-toggle="collapse" href="#0/my/id"></a>',
|
||||
'<a id="trigger3" role="button" data-bs-toggle="collapse" href=".multi"></a>',
|
||||
'<div id="test1" class="multi"></div>',
|
||||
'<div id="test2" class="multi"></div>'
|
||||
'<div id="0/my/id" class="multi"></div>'
|
||||
].join('')
|
||||
|
||||
const trigger1 = fixtureEl.querySelector('#trigger1')
|
||||
const trigger2 = fixtureEl.querySelector('#trigger2')
|
||||
const trigger3 = fixtureEl.querySelector('#trigger3')
|
||||
const target1 = fixtureEl.querySelector('#test1')
|
||||
const target2 = fixtureEl.querySelector('#test2')
|
||||
const target2 = fixtureEl.querySelector(`#${CSS.escape('0/my/id')}`)
|
||||
|
||||
const target2Shown = () => {
|
||||
expect(trigger1).not.toHaveClass('collapsed')
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import SelectorEngine from '../../../src/dom/selector-engine'
|
||||
import { getFixture, clearFixture } from '../../helpers/fixture'
|
||||
import { clearFixture, getFixture } from '../../helpers/fixture'
|
||||
|
||||
describe('SelectorEngine', () => {
|
||||
let fixtureEl
|
||||
@@ -232,5 +232,159 @@ describe('SelectorEngine', () => {
|
||||
expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getSelectorFromElement', () => {
|
||||
it('should get selector from data-bs-target', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="test" data-bs-target=".target"></div>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
|
||||
})
|
||||
|
||||
it('should get selector from href if no data-bs-target set', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" href=".target"></a>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
|
||||
})
|
||||
|
||||
it('should get selector from href if data-bs-target equal to #', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" data-bs-target="#" href=".target"></a>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
|
||||
})
|
||||
|
||||
it('should return null if a selector from a href is a url without an anchor', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" data-bs-target="#" href="foo/bar.html"></a>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
|
||||
})
|
||||
|
||||
it('should return the anchor if a selector from a href is a url', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" data-bs-target="#" href="foo/bar.html#target"></a>',
|
||||
'<div id="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('#target')
|
||||
})
|
||||
|
||||
it('should return null if selector not found', () => {
|
||||
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
|
||||
})
|
||||
|
||||
it('should return null if no selector', () => {
|
||||
fixtureEl.innerHTML = '<div></div>'
|
||||
|
||||
const testEl = fixtureEl.querySelector('div')
|
||||
|
||||
expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getElementFromSelector', () => {
|
||||
it('should get element from data-bs-target', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="test" data-bs-target=".target"></div>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
|
||||
})
|
||||
|
||||
it('should get element from href if no data-bs-target set', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" href=".target"></a>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
|
||||
})
|
||||
|
||||
it('should return null if element not found', () => {
|
||||
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getElementFromSelector(testEl)).toBeNull()
|
||||
})
|
||||
|
||||
it('should return null if no selector', () => {
|
||||
fixtureEl.innerHTML = '<div></div>'
|
||||
|
||||
const testEl = fixtureEl.querySelector('div')
|
||||
|
||||
expect(SelectorEngine.getElementFromSelector(testEl)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getMultipleElementsFromSelector', () => {
|
||||
it('should get elements from data-bs-target', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="test" data-bs-target=".target"></div>',
|
||||
'<div class="target"></div>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
|
||||
})
|
||||
|
||||
it('should get elements in array, from href if no data-bs-target set', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" href=".target"></a>',
|
||||
'<div class="target"></div>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
|
||||
})
|
||||
|
||||
it('should return empty array if elements not found', () => {
|
||||
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toHaveSize(0)
|
||||
})
|
||||
|
||||
it('should return empty array if no selector', () => {
|
||||
fixtureEl.innerHTML = '<div></div>'
|
||||
|
||||
const testEl = fixtureEl.querySelector('div')
|
||||
|
||||
expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toHaveSize(0)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -177,6 +177,43 @@ describe('Tab', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('should work with tab id being an int', done => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div class="card-header d-block d-inline-block">',
|
||||
' <ul class="nav nav-tabs card-header-tabs" id="page_tabs">',
|
||||
' <li class="nav-item">',
|
||||
' <a class="nav-link" draggable="false" data-toggle="tab" href="#tab1">',
|
||||
' Working Tab 1 (#tab1)',
|
||||
' </a>',
|
||||
' </li>',
|
||||
' <li class="nav-item">',
|
||||
' <a id="trigger2" class="nav-link" draggable="false" data-toggle="tab" href="#2">',
|
||||
' Tab with numeric ID should work (#2)',
|
||||
' </a>',
|
||||
' </li>',
|
||||
' </ul>',
|
||||
'</div>',
|
||||
'<div class="card-body">',
|
||||
' <div class="tab-content" id="page_content">',
|
||||
' <div class="tab-pane fade" id="tab1">',
|
||||
' Working Tab 1 (#tab1) Content Here',
|
||||
' </div>',
|
||||
' <div class="tab-pane fade" id="2">',
|
||||
' Working Tab 2 (#2) with numeric ID',
|
||||
' </div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
const profileTriggerEl = fixtureEl.querySelector('#trigger2')
|
||||
const tab = new Tab(profileTriggerEl)
|
||||
|
||||
profileTriggerEl.addEventListener('shown.bs.tab', () => {
|
||||
expect(fixtureEl.querySelector(`#${CSS.escape('2')}`)).toHaveClass('active')
|
||||
done()
|
||||
})
|
||||
|
||||
tab.show()
|
||||
})
|
||||
|
||||
it('should not fire shown when show is prevented', () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
|
||||
@@ -603,19 +640,19 @@ describe('Tab', () => {
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const tabEl = fixtureEl.querySelector('#tab1')
|
||||
const tabEl1 = fixtureEl.querySelector('#tab1')
|
||||
const tabEl2 = fixtureEl.querySelector('#tab2')
|
||||
const tabEl3 = fixtureEl.querySelector('#tab3')
|
||||
const tabEl4 = fixtureEl.querySelector('#tab4')
|
||||
const tab = new Tab(tabEl)
|
||||
const tab1 = new Tab(tabEl1)
|
||||
const tab2 = new Tab(tabEl2)
|
||||
const tab3 = new Tab(tabEl3)
|
||||
const tab4 = new Tab(tabEl4)
|
||||
const spy1 = spyOn(tab, 'show').and.callThrough()
|
||||
const spy1 = spyOn(tab1, 'show').and.callThrough()
|
||||
const spy2 = spyOn(tab2, 'show').and.callThrough()
|
||||
const spy3 = spyOn(tab3, 'show').and.callThrough()
|
||||
const spy4 = spyOn(tab4, 'show').and.callThrough()
|
||||
const spyFocus1 = spyOn(tabEl, 'focus').and.callThrough()
|
||||
const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
|
||||
const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
|
||||
const spyFocus3 = spyOn(tabEl3, 'focus').and.callThrough()
|
||||
const spyFocus4 = spyOn(tabEl4, 'focus').and.callThrough()
|
||||
@@ -623,7 +660,7 @@ describe('Tab', () => {
|
||||
const keydown = createEvent('keydown')
|
||||
keydown.key = 'ArrowRight'
|
||||
|
||||
tabEl.dispatchEvent(keydown)
|
||||
tabEl1.dispatchEvent(keydown)
|
||||
expect(spy1).not.toHaveBeenCalled()
|
||||
expect(spy2).not.toHaveBeenCalled()
|
||||
expect(spy3).not.toHaveBeenCalled()
|
||||
@@ -644,19 +681,19 @@ describe('Tab', () => {
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const tabEl = fixtureEl.querySelector('#tab1')
|
||||
const tabEl1 = fixtureEl.querySelector('#tab1')
|
||||
const tabEl2 = fixtureEl.querySelector('#tab2')
|
||||
const tabEl3 = fixtureEl.querySelector('#tab3')
|
||||
const tabEl4 = fixtureEl.querySelector('#tab4')
|
||||
const tab = new Tab(tabEl)
|
||||
const tab1 = new Tab(tabEl1)
|
||||
const tab2 = new Tab(tabEl2)
|
||||
const tab3 = new Tab(tabEl3)
|
||||
const tab4 = new Tab(tabEl4)
|
||||
const spy1 = spyOn(tab, 'show').and.callThrough()
|
||||
const spy1 = spyOn(tab1, 'show').and.callThrough()
|
||||
const spy2 = spyOn(tab2, 'show').and.callThrough()
|
||||
const spy3 = spyOn(tab3, 'show').and.callThrough()
|
||||
const spy4 = spyOn(tab4, 'show').and.callThrough()
|
||||
const spyFocus1 = spyOn(tabEl, 'focus').and.callThrough()
|
||||
const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
|
||||
const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
|
||||
const spyFocus3 = spyOn(tabEl3, 'focus').and.callThrough()
|
||||
const spyFocus4 = spyOn(tabEl4, 'focus').and.callThrough()
|
||||
|
||||
@@ -128,7 +128,7 @@ describe('Config', () => {
|
||||
const obj = new DummyConfigClass()
|
||||
expect(() => {
|
||||
obj._typeCheckConfig(config)
|
||||
}).toThrowError(TypeError, obj.constructor.NAME.toUpperCase() + ': Option "parent" provided type "number" but expected type "(string|element)".')
|
||||
}).toThrowError(TypeError, `${obj.constructor.NAME.toUpperCase()}: Option "parent" provided type "number" but expected type "(string|element)".`)
|
||||
})
|
||||
|
||||
it('should return null stringified when null is passed', () => {
|
||||
|
||||
@@ -22,119 +22,6 @@ describe('Util', () => {
|
||||
})
|
||||
})
|
||||
|
||||
describe('getSelectorFromElement', () => {
|
||||
it('should get selector from data-bs-target', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="test" data-bs-target=".target"></div>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
|
||||
})
|
||||
|
||||
it('should get selector from href if no data-bs-target set', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" href=".target"></a>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
|
||||
})
|
||||
|
||||
it('should get selector from href if data-bs-target equal to #', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" data-bs-target="#" href=".target"></a>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
|
||||
})
|
||||
|
||||
it('should return null if a selector from a href is a url without an anchor', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" data-bs-target="#" href="foo/bar.html"></a>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(Util.getSelectorFromElement(testEl)).toBeNull()
|
||||
})
|
||||
|
||||
it('should return the anchor if a selector from a href is a url', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" data-bs-target="#" href="foo/bar.html#target"></a>',
|
||||
'<div id="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(Util.getSelectorFromElement(testEl)).toEqual('#target')
|
||||
})
|
||||
|
||||
it('should return null if selector not found', () => {
|
||||
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(Util.getSelectorFromElement(testEl)).toBeNull()
|
||||
})
|
||||
|
||||
it('should return null if no selector', () => {
|
||||
fixtureEl.innerHTML = '<div></div>'
|
||||
|
||||
const testEl = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Util.getSelectorFromElement(testEl)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getElementFromSelector', () => {
|
||||
it('should get element from data-bs-target', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<div id="test" data-bs-target=".target"></div>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(Util.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
|
||||
})
|
||||
|
||||
it('should get element from href if no data-bs-target set', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<a id="test" href=".target"></a>',
|
||||
'<div class="target"></div>'
|
||||
].join('')
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(Util.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
|
||||
})
|
||||
|
||||
it('should return null if element not found', () => {
|
||||
fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
|
||||
|
||||
const testEl = fixtureEl.querySelector('#test')
|
||||
|
||||
expect(Util.getElementFromSelector(testEl)).toBeNull()
|
||||
})
|
||||
|
||||
it('should return null if no selector', () => {
|
||||
fixtureEl.innerHTML = '<div></div>'
|
||||
|
||||
const testEl = fixtureEl.querySelector('div')
|
||||
|
||||
expect(Util.getElementFromSelector(testEl)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getTransitionDurationFromElement', () => {
|
||||
it('should get transition from element', () => {
|
||||
fixtureEl.innerHTML = '<div style="transition: all 300ms ease-out;"></div>'
|
||||
@@ -631,6 +518,25 @@ describe('Util', () => {
|
||||
Util.execute(spy)
|
||||
expect(spy).toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('should execute if arg is function & return the result', () => {
|
||||
const functionFoo = (num1, num2 = 10) => num1 + num2
|
||||
const resultFoo = Util.execute(functionFoo, [4, 5])
|
||||
expect(resultFoo).toBe(9)
|
||||
|
||||
const resultFoo1 = Util.execute(functionFoo, [4])
|
||||
expect(resultFoo1).toBe(14)
|
||||
|
||||
const functionBar = () => 'foo'
|
||||
const resultBar = Util.execute(functionBar)
|
||||
expect(resultBar).toBe('foo')
|
||||
})
|
||||
|
||||
it('should not execute if arg is not function & return default argument', () => {
|
||||
const foo = 'bar'
|
||||
expect(Util.execute(foo)).toBe('bar')
|
||||
expect(Util.execute(foo, [], 4)).toBe(4)
|
||||
})
|
||||
})
|
||||
|
||||
describe('executeAfterTransition', () => {
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
</button>
|
||||
|
||||
<p>For checkboxes and radio buttons, ensure that keyboard behavior is functioning correctly.</p>
|
||||
<p>Navigate to the checkboxes with the keyboard (generally, using <kbd>TAB</kbd> / <kbd>SHIFT + TAB</kbd>), and ensure that <kbd>SPACE</kbd> toggles the currently focused checkbox. Click on one of the checkboxes using the mouse, ensure that focus was correctly set on the actual checkbox, and that <kbd>SPACE</kbd> toggles the checkbox again.</p>
|
||||
<p>Navigate to the checkboxes with the keyboard (generally, using <kbd>Tab</kbd> / <kbd><kbd>Shift</kbd> + <kbd>Tab</kbd></kbd>), and ensure that <kbd>Space</kbd> toggles the currently focused checkbox. Click on one of the checkboxes using the mouse, ensure that focus was correctly set on the actual checkbox, and that <kbd>Space</kbd> toggles the checkbox again.</p>
|
||||
|
||||
<div class="btn-group" data-bs-toggle="buttons">
|
||||
<label class="btn btn-primary active">
|
||||
@@ -29,7 +29,7 @@
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<p>Navigate to the radio button group with the keyboard (generally, using <kbd>TAB</kbd> / <kbd>SHIFT + TAB</kbd>). If no radio button was initially set to be selected, the first/last radio button should receive focus (depending on whether you navigated "forward" to the group with <kbd>TAB</kbd> or "backwards" using <kbd>SHIFT + TAB</kbd>). If a radio button was already selected, navigating with the keyboard should set focus to that particular radio button. Only one radio button in a group should receive focus at any given time. Ensure that the selected radio button can be changed by using the <kbd>←</kbd> and <kbd>→</kbd> arrow keys. Click on one of the radio buttons with the mouse, ensure that focus was correctly set on the actual radio button, and that <kbd>←</kbd> and <kbd>→</kbd> change the selected radio button again.</p>
|
||||
<p>Navigate to the radio button group with the keyboard (generally, using <kbd>Tab</kbd> / <kbd><kbd>Shift</kbd> + <kbd>Tab</kbd></kbd>). If no radio button was initially set to be selected, the first/last radio button should receive focus (depending on whether you navigated "forward" to the group with <kbd>Tab</kbd> or "backwards" using <kbd><kbd>Shift</kbd> + <kbd>Tab</kbd></kbd>). If a radio button was already selected, navigating with the keyboard should set focus to that particular radio button. Only one radio button in a group should receive focus at any given time. Ensure that the selected radio button can be changed by using the <kbd>←</kbd> and <kbd>→</kbd> arrow keys. Click on one of the radio buttons with the mouse, ensure that focus was correctly set on the actual radio button, and that <kbd>←</kbd> and <kbd>→</kbd> change the selected radio button again.</p>
|
||||
|
||||
<div class="btn-group" data-bs-toggle="buttons">
|
||||
<label class="btn btn-primary active">
|
||||
|
||||
@@ -55,7 +55,7 @@
|
||||
// Test to show that transition-duration can be changed with css
|
||||
carousel.addEventListener('slid.bs.carousel', event => {
|
||||
t1 = performance.now()
|
||||
console.log('transition-duration took ' + (t1 - t0) + 'ms, slid at ' + event.timeStamp)
|
||||
console.log(`transition-duration took ${t1 - t0}ms, slid at ${event.timeStamp}`)
|
||||
})
|
||||
carousel.addEventListener('slide.bs.carousel', () => {
|
||||
t0 = performance.now()
|
||||
|
||||
@@ -264,7 +264,7 @@
|
||||
|
||||
slowModal.addEventListener('shown.bs.modal', () => {
|
||||
t1 = performance.now()
|
||||
console.log('transition-duration took ' + (t1 - t0) + 'ms.')
|
||||
console.log(`transition-duration took ${t1 - t0}ms.`)
|
||||
})
|
||||
|
||||
slowModal.addEventListener('show.bs.modal', () => {
|
||||
|
||||
Reference in New Issue
Block a user