Skip to content

Commit

Permalink
Merge branch 'main' into master-fod-Edge-floating-labels
Browse files Browse the repository at this point in the history
  • Loading branch information
XhmikosR committed Jun 22, 2020
2 parents 6938e5c + d6671d8 commit 160606f
Show file tree
Hide file tree
Showing 28 changed files with 213 additions and 139 deletions.
2 changes: 1 addition & 1 deletion .github/CONTRIBUTING.md
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion .github/SUPPORT.md
Expand Up @@ -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.
4 changes: 2 additions & 2 deletions README.md
Expand Up @@ -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`

Expand Down Expand Up @@ -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.


Expand Down
2 changes: 1 addition & 1 deletion build/change-version.js
Expand Up @@ -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',
Expand Down
2 changes: 2 additions & 0 deletions js/src/dom/event-handler.js
Expand Up @@ -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)
}
Expand All @@ -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)
}
Expand Down
30 changes: 15 additions & 15 deletions js/src/tooltip.js
Expand Up @@ -17,7 +17,7 @@ import {
typeCheckConfig
} from './util/index'
import {
DefaultWhitelist,
DefaultAllowlist,
sanitizeHtml
} from './util/sanitizer'
import Data from './dom/data'
Expand All @@ -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',
Expand All @@ -55,7 +55,7 @@ const DefaultType = {
boundary: '(string|element)',
sanitize: 'boolean',
sanitizeFn: '(null|function)',
whiteList: 'object',
allowList: 'object',
popperConfig: '(null|object)'
}

Expand Down Expand Up @@ -84,7 +84,7 @@ const Default = {
boundary: 'scrollParent',
sanitize: true,
sanitizeFn: null,
whiteList: DefaultWhitelist,
allowList: DefaultAllowlist,
popperConfig: null
}

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down
12 changes: 6 additions & 6 deletions js/src/util/sanitizer.js
Expand Up @@ -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'],
Expand Down Expand Up @@ -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
}
Expand All @@ -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)
}
})
Expand Down
22 changes: 22 additions & 0 deletions js/tests/unit/tooltip.spec.js
Expand Up @@ -324,6 +324,28 @@ describe('Tooltip', () => {
tooltip.show()
})

it('should show a tooltip when hovering a children element', done => {
fixtureEl.innerHTML =
'<a href="#" rel="tooltip" title="Another tooltip">' +
'<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 100 100">' +
'<rect width="100%" fill="#563d7c"/>' +
'<circle cx="50" cy="50" r="30" fill="#fff"/>' +
'</svg>' +
'</a>'

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 = '<a href="#" rel="tooltip" title="Another tooltip">'

Expand Down
14 changes: 7 additions & 7 deletions 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)
})
Expand All @@ -18,7 +18,7 @@ describe('Sanitizer', () => {
'</div>'
].join('')

const result = sanitizeHtml(template, DefaultWhitelist, null)
const result = sanitizeHtml(template, DefaultAllowlist, null)

expect(result.indexOf('script') === -1).toEqual(true)
})
Expand All @@ -30,20 +30,20 @@ describe('Sanitizer', () => {
'</div>'
].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 = [
'<div>',
' <script>alert(7)</script>',
'</div>'
].join('')

const result = sanitizeHtml(template, DefaultWhitelist, null)
const result = sanitizeHtml(template, DefaultAllowlist, null)

expect(result.indexOf('<script>') === -1).toEqual(true)
})
Expand All @@ -61,7 +61,7 @@ describe('Sanitizer', () => {

spyOn(DOMParser.prototype, 'parseFromString')

const result = sanitizeHtml(template, DefaultWhitelist, mySanitize)
const result = sanitizeHtml(template, DefaultAllowlist, mySanitize)

expect(result).toEqual(template)
expect(DOMParser.prototype.parseFromString).not.toHaveBeenCalled()
Expand Down

0 comments on commit 160606f

Please sign in to comment.