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
diff options
context:
space:
mode:
authorGeoSot <geo.sotis@gmail.com>2022-03-16 12:58:58 +0300
committerGeoSot <geo.sotis@gmail.com>2022-10-08 00:20:25 +0300
commit8847c8156e324412280d54a5d86af466f1cc554a (patch)
treef89468a6153a074668b9c612d1cef338aafe75ba
parent4cb046a6b8b37a0f328fa5b86fbd573ca3f0dc33 (diff)
refactor: Move `getElementFromSelector` & `getSelectorFromElement`gs/moving-selector-helpers
Move `getElementFromSelector` & getSelectorFromElement` inside selector-engine.js, in order to use SelectorEngine methods, avoiding raw querySelector usage Feature: add `getMultipleElementsFromSelector` helper
-rw-r--r--js/src/carousel.js3
-rw-r--r--js/src/collapse.js16
-rw-r--r--js/src/dom/selector-engine.js54
-rw-r--r--js/src/dropdown.js2
-rw-r--r--js/src/modal.js4
-rw-r--r--js/src/offcanvas.js3
-rw-r--r--js/src/scrollspy.js2
-rw-r--r--js/src/tab.js4
-rw-r--r--js/src/util/component-functions.js3
-rw-r--r--js/src/util/focustrap.js2
-rw-r--r--js/src/util/index.js43
-rw-r--r--js/src/util/scrollbar.js2
-rw-r--r--js/src/util/template-factory.js2
-rw-r--r--js/tests/unit/dom/selector-engine.spec.js165
-rw-r--r--js/tests/unit/util/focustrap.spec.js2
-rw-r--r--js/tests/unit/util/index.spec.js113
16 files changed, 235 insertions, 185 deletions
diff --git a/js/src/carousel.js b/js/src/carousel.js
index 7e89e1614c..49527200d5 100644
--- a/js/src/carousel.js
+++ b/js/src/carousel.js
@@ -7,7 +7,6 @@
import {
defineJQueryPlugin,
- getElementFromSelector,
getNextActiveElement,
isRTL,
isVisible,
@@ -16,7 +15,7 @@ import {
} from './util/index'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
-import SelectorEngine from './dom/selector-engine'
+import { getElementFromSelector, SelectorEngine } from './dom/selector-engine'
import Swipe from './util/swipe'
import BaseComponent from './base-component'
diff --git a/js/src/collapse.js b/js/src/collapse.js
index df2bbc6473..4999257f8f 100644
--- a/js/src/collapse.js
+++ b/js/src/collapse.js
@@ -5,15 +5,14 @@
* --------------------------------------------------------------------------
*/
+import { defineJQueryPlugin, getElement, reflow } from './util/index'
+import EventHandler from './dom/event-handler'
import {
- defineJQueryPlugin,
- getElement,
getElementFromSelector,
+ getMultipleElementsFromSelector,
getSelectorFromElement,
- reflow
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import SelectorEngine from './dom/selector-engine'
+ SelectorEngine
+} from './dom/selector-engine'
import BaseComponent from './base-component'
/**
@@ -285,10 +284,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 getMultipleElementsFromSelector(this)) {
Collapse.getOrCreateInstance(element, { toggle: false }).toggle()
}
})
diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js
index 28f23ff8ff..69eb79aa21 100644
--- a/js/src/dom/selector-engine.js
+++ b/js/src/dom/selector-engine.js
@@ -80,4 +80,56 @@ const SelectorEngine = {
}
}
-export default SelectorEngine
+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 SelectorEngine.findOne(selector) ? selector : null
+ }
+
+ return null
+}
+
+const getElementFromSelector = element => {
+ const selector = getSelector(element)
+
+ return selector ? SelectorEngine.findOne(selector) : null
+}
+
+const getMultipleElementsFromSelector = element => {
+ const selector = getSelector(element)
+
+ return selector ? SelectorEngine.find(selector) : []
+}
+
+export {
+ SelectorEngine,
+ getElementFromSelector,
+ getMultipleElementsFromSelector,
+ getSelectorFromElement
+}
diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index d37886d898..1f4f2c2bda 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -19,7 +19,7 @@ import {
} from './util/index'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
-import SelectorEngine from './dom/selector-engine'
+import { SelectorEngine } from './dom/selector-engine'
import BaseComponent from './base-component'
/**
diff --git a/js/src/modal.js b/js/src/modal.js
index c2c5c19e96..22ae926b29 100644
--- a/js/src/modal.js
+++ b/js/src/modal.js
@@ -5,9 +5,9 @@
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin, getElementFromSelector, isRTL, isVisible, reflow } from './util/index'
+import { defineJQueryPlugin, isRTL, isVisible, reflow } from './util/index'
import EventHandler from './dom/event-handler'
-import SelectorEngine from './dom/selector-engine'
+import { getElementFromSelector, SelectorEngine } from './dom/selector-engine'
import ScrollBarHelper from './util/scrollbar'
import BaseComponent from './base-component'
import Backdrop from './util/backdrop'
diff --git a/js/src/offcanvas.js b/js/src/offcanvas.js
index dc3e910752..436f441c91 100644
--- a/js/src/offcanvas.js
+++ b/js/src/offcanvas.js
@@ -7,14 +7,13 @@
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 { getElementFromSelector, SelectorEngine } from './dom/selector-engine'
import Backdrop from './util/backdrop'
import FocusTrap from './util/focustrap'
import { enableDismissTrigger } from './util/component-functions'
diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js
index a73bba840b..ae70ae8bc4 100644
--- a/js/src/scrollspy.js
+++ b/js/src/scrollspy.js
@@ -7,7 +7,7 @@
import { defineJQueryPlugin, getElement, isDisabled, isVisible } from './util/index'
import EventHandler from './dom/event-handler'
-import SelectorEngine from './dom/selector-engine'
+import { SelectorEngine } from './dom/selector-engine'
import BaseComponent from './base-component'
/**
diff --git a/js/src/tab.js b/js/src/tab.js
index a78c27538a..6d53016f07 100644
--- a/js/src/tab.js
+++ b/js/src/tab.js
@@ -5,9 +5,9 @@
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin, getElementFromSelector, getNextActiveElement, isDisabled } from './util/index'
+import { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index'
import EventHandler from './dom/event-handler'
-import SelectorEngine from './dom/selector-engine'
+import { getElementFromSelector, SelectorEngine } from './dom/selector-engine'
import BaseComponent from './base-component'
/**
diff --git a/js/src/util/component-functions.js b/js/src/util/component-functions.js
index 798366b079..796b1bfa6c 100644
--- a/js/src/util/component-functions.js
+++ b/js/src/util/component-functions.js
@@ -6,7 +6,8 @@
*/
import EventHandler from '../dom/event-handler'
-import { getElementFromSelector, isDisabled } from './index'
+import { isDisabled } from './index'
+import { getElementFromSelector } from '../dom/selector-engine'
const enableDismissTrigger = (component, method = 'hide') => {
const clickEvent = `click.dismiss${component.EVENT_KEY}`
diff --git a/js/src/util/focustrap.js b/js/src/util/focustrap.js
index 01ac766837..5924639d22 100644
--- a/js/src/util/focustrap.js
+++ b/js/src/util/focustrap.js
@@ -6,7 +6,7 @@
*/
import EventHandler from '../dom/event-handler'
-import SelectorEngine from '../dom/selector-engine'
+import { SelectorEngine } from '../dom/selector-engine'
import Config from './config'
/**
diff --git a/js/src/util/index.js b/js/src/util/index.js
index ad99f85ed9..b92eddba25 100644
--- a/js/src/util/index.js
+++ b/js/src/util/index.js
@@ -30,47 +30,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
@@ -316,10 +275,8 @@ export {
executeAfterTransition,
findShadowRoot,
getElement,
- getElementFromSelector,
getjQuery,
getNextActiveElement,
- getSelectorFromElement,
getTransitionDurationFromElement,
getUID,
isDisabled,
diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js
index 421426d41a..d531b769d6 100644
--- a/js/src/util/scrollbar.js
+++ b/js/src/util/scrollbar.js
@@ -5,7 +5,7 @@
* --------------------------------------------------------------------------
*/
-import SelectorEngine from '../dom/selector-engine'
+import { SelectorEngine } from '../dom/selector-engine'
import Manipulator from '../dom/manipulator'
import { isElement } from './index'
diff --git a/js/src/util/template-factory.js b/js/src/util/template-factory.js
index 16ec6c28d2..1de03473cf 100644
--- a/js/src/util/template-factory.js
+++ b/js/src/util/template-factory.js
@@ -7,7 +7,7 @@
import { DefaultAllowlist, sanitizeHtml } from './sanitizer'
import { execute, getElement, isElement } from '../util/index'
-import SelectorEngine from '../dom/selector-engine'
+import { SelectorEngine } from '../dom/selector-engine'
import Config from './config'
/**
diff --git a/js/tests/unit/dom/selector-engine.spec.js b/js/tests/unit/dom/selector-engine.spec.js
index 0245896c68..eb485c1356 100644
--- a/js/tests/unit/dom/selector-engine.spec.js
+++ b/js/tests/unit/dom/selector-engine.spec.js
@@ -1,5 +1,10 @@
-import SelectorEngine from '../../../src/dom/selector-engine'
-import { getFixture, clearFixture } from '../../helpers/fixture'
+import {
+ getElementFromSelector,
+ getMultipleElementsFromSelector,
+ getSelectorFromElement,
+ SelectorEngine
+} from '../../../src/dom/selector-engine'
+import { clearFixture, getFixture } from '../../helpers/fixture'
describe('SelectorEngine', () => {
let fixtureEl
@@ -232,5 +237,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(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(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(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(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(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(getSelectorFromElement(testEl)).toBeNull()
+ })
+
+ it('should return null if no selector', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const testEl = fixtureEl.querySelector('div')
+
+ expect(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(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(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(getElementFromSelector(testEl)).toBeNull()
+ })
+
+ it('should return null if no selector', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const testEl = fixtureEl.querySelector('div')
+
+ expect(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(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(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(getMultipleElementsFromSelector(testEl)).toHaveSize(0)
+ })
+
+ it('should return empty array if no selector', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const testEl = fixtureEl.querySelector('div')
+
+ expect(getMultipleElementsFromSelector(testEl)).toHaveSize(0)
+ })
+ })
+})
diff --git a/js/tests/unit/util/focustrap.spec.js b/js/tests/unit/util/focustrap.spec.js
index bedd124c9e..6178f7a5c3 100644
--- a/js/tests/unit/util/focustrap.spec.js
+++ b/js/tests/unit/util/focustrap.spec.js
@@ -1,6 +1,6 @@
import FocusTrap from '../../../src/util/focustrap'
import EventHandler from '../../../src/dom/event-handler'
-import SelectorEngine from '../../../src/dom/selector-engine'
+import { SelectorEngine } from '../../../src/dom/selector-engine'
import { clearFixture, createEvent, getFixture } from '../../helpers/fixture'
describe('FocusTrap', () => {
diff --git a/js/tests/unit/util/index.spec.js b/js/tests/unit/util/index.spec.js
index 6edc494335..202c72061d 100644
--- a/js/tests/unit/util/index.spec.js
+++ b/js/tests/unit/util/index.spec.js
@@ -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>'