1
0
Fork 0
mirror of https://github.com/twbs/bootstrap.git synced 2022-11-09 12:25:43 -05:00

Move get element functionality to a helper (#33327)

Looking around on js components I found out many checks, different expressed but with same purpose.
Some of them are trying to parse string to element, others, jQuery element to js simple nodeElement etc

With this Pr, I am trying to give a standard way to parse an element

So this pr:

* Creates `getElement` helper that tries to parse an argument to element or null
* Changes `isElement` to make  explicit checks and return Boolean 
* fixes tests deficiencies
This commit is contained in:
GeoSot 2021-05-13 18:17:20 +03:00 committed by GitHub
parent e376142d85
commit 6e1c9096f0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 85 additions and 49 deletions

View file

@ -9,6 +9,7 @@ import Data from './dom/data'
import { import {
emulateTransitionEnd, emulateTransitionEnd,
execute, execute,
getElement,
getTransitionDurationFromElement getTransitionDurationFromElement
} from './util/index' } from './util/index'
import EventHandler from './dom/event-handler' import EventHandler from './dom/event-handler'
@ -23,7 +24,7 @@ const VERSION = '5.0.0'
class BaseComponent { class BaseComponent {
constructor(element) { constructor(element) {
element = typeof element === 'string' ? document.querySelector(element) : element element = getElement(element)
if (!element) { if (!element) {
return return

View file

@ -7,9 +7,9 @@
import { import {
defineJQueryPlugin, defineJQueryPlugin,
getElement,
getSelectorFromElement, getSelectorFromElement,
getElementFromSelector, getElementFromSelector,
isElement,
reflow, reflow,
typeCheckConfig typeCheckConfig
} from './util/index' } from './util/index'
@ -272,14 +272,7 @@ class Collapse extends BaseComponent {
_getParent() { _getParent() {
let { parent } = this._config let { parent } = this._config
if (isElement(parent)) { parent = getElement(parent)
// it's a jQuery object
if (typeof parent.jquery !== 'undefined' || typeof parent[0] !== 'undefined') {
parent = parent[0]
}
} else {
parent = SelectorEngine.findOne(parent)
}
const selector = `${SELECTOR_DATA_TOGGLE}[data-bs-parent="${parent}"]` const selector = `${SELECTOR_DATA_TOGGLE}[data-bs-parent="${parent}"]`

View file

@ -9,6 +9,7 @@ import * as Popper from '@popperjs/core'
import { import {
defineJQueryPlugin, defineJQueryPlugin,
getElement,
getElementFromSelector, getElementFromSelector,
isDisabled, isDisabled,
isElement, isElement,
@ -166,12 +167,7 @@ class Dropdown extends BaseComponent {
if (this._config.reference === 'parent') { if (this._config.reference === 'parent') {
referenceElement = parent referenceElement = parent
} else if (isElement(this._config.reference)) { } else if (isElement(this._config.reference)) {
referenceElement = this._config.reference referenceElement = getElement(this._config.reference)
// Check if it's jQuery element
if (typeof this._config.reference.jquery !== 'undefined') {
referenceElement = this._config.reference[0]
}
} else if (typeof this._config.reference === 'object') { } else if (typeof this._config.reference === 'object') {
referenceElement = this._config.reference referenceElement = this._config.reference
} }

View file

@ -10,6 +10,7 @@ import * as Popper from '@popperjs/core'
import { import {
defineJQueryPlugin, defineJQueryPlugin,
findShadowRoot, findShadowRoot,
getElement,
getUID, getUID,
isElement, isElement,
isRTL, isRTL,
@ -256,7 +257,7 @@ class Tooltip extends BaseComponent {
const attachment = this._getAttachment(placement) const attachment = this._getAttachment(placement)
this._addAttachmentClass(attachment) this._addAttachmentClass(attachment)
const container = this._getContainer() const { container } = this._config
Data.set(tip, this.constructor.DATA_KEY, this) Data.set(tip, this.constructor.DATA_KEY, this)
if (!this._element.ownerDocument.documentElement.contains(this.tip)) { if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
@ -385,10 +386,8 @@ class Tooltip extends BaseComponent {
return return
} }
if (typeof content === 'object' && isElement(content)) { if (isElement(content)) {
if (content.jquery) { content = getElement(content)
content = content[0]
}
// content is a DOM node or a jQuery // content is a DOM node or a jQuery
if (this._config.html) { if (this._config.html) {
@ -518,18 +517,6 @@ class Tooltip extends BaseComponent {
this.getTipElement().classList.add(`${CLASS_PREFIX}-${this.updateAttachment(attachment)}`) this.getTipElement().classList.add(`${CLASS_PREFIX}-${this.updateAttachment(attachment)}`)
} }
_getContainer() {
if (this._config.container === false) {
return document.body
}
if (isElement(this._config.container)) {
return this._config.container
}
return SelectorEngine.findOne(this._config.container)
}
_getAttachment(placement) { _getAttachment(placement) {
return AttachmentMap[placement.toUpperCase()] return AttachmentMap[placement.toUpperCase()]
} }
@ -664,16 +651,14 @@ class Tooltip extends BaseComponent {
} }
}) })
if (config && typeof config.container === 'object' && config.container.jquery) {
config.container = config.container[0]
}
config = { config = {
...this.constructor.Default, ...this.constructor.Default,
...dataAttributes, ...dataAttributes,
...(typeof config === 'object' && config ? config : {}) ...(typeof config === 'object' && config ? config : {})
} }
config.container = config.container === false ? document.body : getElement(config.container)
if (typeof config.delay === 'number') { if (typeof config.delay === 'number') {
config.delay = { config.delay = {
show: config.delay, show: config.delay,

View file

@ -1,3 +1,5 @@
import SelectorEngine from '../dom/selector-engine'
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* Bootstrap (v5.0.0): util/index.js * Bootstrap (v5.0.0): util/index.js
@ -100,7 +102,29 @@ const triggerTransitionEnd = element => {
element.dispatchEvent(new Event(TRANSITION_END)) element.dispatchEvent(new Event(TRANSITION_END))
} }
const isElement = obj => (obj[0] || obj).nodeType const isElement = obj => {
if (!obj || typeof obj !== 'object') {
return false
}
if (typeof obj.jquery !== 'undefined') {
obj = obj[0]
}
return typeof obj.nodeType !== 'undefined'
}
const getElement = obj => {
if (isElement(obj)) { // it's a jQuery object or a node element
return obj.jquery ? obj[0] : obj
}
if (typeof obj === 'string' && obj.length > 0) {
return SelectorEngine.findOne(obj)
}
return null
}
const emulateTransitionEnd = (element, duration) => { const emulateTransitionEnd = (element, duration) => {
let called = false let called = false
@ -238,6 +262,7 @@ const execute = callback => {
} }
export { export {
getElement,
getUID, getUID,
getSelectorFromElement, getSelectorFromElement,
getElementFromSelector, getElementFromSelector,

View file

@ -58,7 +58,8 @@ describe('Collapse', () => {
const collapseEl = fixtureEl.querySelector('div.collapse') const collapseEl = fixtureEl.querySelector('div.collapse')
const myCollapseEl = fixtureEl.querySelector('.my-collapse') const myCollapseEl = fixtureEl.querySelector('.my-collapse')
const fakejQueryObject = { const fakejQueryObject = {
0: myCollapseEl 0: myCollapseEl,
jquery: 'foo'
} }
const collapse = new Collapse(collapseEl, { const collapse = new Collapse(collapseEl, {
parent: fakejQueryObject parent: fakejQueryObject

View file

@ -3,7 +3,7 @@ import EventHandler from '../../src/dom/event-handler'
import { noop } from '../../src/util' import { noop } from '../../src/util'
/** Test helpers */ /** Test helpers */
import { getFixture, clearFixture, createEvent, jQueryMock } from '../helpers/fixture' import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
describe('Dropdown', () => { describe('Dropdown', () => {
let fixtureEl let fixtureEl
@ -467,6 +467,7 @@ describe('Dropdown', () => {
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]') const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const virtualElement = { const virtualElement = {
nodeType: 1,
getBoundingClientRect() { getBoundingClientRect() {
return { return {
width: 0, width: 0,

View file

@ -1,7 +1,7 @@
import * as Util from '../../../src/util/index' import * as Util from '../../../src/util/index'
/** Test helpers */ /** Test helpers */
import { getFixture, clearFixture } from '../../helpers/fixture' import { clearFixture, getFixture } from '../../helpers/fixture'
describe('Util', () => { describe('Util', () => {
let fixtureEl let fixtureEl
@ -171,24 +171,58 @@ describe('Util', () => {
}) })
describe('isElement', () => { describe('isElement', () => {
it('should detect if the parameter is an element or not', () => { it('should detect if the parameter is an element or not and return Boolean', () => {
fixtureEl.innerHTML = '<div></div>' fixtureEl.innerHTML =
[
'<div id="foo" class="test"></div>',
'<div id="bar" class="test"></div>'
].join('')
const el = document.querySelector('div') const el = fixtureEl.querySelector('#foo')
expect(Util.isElement(el)).toEqual(el.nodeType) expect(Util.isElement(el)).toEqual(true)
expect(Util.isElement({})).toEqual(undefined) expect(Util.isElement({})).toEqual(false)
expect(Util.isElement(fixtureEl.querySelectorAll('.test'))).toEqual(false)
}) })
it('should detect jQuery element', () => { it('should detect jQuery element', () => {
fixtureEl.innerHTML = '<div></div>' fixtureEl.innerHTML = '<div></div>'
const el = document.querySelector('div') const el = fixtureEl.querySelector('div')
const fakejQuery = { const fakejQuery = {
0: el 0: el,
jquery: 'foo'
} }
expect(Util.isElement(fakejQuery)).toEqual(el.nodeType) expect(Util.isElement(fakejQuery)).toEqual(true)
})
})
describe('getElement', () => {
it('should try to parse element', () => {
fixtureEl.innerHTML =
[
'<div id="foo" class="test"></div>',
'<div id="bar" class="test"></div>'
].join('')
const el = fixtureEl.querySelector('div')
expect(Util.getElement(el)).toEqual(el)
expect(Util.getElement('#foo')).toEqual(el)
expect(Util.getElement('#fail')).toBeNull()
expect(Util.getElement({})).toBeNull()
expect(Util.getElement([])).toBeNull()
expect(Util.getElement()).toBeNull()
expect(Util.getElement(null)).toBeNull()
expect(Util.getElement(fixtureEl.querySelectorAll('.test'))).toBeNull()
const fakejQueryObject = {
0: el,
jquery: 'foo'
}
expect(Util.getElement(fakejQueryObject)).toEqual(el)
}) })
}) })