]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Fix `this` reference for JavaScript functions (#38725)
authorNathan Sarang-Walters <nwalters512@gmail.com>
Fri, 19 Jul 2024 05:05:21 +0000 (22:05 -0700)
committerGitHub <noreply@github.com>
Fri, 19 Jul 2024 05:05:21 +0000 (07:05 +0200)
js/src/dropdown.js
js/src/tooltip.js
js/src/util/index.js
js/src/util/template-factory.js
js/tests/unit/dropdown.spec.js
js/tests/unit/popover.spec.js
js/tests/unit/tooltip.spec.js
js/tests/unit/util/index.spec.js

index 9190b3ed5704584f1a49eeca9ee0d2ac86feaa29..96094a3e6577ef5166862edd1e9c542e7550a62e 100644 (file)
@@ -320,7 +320,7 @@ class Dropdown extends BaseComponent {
 
     return {
       ...defaultBsPopperConfig,
-      ...execute(this._config.popperConfig, [defaultBsPopperConfig])
+      ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])
     }
   }
 
index 6a6cfeff218bf02d7313e5c6dbb7fef0c81d9884..92d455349336050d374e9e0d7ab878c51153b02d 100644 (file)
@@ -392,7 +392,7 @@ class Tooltip extends BaseComponent {
   }
 
   _resolvePossibleFunction(arg) {
-    return execute(arg, [this._element])
+    return execute(arg, [this._element, this._element])
   }
 
   _getPopperConfig(attachment) {
@@ -438,7 +438,7 @@ class Tooltip extends BaseComponent {
 
     return {
       ...defaultBsPopperConfig,
-      ...execute(this._config.popperConfig, [defaultBsPopperConfig])
+      ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])
     }
   }
 
index 2adf19e3b19a735f89696c909990361da3c80e61..c271cc5368ca889a569eb6c26b6933f4b56c662c 100644 (file)
@@ -223,7 +223,7 @@ const defineJQueryPlugin = plugin => {
 }
 
 const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {
-  return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue
+  return typeof possibleCallback === 'function' ? possibleCallback.call(...args) : defaultValue
 }
 
 const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
index f73589bcc7851cdae63b7459e633a8815120a53a..6d1532b4d4bb54bbcdf24322900356b273bf494f 100644 (file)
@@ -143,7 +143,7 @@ class TemplateFactory extends Config {
   }
 
   _resolvePossibleFunction(arg) {
-    return execute(arg, [this])
+    return execute(arg, [undefined, this])
   }
 
   _putElementInTemplate(element, templateElement) {
index 156005588dbe0752ff3ec585bfa84e7da3c3cf52..63ae4bd102bc0e4ce8d8eeca93d570f73ae235b6 100644 (file)
@@ -172,7 +172,10 @@ describe('Dropdown', () => {
 
       const popperConfig = dropdown._getPopperConfig()
 
-      expect(getPopperConfig).toHaveBeenCalled()
+      // Ensure that the function was called with the default config.
+      expect(getPopperConfig).toHaveBeenCalledWith(jasmine.objectContaining({
+        placement: jasmine.any(String)
+      }))
       expect(popperConfig.placement).toEqual('left')
     })
   })
index 53dc7d89ea6d28446de14daf92af314ab4ed426e..ba38ebe0662910268726bd016746646b65e7bce2 100644 (file)
@@ -95,6 +95,60 @@ describe('Popover', () => {
       })
     })
 
+    it('should call content and title functions with trigger element', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" data-foo="bar">BS twitter</a>'
+
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl, {
+          title(el) {
+            return el.dataset.foo
+          },
+          content(el) {
+            return el.dataset.foo
+          }
+        })
+
+        popoverEl.addEventListener('shown.bs.popover', () => {
+          const popoverDisplayed = document.querySelector('.popover')
+
+          expect(popoverDisplayed).not.toBeNull()
+          expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('bar')
+          expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('bar')
+          resolve()
+        })
+
+        popover.show()
+      })
+    })
+
+    it('should call content and title functions with correct this value', () => {
+      return new Promise(resolve => {
+        fixtureEl.innerHTML = '<a href="#" data-foo="bar">BS twitter</a>'
+
+        const popoverEl = fixtureEl.querySelector('a')
+        const popover = new Popover(popoverEl, {
+          title() {
+            return this.dataset.foo
+          },
+          content() {
+            return this.dataset.foo
+          }
+        })
+
+        popoverEl.addEventListener('shown.bs.popover', () => {
+          const popoverDisplayed = document.querySelector('.popover')
+
+          expect(popoverDisplayed).not.toBeNull()
+          expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('bar')
+          expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('bar')
+          resolve()
+        })
+
+        popover.show()
+      })
+    })
+
     it('should show a popover with just content without having header', () => {
       return new Promise(resolve => {
         fixtureEl.innerHTML = '<a href="#">Nice link</a>'
index ceb8de41e1a06a63a390f1cbe5da0936199860a9..37f2c230d037f5d71f4b0acd14af5484e909c5cf 100644 (file)
@@ -177,7 +177,10 @@ describe('Tooltip', () => {
 
       const popperConfig = tooltip._getPopperConfig('top')
 
-      expect(getPopperConfig).toHaveBeenCalled()
+      // Ensure that the function was called with the default config.
+      expect(getPopperConfig).toHaveBeenCalledWith(jasmine.objectContaining({
+        placement: jasmine.any(String)
+      }))
       expect(popperConfig.placement).toEqual('left')
     })
 
@@ -919,10 +922,12 @@ describe('Tooltip', () => {
 
     it('should show a tooltip with custom class provided as a function in config', () => {
       return new Promise(resolve => {
-        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
+        fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-class-a="custom-class-a" data-class-b="custom-class-b"></a>'
 
-        const spy = jasmine.createSpy('customClass').and.returnValue('custom-class')
         const tooltipEl = fixtureEl.querySelector('a')
+        const spy = jasmine.createSpy('customClass').and.callFake(function (el) {
+          return `${el.dataset.classA} ${this.dataset.classB}`
+        })
         const tooltip = new Tooltip(tooltipEl, {
           customClass: spy
         })
@@ -931,7 +936,8 @@ describe('Tooltip', () => {
           const tip = document.querySelector('.tooltip')
           expect(tip).not.toBeNull()
           expect(spy).toHaveBeenCalled()
-          expect(tip).toHaveClass('custom-class')
+          expect(tip).toHaveClass('custom-class-a')
+          expect(tip).toHaveClass('custom-class-b')
           resolve()
         })
 
@@ -1337,6 +1343,32 @@ describe('Tooltip', () => {
 
       expect(tooltip._getTitle()).toEqual('test')
     })
+
+    it('should call title function with trigger element', () => {
+      fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-foo="bar"></a>'
+
+      const tooltipEl = fixtureEl.querySelector('a')
+      const tooltip = new Tooltip(tooltipEl, {
+        title(el) {
+          return el.dataset.foo
+        }
+      })
+
+      expect(tooltip._getTitle()).toEqual('bar')
+    })
+
+    it('should call title function with correct this value', () => {
+      fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-foo="bar"></a>'
+
+      const tooltipEl = fixtureEl.querySelector('a')
+      const tooltip = new Tooltip(tooltipEl, {
+        title() {
+          return this.dataset.foo
+        }
+      })
+
+      expect(tooltip._getTitle()).toEqual('bar')
+    })
   })
 
   describe('getInstance', () => {
index 4065a91680321b2c4fcb968272a48cb9366549f9..9e154818f288770180653bbd033b96fed33b40d0 100644 (file)
@@ -521,10 +521,10 @@ describe('Util', () => {
 
     it('should execute if arg is function & return the result', () => {
       const functionFoo = (num1, num2 = 10) => num1 + num2
-      const resultFoo = Util.execute(functionFoo, [4, 5])
+      const resultFoo = Util.execute(functionFoo, [undefined, 4, 5])
       expect(resultFoo).toBe(9)
 
-      const resultFoo1 = Util.execute(functionFoo, [4])
+      const resultFoo1 = Util.execute(functionFoo, [undefined, 4])
       expect(resultFoo1).toBe(14)
 
       const functionBar = () => 'foo'