mirror of
https://github.com/twbs/bootstrap.git
synced 2025-08-09 07:06:36 +02:00
Extract static DATA_KEY
& EVENT_KEY
to base-component (#33635)
* Force each plugin that extends base-components to implement a static method `NAME()` * Remove redundant `NAME` argument from 'Utils.defineJQueryPlugin' & fix test
This commit is contained in:
@@ -43,8 +43,8 @@ const CLASS_NAME_SHOW = 'show'
|
|||||||
class Alert extends BaseComponent {
|
class Alert extends BaseComponent {
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
static get DATA_KEY() {
|
static get NAME() {
|
||||||
return DATA_KEY
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@@ -127,6 +127,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert.handleDi
|
|||||||
* add .Alert to jQuery only if jQuery is present
|
* add .Alert to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Alert)
|
defineJQueryPlugin(Alert)
|
||||||
|
|
||||||
export default Alert
|
export default Alert
|
||||||
|
@@ -35,7 +35,7 @@ class BaseComponent {
|
|||||||
|
|
||||||
dispose() {
|
dispose() {
|
||||||
Data.remove(this._element, this.constructor.DATA_KEY)
|
Data.remove(this._element, this.constructor.DATA_KEY)
|
||||||
EventHandler.off(this._element, `.${this.constructor.DATA_KEY}`)
|
EventHandler.off(this._element, this.constructor.EVENT_KEY)
|
||||||
|
|
||||||
Object.getOwnPropertyNames(this).forEach(propertyName => {
|
Object.getOwnPropertyNames(this).forEach(propertyName => {
|
||||||
this[propertyName] = null
|
this[propertyName] = null
|
||||||
@@ -63,6 +63,18 @@ class BaseComponent {
|
|||||||
static get VERSION() {
|
static get VERSION() {
|
||||||
return VERSION
|
return VERSION
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static get NAME() {
|
||||||
|
throw new Error('You have to implement the static method "NAME", for each component!')
|
||||||
|
}
|
||||||
|
|
||||||
|
static get DATA_KEY() {
|
||||||
|
return `bs.${this.NAME}`
|
||||||
|
}
|
||||||
|
|
||||||
|
static get EVENT_KEY() {
|
||||||
|
return `.${this.DATA_KEY}`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default BaseComponent
|
export default BaseComponent
|
||||||
|
@@ -36,8 +36,8 @@ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
|
|||||||
class Button extends BaseComponent {
|
class Button extends BaseComponent {
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
static get DATA_KEY() {
|
static get NAME() {
|
||||||
return DATA_KEY
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@@ -90,6 +90,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
|
|||||||
* add .Button to jQuery only if jQuery is present
|
* add .Button to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Button)
|
defineJQueryPlugin(Button)
|
||||||
|
|
||||||
export default Button
|
export default Button
|
||||||
|
@@ -127,8 +127,8 @@ class Carousel extends BaseComponent {
|
|||||||
return Default
|
return Default
|
||||||
}
|
}
|
||||||
|
|
||||||
static get DATA_KEY() {
|
static get NAME() {
|
||||||
return DATA_KEY
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@@ -598,6 +598,6 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
|
|||||||
* add .Carousel to jQuery only if jQuery is present
|
* add .Carousel to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Carousel)
|
defineJQueryPlugin(Carousel)
|
||||||
|
|
||||||
export default Carousel
|
export default Carousel
|
||||||
|
@@ -105,8 +105,8 @@ class Collapse extends BaseComponent {
|
|||||||
return Default
|
return Default
|
||||||
}
|
}
|
||||||
|
|
||||||
static get DATA_KEY() {
|
static get NAME() {
|
||||||
return DATA_KEY
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@@ -390,6 +390,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
|
|||||||
* add .Collapse to jQuery only if jQuery is present
|
* add .Collapse to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Collapse)
|
defineJQueryPlugin(Collapse)
|
||||||
|
|
||||||
export default Collapse
|
export default Collapse
|
||||||
|
@@ -116,8 +116,8 @@ class Dropdown extends BaseComponent {
|
|||||||
return DefaultType
|
return DefaultType
|
||||||
}
|
}
|
||||||
|
|
||||||
static get DATA_KEY() {
|
static get NAME() {
|
||||||
return DATA_KEY
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@@ -530,6 +530,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
|
|||||||
* add .Dropdown to jQuery only if jQuery is present
|
* add .Dropdown to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Dropdown)
|
defineJQueryPlugin(Dropdown)
|
||||||
|
|
||||||
export default Dropdown
|
export default Dropdown
|
||||||
|
@@ -93,8 +93,8 @@ class Modal extends BaseComponent {
|
|||||||
return Default
|
return Default
|
||||||
}
|
}
|
||||||
|
|
||||||
static get DATA_KEY() {
|
static get NAME() {
|
||||||
return DATA_KEY
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@@ -441,6 +441,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
|
|||||||
* add .Modal to jQuery only if jQuery is present
|
* add .Modal to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Modal)
|
defineJQueryPlugin(Modal)
|
||||||
|
|
||||||
export default Modal
|
export default Modal
|
||||||
|
@@ -78,12 +78,12 @@ class Offcanvas extends BaseComponent {
|
|||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
static get Default() {
|
static get NAME() {
|
||||||
return Default
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
static get DATA_KEY() {
|
static get Default() {
|
||||||
return DATA_KEY
|
return Default
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@@ -271,6 +271,6 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
|
|||||||
* ------------------------------------------------------------------------
|
* ------------------------------------------------------------------------
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Offcanvas)
|
defineJQueryPlugin(Offcanvas)
|
||||||
|
|
||||||
export default Offcanvas
|
export default Offcanvas
|
||||||
|
@@ -76,18 +76,10 @@ class Popover extends Tooltip {
|
|||||||
return NAME
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
static get DATA_KEY() {
|
|
||||||
return DATA_KEY
|
|
||||||
}
|
|
||||||
|
|
||||||
static get Event() {
|
static get Event() {
|
||||||
return Event
|
return Event
|
||||||
}
|
}
|
||||||
|
|
||||||
static get EVENT_KEY() {
|
|
||||||
return EVENT_KEY
|
|
||||||
}
|
|
||||||
|
|
||||||
static get DefaultType() {
|
static get DefaultType() {
|
||||||
return DefaultType
|
return DefaultType
|
||||||
}
|
}
|
||||||
@@ -166,6 +158,6 @@ class Popover extends Tooltip {
|
|||||||
* add .Popover to jQuery only if jQuery is present
|
* add .Popover to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Popover)
|
defineJQueryPlugin(Popover)
|
||||||
|
|
||||||
export default Popover
|
export default Popover
|
||||||
|
@@ -87,8 +87,8 @@ class ScrollSpy extends BaseComponent {
|
|||||||
return Default
|
return Default
|
||||||
}
|
}
|
||||||
|
|
||||||
static get DATA_KEY() {
|
static get NAME() {
|
||||||
return DATA_KEY
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@@ -303,6 +303,6 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
|
|||||||
* add .ScrollSpy to jQuery only if jQuery is present
|
* add .ScrollSpy to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, ScrollSpy)
|
defineJQueryPlugin(ScrollSpy)
|
||||||
|
|
||||||
export default ScrollSpy
|
export default ScrollSpy
|
||||||
|
@@ -55,8 +55,8 @@ const SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active'
|
|||||||
class Tab extends BaseComponent {
|
class Tab extends BaseComponent {
|
||||||
// Getters
|
// Getters
|
||||||
|
|
||||||
static get DATA_KEY() {
|
static get NAME() {
|
||||||
return DATA_KEY
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@@ -220,6 +220,6 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
|
|||||||
* add .Tab to jQuery only if jQuery is present
|
* add .Tab to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Tab)
|
defineJQueryPlugin(Tab)
|
||||||
|
|
||||||
export default Tab
|
export default Tab
|
||||||
|
@@ -81,8 +81,8 @@ class Toast extends BaseComponent {
|
|||||||
return Default
|
return Default
|
||||||
}
|
}
|
||||||
|
|
||||||
static get DATA_KEY() {
|
static get NAME() {
|
||||||
return DATA_KEY
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
// Public
|
// Public
|
||||||
@@ -243,6 +243,6 @@ class Toast extends BaseComponent {
|
|||||||
* add .Toast to jQuery only if jQuery is present
|
* add .Toast to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Toast)
|
defineJQueryPlugin(Toast)
|
||||||
|
|
||||||
export default Toast
|
export default Toast
|
||||||
|
@@ -155,18 +155,10 @@ class Tooltip extends BaseComponent {
|
|||||||
return NAME
|
return NAME
|
||||||
}
|
}
|
||||||
|
|
||||||
static get DATA_KEY() {
|
|
||||||
return DATA_KEY
|
|
||||||
}
|
|
||||||
|
|
||||||
static get Event() {
|
static get Event() {
|
||||||
return Event
|
return Event
|
||||||
}
|
}
|
||||||
|
|
||||||
static get EVENT_KEY() {
|
|
||||||
return EVENT_KEY
|
|
||||||
}
|
|
||||||
|
|
||||||
static get DefaultType() {
|
static get DefaultType() {
|
||||||
return DefaultType
|
return DefaultType
|
||||||
}
|
}
|
||||||
@@ -774,6 +766,6 @@ class Tooltip extends BaseComponent {
|
|||||||
* add .Tooltip to jQuery only if jQuery is present
|
* add .Tooltip to jQuery only if jQuery is present
|
||||||
*/
|
*/
|
||||||
|
|
||||||
defineJQueryPlugin(NAME, Tooltip)
|
defineJQueryPlugin(Tooltip)
|
||||||
|
|
||||||
export default Tooltip
|
export default Tooltip
|
||||||
|
@@ -214,11 +214,12 @@ const onDOMContentLoaded = callback => {
|
|||||||
|
|
||||||
const isRTL = () => document.documentElement.dir === 'rtl'
|
const isRTL = () => document.documentElement.dir === 'rtl'
|
||||||
|
|
||||||
const defineJQueryPlugin = (name, plugin) => {
|
const defineJQueryPlugin = plugin => {
|
||||||
onDOMContentLoaded(() => {
|
onDOMContentLoaded(() => {
|
||||||
const $ = getjQuery()
|
const $ = getjQuery()
|
||||||
/* istanbul ignore if */
|
/* istanbul ignore if */
|
||||||
if ($) {
|
if ($) {
|
||||||
|
const name = plugin.NAME
|
||||||
const JQUERY_NO_CONFLICT = $.fn[name]
|
const JQUERY_NO_CONFLICT = $.fn[name]
|
||||||
$.fn[name] = plugin.jQueryInterface
|
$.fn[name] = plugin.jQueryInterface
|
||||||
$.fn[name].Constructor = plugin
|
$.fn[name].Constructor = plugin
|
||||||
|
116
js/tests/unit/base-component.spec.js
Normal file
116
js/tests/unit/base-component.spec.js
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
import BaseComponent from '../../src/base-component'
|
||||||
|
import { clearFixture, getFixture } from '../helpers/fixture'
|
||||||
|
import EventHandler from '../../src/dom/event-handler'
|
||||||
|
import { noop } from '../../src/util'
|
||||||
|
|
||||||
|
class DummyClass extends BaseComponent {
|
||||||
|
constructor(element) {
|
||||||
|
super(element)
|
||||||
|
|
||||||
|
EventHandler.on(this._element, `click${DummyClass.EVENT_KEY}`, noop)
|
||||||
|
}
|
||||||
|
|
||||||
|
static get NAME() {
|
||||||
|
return 'dummy'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('Base Component', () => {
|
||||||
|
let fixtureEl
|
||||||
|
const name = 'dummy'
|
||||||
|
let element
|
||||||
|
let instance
|
||||||
|
const createInstance = () => {
|
||||||
|
fixtureEl.innerHTML = '<div id="foo"></div>'
|
||||||
|
element = fixtureEl.querySelector('#foo')
|
||||||
|
instance = new DummyClass(element)
|
||||||
|
}
|
||||||
|
|
||||||
|
beforeAll(() => {
|
||||||
|
fixtureEl = getFixture()
|
||||||
|
})
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
clearFixture()
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('Static Methods', () => {
|
||||||
|
describe('VERSION', () => {
|
||||||
|
it('should return version', () => {
|
||||||
|
expect(typeof DummyClass.VERSION).toEqual('string')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('DATA_KEY', () => {
|
||||||
|
it('should return plugin data key', () => {
|
||||||
|
expect(DummyClass.DATA_KEY).toEqual(`bs.${name}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('NAME', () => {
|
||||||
|
it('should return plugin NAME', () => {
|
||||||
|
expect(DummyClass.NAME).toEqual(name)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('EVENT_KEY', () => {
|
||||||
|
it('should return plugin event key', () => {
|
||||||
|
expect(DummyClass.EVENT_KEY).toEqual(`.bs.${name}`)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('Public Methods', () => {
|
||||||
|
describe('constructor', () => {
|
||||||
|
it('should accept element, either passed as a CSS selector or DOM element', () => {
|
||||||
|
fixtureEl.innerHTML = [
|
||||||
|
'<div id="foo"></div>',
|
||||||
|
'<div id="bar"></div>'
|
||||||
|
].join('')
|
||||||
|
|
||||||
|
const el = fixtureEl.querySelector('#foo')
|
||||||
|
const elInstance = new DummyClass(el)
|
||||||
|
const selectorInstance = new DummyClass('#bar')
|
||||||
|
|
||||||
|
expect(elInstance._element).toEqual(el)
|
||||||
|
expect(selectorInstance._element).toEqual(fixtureEl.querySelector('#bar'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
describe('dispose', () => {
|
||||||
|
it('should dispose an component', () => {
|
||||||
|
createInstance()
|
||||||
|
expect(DummyClass.getInstance(element)).not.toBeNull()
|
||||||
|
|
||||||
|
instance.dispose()
|
||||||
|
|
||||||
|
expect(DummyClass.getInstance(element)).toBeNull()
|
||||||
|
expect(instance._element).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should de-register element event listeners', () => {
|
||||||
|
createInstance()
|
||||||
|
spyOn(EventHandler, 'off')
|
||||||
|
|
||||||
|
instance.dispose()
|
||||||
|
|
||||||
|
expect(EventHandler.off).toHaveBeenCalledWith(element, DummyClass.EVENT_KEY)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('getInstance', () => {
|
||||||
|
it('should return an instance', () => {
|
||||||
|
createInstance()
|
||||||
|
|
||||||
|
expect(DummyClass.getInstance(element)).toEqual(instance)
|
||||||
|
expect(DummyClass.getInstance(element)).toBeInstanceOf(DummyClass)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return null when there is no instance', () => {
|
||||||
|
fixtureEl.innerHTML = '<div></div>'
|
||||||
|
|
||||||
|
const div = fixtureEl.querySelector('div')
|
||||||
|
|
||||||
|
expect(DummyClass.getInstance(div)).toEqual(null)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
@@ -560,9 +560,10 @@ describe('Util', () => {
|
|||||||
|
|
||||||
it('should define a plugin on the jQuery instance', () => {
|
it('should define a plugin on the jQuery instance', () => {
|
||||||
const pluginMock = function () {}
|
const pluginMock = function () {}
|
||||||
|
pluginMock.NAME = 'test'
|
||||||
pluginMock.jQueryInterface = function () {}
|
pluginMock.jQueryInterface = function () {}
|
||||||
|
|
||||||
Util.defineJQueryPlugin('test', pluginMock)
|
Util.defineJQueryPlugin(pluginMock)
|
||||||
expect(fakejQuery.fn.test).toBe(pluginMock.jQueryInterface)
|
expect(fakejQuery.fn.test).toBe(pluginMock.jQueryInterface)
|
||||||
expect(fakejQuery.fn.test.Constructor).toBe(pluginMock)
|
expect(fakejQuery.fn.test.Constructor).toBe(pluginMock)
|
||||||
expect(typeof fakejQuery.fn.test.noConflict).toEqual('function')
|
expect(typeof fakejQuery.fn.test.noConflict).toEqual('function')
|
||||||
|
Reference in New Issue
Block a user