]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Extract static `DATA_KEY` & `EVENT_KEY` to base-component (#33635)
authorGeoSot <geo.sotis@gmail.com>
Tue, 11 May 2021 07:49:30 +0000 (10:49 +0300)
committerGitHub <noreply@github.com>
Tue, 11 May 2021 07:49:30 +0000 (10:49 +0300)
* Force each plugin that extends base-components to implement a static method `NAME()`
* Remove redundant `NAME` argument from 'Utils.defineJQueryPlugin' & fix test

16 files changed:
js/src/alert.js
js/src/base-component.js
js/src/button.js
js/src/carousel.js
js/src/collapse.js
js/src/dropdown.js
js/src/modal.js
js/src/offcanvas.js
js/src/popover.js
js/src/scrollspy.js
js/src/tab.js
js/src/toast.js
js/src/tooltip.js
js/src/util/index.js
js/tests/unit/base-component.spec.js [new file with mode: 0644]
js/tests/unit/util/index.spec.js

index 8d0838737a70f4c66fef8e12bddb445a54dc056e..db58d7426b0ec22184c3610d4005586874952a89 100644 (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
index 588a59d756cfe30392886741f6ce82039124623b..eacc8420bce03616e45f191f2d50c31e86a4337b 100644 (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
index 45c691dd438d73495654a12994917ecfc2bb5efc..4932c19552cf8be84bd62f5bf4e56829d16e36c7 100644 (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
index 92733637e9b0d48d14b6cad49c1f32b397f16b6d..0e45fed761d749fb23c72d146352109d44b7a5fd 100644 (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
index 0d1b91be55cf6e1ec519af9562bf4d1ffb7e1209..1c8f53ebd62584d95627a1c847d44ad2d0094f61 100644 (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
index f56ab201b7cef33fa0eb86d6427638574ab1fe9d..8f501d8113e258d01b6ea2dd7724f20be93da678 100644 (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
index 44f2a0cbb28c1cb240990e067fcc78bfd6464d7c..058d4a36b28fec867332b384a51d2402c6f945b2 100644 (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
index 8ddb776b1f3b332dd2ea67247bc82aead00ce4a8..65d1e6ba752abfddc68ea1629d4ea1af76d90631 100644 (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
index 58b3623280d6ed849bb1a47c37b333d1da2a26a3..c105ee2a1402d170493a80a8e3900891df4627a0 100644 (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
index 7c59dabcff0f000012fe288267ef96c2fe27eeb5..d22f489570fd13a947a014e2aa97bb8a608694d8 100644 (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
index 8ee27381136954e3ec4a2d214bd364c6da185890..34107bac0384bff535c833d907e423152555ead4 100644 (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
index cf869cd5a3039ca18d6420e4d7b1f5d21a71dd14..e427ea65690cbf4f450a390e1544190f17d8b113 100644 (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
index 7226d3123490d049797b5354bd5d9249995bc3df..632115d72e300a477043242742713e3c5fc6f09d 100644 (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
index a5144f15e43c13ab9018c265598aae58e1c7b0a4..0dd6b1d454cb87519f65dbadadd32b284798b58f 100644 (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
diff --git a/js/tests/unit/base-component.spec.js b/js/tests/unit/base-component.spec.js
new file mode 100644 (file)
index 0000000..7a849be
--- /dev/null
@@ -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)
+      })
+    })
+  })
+})
index 11b6f7fa49fbd8e0453b33d115d7108c2f8b0692..a7c1c28982efcd04cff7beac12f67988e3dc627e 100644 (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')