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:
authorPierre Souchay <pierresouchay@users.noreply.github.com>2022-11-07 15:43:06 +0300
committerGitHub <noreply@github.com>2022-11-07 15:43:06 +0300
commitef4e2daa48193463b36fdc297d79c6a002e4ee67 (patch)
treedb68bf3bc32bfdfce97f1c3824c29c86e24b8f27
parente81e7cda90026cdb2a05fcdadd2d66f48f0bbdc4 (diff)
Properly escape IDs in getSelector() to handle weird IDs (#35565) (#35566)
-rw-r--r--.bundlewatch.config.json2
-rw-r--r--js/src/dom/selector-engine.js3
-rw-r--r--js/src/util/index.js17
-rw-r--r--js/tests/unit/collapse.spec.js6
-rw-r--r--js/tests/unit/tab.spec.js37
5 files changed, 59 insertions, 6 deletions
diff --git a/.bundlewatch.config.json b/.bundlewatch.config.json
index 105c8c1580..e61a2acd4e 100644
--- a/.bundlewatch.config.json
+++ b/.bundlewatch.config.json
@@ -38,7 +38,7 @@
},
{
"path": "./dist/js/bootstrap.bundle.min.js",
- "maxSize": "22.75 kB"
+ "maxSize": "23.0 kB"
},
{
"path": "./dist/js/bootstrap.esm.js",
diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js
index ad10a60831..248dab4944 100644
--- a/js/src/dom/selector-engine.js
+++ b/js/src/dom/selector-engine.js
@@ -5,7 +5,7 @@
* --------------------------------------------------------------------------
*/
-import { isDisabled, isVisible } from '../util/index.js'
+import { isDisabled, isVisible, parseSelector } from '../util/index.js'
/**
* Constants
@@ -99,6 +99,7 @@ const SelectorEngine = {
}
selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null
+ selector = parseSelector(selector)
}
return selector
diff --git a/js/src/util/index.js b/js/src/util/index.js
index b92eddba25..8c69221736 100644
--- a/js/src/util/index.js
+++ b/js/src/util/index.js
@@ -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.replaceAll(/#([^\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) {
@@ -76,7 +90,7 @@ const getElement = object => {
}
if (typeof object === 'string' && object.length > 0) {
- return document.querySelector(object)
+ return document.querySelector(parseSelector(object))
}
return null
@@ -285,6 +299,7 @@ export {
isVisible,
noop,
onDOMContentLoaded,
+ parseSelector,
reflow,
triggerTransitionEnd,
toType
diff --git a/js/tests/unit/collapse.spec.js b/js/tests/unit/collapse.spec.js
index 9c86719881..834d1b98e4 100644
--- a/js/tests/unit/collapse.spec.js
+++ b/js/tests/unit/collapse.spec.js
@@ -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')
diff --git a/js/tests/unit/tab.spec.js b/js/tests/unit/tab.spec.js
index 896e611801..66238efbb8 100644
--- a/js/tests/unit/tab.spec.js
+++ b/js/tests/unit/tab.spec.js
@@ -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>'