fix: add trick to support tooltip selector usage on dynamic created tooltips that utilize `title` attribute (#36914)

This commit is contained in:
GeoSot 2022-09-14 16:24:37 +03:00 committed by GitHub
parent 4600a25404
commit 3bd5756414
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 58 additions and 45 deletions

View File

@ -121,6 +121,10 @@ class Tooltip extends BaseComponent {
this.tip = null this.tip = null
this._setListeners() this._setListeners()
if (!this._config.selector) {
this._fixTitle()
}
} }
// Getters // Getters
@ -149,25 +153,12 @@ class Tooltip extends BaseComponent {
this._isEnabled = !this._isEnabled this._isEnabled = !this._isEnabled
} }
toggle(event) { toggle() {
if (!this._isEnabled) { if (!this._isEnabled) {
return return
} }
if (event) { this._activeTrigger.click = !this._activeTrigger.click
const context = this._initializeOnDelegatedTarget(event)
context._activeTrigger.click = !context._activeTrigger.click
if (context._isWithActiveTrigger()) {
context._enter()
} else {
context._leave()
}
return
}
if (this._isShown()) { if (this._isShown()) {
this._leave() this._leave()
return return
@ -185,8 +176,8 @@ class Tooltip extends BaseComponent {
this.tip.remove() this.tip.remove()
} }
if (this._config.originalTitle) { if (this._element.getAttribute('data-bs-original-title')) {
this._element.setAttribute('title', this._config.originalTitle) this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'))
} }
this._disposePopper() this._disposePopper()
@ -375,7 +366,7 @@ class Tooltip extends BaseComponent {
} }
_getTitle() { _getTitle() {
return this._resolvePossibleFunction(this._config.title) || this._config.originalTitle return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title')
} }
// Private // Private
@ -469,7 +460,10 @@ class Tooltip extends BaseComponent {
for (const trigger of triggers) { for (const trigger of triggers) {
if (trigger === 'click') { if (trigger === 'click') {
EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => this.toggle(event)) EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {
const context = this._initializeOnDelegatedTarget(event)
context.toggle()
})
} else if (trigger !== TRIGGER_MANUAL) { } else if (trigger !== TRIGGER_MANUAL) {
const eventIn = trigger === TRIGGER_HOVER ? const eventIn = trigger === TRIGGER_HOVER ?
this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_MOUSEENTER) :
@ -500,20 +494,10 @@ class Tooltip extends BaseComponent {
} }
EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler) EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)
if (this._config.selector) {
this._config = {
...this._config,
trigger: 'manual',
selector: ''
}
} else {
this._fixTitle()
}
} }
_fixTitle() { _fixTitle() {
const title = this._config.originalTitle const title = this._element.getAttribute('title')
if (!title) { if (!title) {
return return
@ -523,6 +507,7 @@ class Tooltip extends BaseComponent {
this._element.setAttribute('aria-label', title) this._element.setAttribute('aria-label', title)
} }
this._element.setAttribute('data-bs-original-title', title) // DO NOT USE IT. Is only for backwards compatibility
this._element.removeAttribute('title') this._element.removeAttribute('title')
} }
@ -593,7 +578,6 @@ class Tooltip extends BaseComponent {
} }
} }
config.originalTitle = this._element.getAttribute('title') || ''
if (typeof config.title === 'number') { if (typeof config.title === 'number') {
config.title = config.title.toString() config.title = config.title.toString()
} }
@ -614,6 +598,9 @@ class Tooltip extends BaseComponent {
} }
} }
config.selector = false
config.trigger = 'manual'
// In the future can be replaced with: // In the future can be replaced with:
// const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]]) // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])
// `Object.fromEntries(keysWithDifferentValues)` // `Object.fromEntries(keysWithDifferentValues)`

View File

@ -66,42 +66,68 @@
<div id="shadow" class="pt-5"></div> <div id="shadow" class="pt-5"></div>
</div> </div>
<div id="customContainer"></div> <div id="customContainer"></div>
<div class="row mt-4 border-top">
<hr>
<div class="h4">Test Selector triggered tooltips</div>
<div id="wrapperTriggeredBySelector">
<div class="py-2 selectorButtonsBlock">
<button type="button" class="btn btn-secondary bs-dynamic-tooltip" title="random title">Using title</button>
<button type="button" class="btn btn-secondary bs-dynamic-tooltip" data-bs-title="random title">Using bs-title</button>
</div>
</div>
<div class="mt-3">
<button type="button" class="btn btn-primary" onclick="duplicateButtons()">Duplicate above two buttons</button>
</div>
</div>
</div> </div>
<script src="../../../dist/js/bootstrap.bundle.js"></script> <script src="../../../dist/js/bootstrap.bundle.js"></script>
<script> <script>
if (typeof document.body.attachShadow === 'function') { if (typeof document.body.attachShadow === 'function') {
var shadowRoot = document.getElementById('shadow').attachShadow({ mode: 'open' }) const shadowRoot = document.getElementById('shadow').attachShadow({ mode: 'open' })
shadowRoot.innerHTML = shadowRoot.innerHTML =
'<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom">' + '<button id="firstShadowTooltip" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom">' +
' Tooltip on top in a shadow dom' + ' Tooltip on top in a shadow dom' +
'</button>' + '</button>' +
'<button id="secondTooltip" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom with container option">' + '<button id="secondShadowTooltip" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom with container option">' +
' Tooltip on top in a shadow dom' + ' Tooltip on top in a shadow dom' +
'</button>' '</button>'
var firstChildTooltip = new bootstrap.Tooltip(shadowRoot.firstChild) new bootstrap.Tooltip(shadowRoot.firstChild)
var secondChildTooltip = new bootstrap.Tooltip(shadowRoot.getElementById('secondTooltip'), { new bootstrap.Tooltip(shadowRoot.getElementById('secondShadowTooltip'), {
container: shadowRoot container: shadowRoot
}) })
} }
var tooltipElements = document.querySelectorAll('[data-bs-toggle="tooltip"]')
for (const tooltipEl of tooltipElements) {
new bootstrap.Tooltip(tooltipEl)
}
var tooltipElement = document.getElementById('tooltipElement') new bootstrap.Tooltip('#tooltipElement', {
var tooltipElementInstance = new bootstrap.Tooltip(tooltipElement, { container: '#customContainer'
container: document.getElementById('customContainer')
}) })
var target = document.getElementById('target') const targetTooltip = new bootstrap.Tooltip('#target', {
var targetTooltip = new bootstrap.Tooltip(target, {
placement : 'top', placement : 'top',
trigger : 'manual' trigger : 'manual'
}) })
targetTooltip.show() targetTooltip.show()
document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(tooltipEl=> new bootstrap.Tooltip(tooltipEl))
</script> </script>
<script>
new bootstrap.Tooltip('#wrapperTriggeredBySelector', {
animation: false,
selector: '.bs-dynamic-tooltip'
})
function duplicateButtons() {
const buttonsBlock = document.querySelector('.selectorButtonsBlock')// get first
const buttonsBlockClone = buttonsBlock.cloneNode(true)
buttonsBlockClone.innerHTML+= new Date().toLocaleString()
document.querySelector('#wrapperTriggeredBySelector').append(buttonsBlockClone)
}
</script>
</body> </body>
</html> </html>