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:
GeoSot 2021-05-11 10:49:30 +03:00 committed by GitHub
parent 7647b8fe5b
commit 9fe36edf68
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 167 additions and 53 deletions

View File

@ -43,8 +43,8 @@ const CLASS_NAME_SHOW = 'show'
class Alert extends BaseComponent {
// Getters
static get DATA_KEY() {
return DATA_KEY
static get NAME() {
return NAME
}
// 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
*/
defineJQueryPlugin(NAME, Alert)
defineJQueryPlugin(Alert)
export default Alert

View File

@ -35,7 +35,7 @@ class BaseComponent {
dispose() {
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 => {
this[propertyName] = null
@ -63,6 +63,18 @@ class BaseComponent {
static get 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

View File

@ -36,8 +36,8 @@ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
class Button extends BaseComponent {
// Getters
static get DATA_KEY() {
return DATA_KEY
static get NAME() {
return NAME
}
// 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
*/
defineJQueryPlugin(NAME, Button)
defineJQueryPlugin(Button)
export default Button

View File

@ -127,8 +127,8 @@ class Carousel extends BaseComponent {
return Default
}
static get DATA_KEY() {
return DATA_KEY
static get NAME() {
return NAME
}
// Public
@ -598,6 +598,6 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
* add .Carousel to jQuery only if jQuery is present
*/
defineJQueryPlugin(NAME, Carousel)
defineJQueryPlugin(Carousel)
export default Carousel

View File

@ -105,8 +105,8 @@ class Collapse extends BaseComponent {
return Default
}
static get DATA_KEY() {
return DATA_KEY
static get NAME() {
return NAME
}
// 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
*/
defineJQueryPlugin(NAME, Collapse)
defineJQueryPlugin(Collapse)
export default Collapse

View File

@ -116,8 +116,8 @@ class Dropdown extends BaseComponent {
return DefaultType
}
static get DATA_KEY() {
return DATA_KEY
static get NAME() {
return NAME
}
// 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
*/
defineJQueryPlugin(NAME, Dropdown)
defineJQueryPlugin(Dropdown)
export default Dropdown

View File

@ -93,8 +93,8 @@ class Modal extends BaseComponent {
return Default
}
static get DATA_KEY() {
return DATA_KEY
static get NAME() {
return NAME
}
// 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
*/
defineJQueryPlugin(NAME, Modal)
defineJQueryPlugin(Modal)
export default Modal

View File

@ -78,12 +78,12 @@ class Offcanvas extends BaseComponent {
// Getters
static get Default() {
return Default
static get NAME() {
return NAME
}
static get DATA_KEY() {
return DATA_KEY
static get Default() {
return Default
}
// Public
@ -271,6 +271,6 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
* ------------------------------------------------------------------------
*/
defineJQueryPlugin(NAME, Offcanvas)
defineJQueryPlugin(Offcanvas)
export default Offcanvas

View File

@ -76,18 +76,10 @@ class Popover extends Tooltip {
return NAME
}
static get DATA_KEY() {
return DATA_KEY
}
static get Event() {
return Event
}
static get EVENT_KEY() {
return EVENT_KEY
}
static get DefaultType() {
return DefaultType
}
@ -166,6 +158,6 @@ class Popover extends Tooltip {
* add .Popover to jQuery only if jQuery is present
*/
defineJQueryPlugin(NAME, Popover)
defineJQueryPlugin(Popover)
export default Popover

View File

@ -87,8 +87,8 @@ class ScrollSpy extends BaseComponent {
return Default
}
static get DATA_KEY() {
return DATA_KEY
static get NAME() {
return NAME
}
// Public
@ -303,6 +303,6 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
* add .ScrollSpy to jQuery only if jQuery is present
*/
defineJQueryPlugin(NAME, ScrollSpy)
defineJQueryPlugin(ScrollSpy)
export default ScrollSpy

View File

@ -55,8 +55,8 @@ const SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active'
class Tab extends BaseComponent {
// Getters
static get DATA_KEY() {
return DATA_KEY
static get NAME() {
return NAME
}
// 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
*/
defineJQueryPlugin(NAME, Tab)
defineJQueryPlugin(Tab)
export default Tab

View File

@ -81,8 +81,8 @@ class Toast extends BaseComponent {
return Default
}
static get DATA_KEY() {
return DATA_KEY
static get NAME() {
return NAME
}
// Public
@ -243,6 +243,6 @@ class Toast extends BaseComponent {
* add .Toast to jQuery only if jQuery is present
*/
defineJQueryPlugin(NAME, Toast)
defineJQueryPlugin(Toast)
export default Toast

View File

@ -155,18 +155,10 @@ class Tooltip extends BaseComponent {
return NAME
}
static get DATA_KEY() {
return DATA_KEY
}
static get Event() {
return Event
}
static get EVENT_KEY() {
return EVENT_KEY
}
static get DefaultType() {
return DefaultType
}
@ -774,6 +766,6 @@ class Tooltip extends BaseComponent {
* add .Tooltip to jQuery only if jQuery is present
*/
defineJQueryPlugin(NAME, Tooltip)
defineJQueryPlugin(Tooltip)
export default Tooltip

View File

@ -214,11 +214,12 @@ const onDOMContentLoaded = callback => {
const isRTL = () => document.documentElement.dir === 'rtl'
const defineJQueryPlugin = (name, plugin) => {
const defineJQueryPlugin = plugin => {
onDOMContentLoaded(() => {
const $ = getjQuery()
/* istanbul ignore if */
if ($) {
const name = plugin.NAME
const JQUERY_NO_CONFLICT = $.fn[name]
$.fn[name] = plugin.jQueryInterface
$.fn[name].Constructor = plugin

View 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)
})
})
})
})

View File

@ -560,9 +560,10 @@ describe('Util', () => {
it('should define a plugin on the jQuery instance', () => {
const pluginMock = function () {}
pluginMock.NAME = 'test'
pluginMock.jQueryInterface = function () {}
Util.defineJQueryPlugin('test', pluginMock)
Util.defineJQueryPlugin(pluginMock)
expect(fakejQuery.fn.test).toBe(pluginMock.jQueryInterface)
expect(fakejQuery.fn.test.Constructor).toBe(pluginMock)
expect(typeof fakejQuery.fn.test.noConflict).toEqual('function')