diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 9b9b2edadc52..75841c222ea0 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -19,7 +19,7 @@ and [submitting pull requests](#pull-requests), but please respect the following
restrictions:
* Please **do not** use the issue tracker for personal support requests. Stack
- Overflow ([`bootstrap-4`](https://stackoverflow.com/questions/tagged/bootstrap-4) tag),
+ Overflow ([`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5) tag),
[Slack](https://bootstrap-slack.herokuapp.com/) or [IRC](README.md#community) are better places to get help.
* Please **do not** derail or troll issues. Keep the discussion on topic and
diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md
index de3c4b552e9a..c30bed3b4511 100644
--- a/.github/SUPPORT.md
+++ b/.github/SUPPORT.md
@@ -8,4 +8,4 @@ For general troubleshooting or help getting started:
- Join [the official Slack room](https://bootstrap-slack.herokuapp.com/).
- Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel.
-- Ask and explore Stack Overflow with the [`bootstrap-4`](https://stackoverflow.com/questions/tagged/bootstrap-4) tag.
+- Ask and explore Stack Overflow with the [`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5) tag.
diff --git a/README.md b/README.md
index ddbc6a1c3523..c96d3f04f29f 100644
--- a/README.md
+++ b/README.md
@@ -49,7 +49,7 @@ Several quick start options are available:
- [Download the latest release.](https://github.com/twbs/bootstrap/archive/v5.0.0.zip)
- Clone the repo: `git clone https://github.com/twbs/bootstrap.git`
- Install with [npm](https://www.npmjs.com/): `npm install bootstrap@next`
-- Install with [yarn](https://yarnpkg.com/): `yarn add bootstrap@5.0.0`
+- Install with [yarn](https://yarnpkg.com/): `yarn add bootstrap@next`
- Install with [Composer](https://getcomposer.org/): `composer require twbs/bootstrap:5.0.0`
- Install with [NuGet](https://www.nuget.org/): CSS: `Install-Package bootstrap` Sass: `Install-Package bootstrap.sass`
@@ -161,7 +161,7 @@ Get updates on Bootstrap's development and chat with the project maintainers and
- Read and subscribe to [The Official Bootstrap Blog](https://blog.getbootstrap.com/).
- Join [the official Slack room](https://bootstrap-slack.herokuapp.com/).
- Chat with fellow Bootstrappers in IRC. On the `irc.freenode.net` server, in the `##bootstrap` channel.
-- Implementation help may be found at Stack Overflow (tagged [`bootstrap-4`](https://stackoverflow.com/questions/tagged/bootstrap-4)).
+- Implementation help may be found at Stack Overflow (tagged [`bootstrap-5`](https://stackoverflow.com/questions/tagged/bootstrap-5)).
- Developers should use the keyword `bootstrap` on packages which modify or add to the functionality of Bootstrap when distributing through [npm](https://www.npmjs.com/browse/keyword/bootstrap) or similar delivery mechanisms for maximum discoverability.
diff --git a/build/change-version.js b/build/change-version.js
index ba6e46a47184..b8a640fa8ee3 100644
--- a/build/change-version.js
+++ b/build/change-version.js
@@ -95,7 +95,7 @@ function main(args) {
'vendor'
])
const INCLUDED_EXTENSIONS = new Set([
- // This extension whitelist is how we avoid modifying binary files
+ // This extension allowlist is how we avoid modifying binary files
'',
'.css',
'.html',
diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js
index b76677858885..5fea03018288 100644
--- a/js/src/dom/event-handler.js
+++ b/js/src/dom/event-handler.js
@@ -94,6 +94,7 @@ function getEvent(element) {
function bootstrapHandler(element, fn) {
return function handler(event) {
+ event.delegateTarget = element
if (handler.oneOff) {
EventHandler.off(element, event.type, fn)
}
@@ -109,6 +110,7 @@ function bootstrapDelegationHandler(element, selector, fn) {
for (let { target } = event; target && target !== this; target = target.parentNode) {
for (let i = domElements.length; i--;) {
if (domElements[i] === target) {
+ event.delegateTarget = target
if (handler.oneOff) {
EventHandler.off(element, event.type, fn)
}
diff --git a/js/src/tooltip.js b/js/src/tooltip.js
index d76679a6a09b..33c0f6eec224 100644
--- a/js/src/tooltip.js
+++ b/js/src/tooltip.js
@@ -17,7 +17,7 @@ import {
typeCheckConfig
} from './util/index'
import {
- DefaultWhitelist,
+ DefaultAllowlist,
sanitizeHtml
} from './util/sanitizer'
import Data from './dom/data'
@@ -38,7 +38,7 @@ const DATA_KEY = 'bs.tooltip'
const EVENT_KEY = `.${DATA_KEY}`
const CLASS_PREFIX = 'bs-tooltip'
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
-const DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']
+const DISALLOWED_ATTRIBUTES = ['sanitize', 'allowList', 'sanitizeFn']
const DefaultType = {
animation: 'boolean',
@@ -55,7 +55,7 @@ const DefaultType = {
boundary: '(string|element)',
sanitize: 'boolean',
sanitizeFn: '(null|function)',
- whiteList: 'object',
+ allowList: 'object',
popperConfig: '(null|object)'
}
@@ -84,7 +84,7 @@ const Default = {
boundary: 'scrollParent',
sanitize: true,
sanitizeFn: null,
- whiteList: DefaultWhitelist,
+ allowList: DefaultAllowlist,
popperConfig: null
}
@@ -194,14 +194,14 @@ class Tooltip {
if (event) {
const dataKey = this.constructor.DATA_KEY
- let context = Data.getData(event.target, dataKey)
+ let context = Data.getData(event.delegateTarget, dataKey)
if (!context) {
context = new this.constructor(
- event.target,
+ event.delegateTarget,
this._getDelegateConfig()
)
- Data.setData(event.target, dataKey, context)
+ Data.setData(event.delegateTarget, dataKey, context)
}
context._activeTrigger.click = !context._activeTrigger.click
@@ -428,7 +428,7 @@ class Tooltip {
if (this.config.html) {
if (this.config.sanitize) {
- content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn)
+ content = sanitizeHtml(content, this.config.allowList, this.config.sanitizeFn)
}
element.innerHTML = content
@@ -587,14 +587,14 @@ class Tooltip {
_enter(event, context) {
const dataKey = this.constructor.DATA_KEY
- context = context || Data.getData(event.target, dataKey)
+ context = context || Data.getData(event.delegateTarget, dataKey)
if (!context) {
context = new this.constructor(
- event.target,
+ event.delegateTarget,
this._getDelegateConfig()
)
- Data.setData(event.target, dataKey, context)
+ Data.setData(event.delegateTarget, dataKey, context)
}
if (event) {
@@ -627,14 +627,14 @@ class Tooltip {
_leave(event, context) {
const dataKey = this.constructor.DATA_KEY
- context = context || Data.getData(event.target, dataKey)
+ context = context || Data.getData(event.delegateTarget, dataKey)
if (!context) {
context = new this.constructor(
- event.target,
+ event.delegateTarget,
this._getDelegateConfig()
)
- Data.setData(event.target, dataKey, context)
+ Data.setData(event.delegateTarget, dataKey, context)
}
if (event) {
@@ -711,7 +711,7 @@ class Tooltip {
typeCheckConfig(NAME, config, this.constructor.DefaultType)
if (config.sanitize) {
- config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn)
+ config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn)
}
return config
diff --git a/js/src/util/sanitizer.js b/js/src/util/sanitizer.js
index e1ec36a40051..27bdf6cb1ffd 100644
--- a/js/src/util/sanitizer.js
+++ b/js/src/util/sanitizer.js
@@ -55,7 +55,7 @@ const allowedAttribute = (attr, allowedAttributeList) => {
return false
}
-export const DefaultWhitelist = {
+export const DefaultAllowlist = {
// Global attributes allowed on any supplied element below.
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
a: ['target', 'href', 'title', 'rel'],
@@ -89,7 +89,7 @@ export const DefaultWhitelist = {
ul: []
}
-export function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
+export function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {
if (!unsafeHtml.length) {
return unsafeHtml
}
@@ -100,24 +100,24 @@ export function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
const domParser = new window.DOMParser()
const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')
- const whitelistKeys = Object.keys(whiteList)
+ const allowlistKeys = Object.keys(allowList)
const elements = [].concat(...createdDocument.body.querySelectorAll('*'))
for (let i = 0, len = elements.length; i < len; i++) {
const el = elements[i]
const elName = el.nodeName.toLowerCase()
- if (whitelistKeys.indexOf(elName) === -1) {
+ if (allowlistKeys.indexOf(elName) === -1) {
el.parentNode.removeChild(el)
continue
}
const attributeList = [].concat(...el.attributes)
- const whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])
+ const allowedAttributes = [].concat(allowList['*'] || [], allowList[elName] || [])
attributeList.forEach(attr => {
- if (!allowedAttribute(attr, whitelistedAttributes)) {
+ if (!allowedAttribute(attr, allowedAttributes)) {
el.removeAttribute(attr.nodeName)
}
})
diff --git a/js/tests/unit/tooltip.spec.js b/js/tests/unit/tooltip.spec.js
index e713fe560202..0a98096a405e 100644
--- a/js/tests/unit/tooltip.spec.js
+++ b/js/tests/unit/tooltip.spec.js
@@ -324,6 +324,28 @@ describe('Tooltip', () => {
tooltip.show()
})
+ it('should show a tooltip when hovering a children element', done => {
+ fixtureEl.innerHTML =
+ '' +
+ '' +
+ ''
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ spyOn(tooltip, 'show')
+
+ tooltipEl.querySelector('rect').dispatchEvent(createEvent('mouseover', { bubbles: true }))
+
+ setTimeout(() => {
+ expect(tooltip.show).toHaveBeenCalled()
+ done()
+ }, 0)
+ })
+
it('should show a tooltip on mobile', done => {
fixtureEl.innerHTML = ''
diff --git a/js/tests/unit/util/sanitizer.spec.js b/js/tests/unit/util/sanitizer.spec.js
index c4259e7fd66c..dcfad8436f8f 100644
--- a/js/tests/unit/util/sanitizer.spec.js
+++ b/js/tests/unit/util/sanitizer.spec.js
@@ -1,11 +1,11 @@
-import { DefaultWhitelist, sanitizeHtml } from '../../../src/util/sanitizer'
+import { DefaultAllowlist, sanitizeHtml } from '../../../src/util/sanitizer'
describe('Sanitizer', () => {
describe('sanitizeHtml', () => {
it('should return the same on empty string', () => {
const empty = ''
- const result = sanitizeHtml(empty, DefaultWhitelist, null)
+ const result = sanitizeHtml(empty, DefaultAllowlist, null)
expect(result).toEqual(empty)
})
@@ -18,7 +18,7 @@ describe('Sanitizer', () => {
''
].join('')
- const result = sanitizeHtml(template, DefaultWhitelist, null)
+ const result = sanitizeHtml(template, DefaultAllowlist, null)
expect(result.indexOf('script') === -1).toEqual(true)
})
@@ -30,20 +30,20 @@ describe('Sanitizer', () => {
''
].join('')
- const result = sanitizeHtml(template, DefaultWhitelist, null)
+ const result = sanitizeHtml(template, DefaultAllowlist, null)
expect(result.indexOf('aria-pressed') !== -1).toEqual(true)
expect(result.indexOf('class="test"') !== -1).toEqual(true)
})
- it('should remove not whitelist tags', () => {
+ it('should remove tags not in allowlist', () => {
const template = [
'',
' ',
'
'
].join('')
- const result = sanitizeHtml(template, DefaultWhitelist, null)
+ const result = sanitizeHtml(template, DefaultAllowlist, null)
expect(result.indexOf('