]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Update to popper.js v2.x
authorJohann-S <johann.servoire@gmail.com>
Fri, 19 Jun 2020 08:17:01 +0000 (11:17 +0300)
committerXhmikosR <xhmikosr@gmail.com>
Sun, 6 Dec 2020 16:42:40 +0000 (18:42 +0200)
26 files changed:
build/build-plugins.js
build/generate-sri.js
build/rollup.config.js
config.yml
js/src/dropdown.js
js/src/tooltip.js
js/tests/integration/bundle-modularity.js
js/tests/integration/bundle.js
js/tests/integration/rollup.bundle.js
js/tests/karma.conf.js
js/tests/unit/dropdown.spec.js
js/tests/unit/tooltip.spec.js
js/tests/visual/dropdown.html
js/tests/visual/popover.html
js/tests/visual/tooltip.html
nuget/bootstrap.nuspec
nuget/bootstrap.sass.nuspec
package-lock.json
package.json
scss/_dropdown.scss
scss/_popover.scss
scss/_tooltip.scss
site/content/docs/5.0/components/dropdowns.md
site/content/docs/5.0/components/tooltips.md
site/content/docs/5.0/getting-started/webpack.md
site/content/docs/5.0/migration.md

index 7deda49b1248d2a4eca3047d2557f446fbe519f5..78f76622a410cd4e629cdc923e6ff25566ec9461 100644 (file)
@@ -14,6 +14,7 @@ const rollup = require('rollup')
 const { babel } = require('@rollup/plugin-babel')
 const banner = require('./banner.js')
 
+const rootPath = path.resolve(__dirname, '../js/dist/')
 const plugins = [
   babel({
     // Only transpile our source code
@@ -39,7 +40,6 @@ const bsPlugins = {
   Toast: path.resolve(__dirname, '../js/src/toast.js'),
   Tooltip: path.resolve(__dirname, '../js/src/tooltip.js')
 }
-const rootPath = path.resolve(__dirname, '../js/dist/')
 
 const defaultPluginConfig = {
   external: [
@@ -87,9 +87,9 @@ const getConfigByPluginKey = pluginKey => {
 
   if (pluginKey === 'Dropdown' || pluginKey === 'Tooltip') {
     const config = Object.assign(defaultPluginConfig)
-    config.external.push(bsPlugins.Manipulator, 'popper.js')
+    config.external.push(bsPlugins.Manipulator, '@popperjs/core')
     config.globals[bsPlugins.Manipulator] = 'Manipulator'
-    config.globals['popper.js'] = 'Popper'
+    config.globals['@popperjs/core'] = 'Popper'
     return config
   }
 
index 0c272ceadd664ceb098052dfaa5e07b78ac50cd7..8a2e59360ad1f7565d14d6c1fc1ab52db5f160da 100644 (file)
@@ -42,7 +42,7 @@ const files = [
     configPropertyName: 'js_bundle_hash'
   },
   {
-    file: 'node_modules/popper.js/dist/umd/popper.min.js',
+    file: 'node_modules/@popperjs/core/dist/umd/popper.min.js',
     configPropertyName: 'popper_hash'
   }
 ]
index 05579d165a8f8a8bb14b3678d7658f07ae8f3767..7f9c1c7e11bf755fccc43a8df64931fe94b4bcfc 100644 (file)
@@ -3,13 +3,14 @@
 const path = require('path')
 const { babel } = require('@rollup/plugin-babel')
 const { nodeResolve } = require('@rollup/plugin-node-resolve')
+const replace = require('@rollup/plugin-replace')
 const banner = require('./banner.js')
 
 const BUNDLE = process.env.BUNDLE === 'true'
 const ESM = process.env.ESM === 'true'
 
 let fileDest = `bootstrap${ESM ? '.esm' : ''}`
-const external = ['popper.js']
+const external = ['@popperjs/core']
 const plugins = [
   babel({
     // Only transpile our source code
@@ -19,15 +20,15 @@ const plugins = [
   })
 ]
 const globals = {
-  'popper.js': 'Popper'
+  '@popperjs/core': 'Popper'
 }
 
 if (BUNDLE) {
   fileDest += '.bundle'
   // Remove last entry in external array to bundle Popper
   external.pop()
-  delete globals['popper.js']
-  plugins.push(nodeResolve())
+  delete globals['@popperjs/core']
+  plugins.push(replace({ 'process.env.NODE_ENV': '"production"' }), nodeResolve())
 }
 
 const rollupConfig = {
index d109da5213843349099031e85e97244d41a3f6a1..2bdc700bce3ba90d4c210fa1ff580427e3569114 100644 (file)
@@ -75,5 +75,5 @@ params:
     js_hash:          "sha384-supZtwqjyYg6XvvTCi4/w6J6Hm6IKqXaaeoyGhIhonCdkSvA70sSucW7OqXIo4lZ"
     js_bundle:        "https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-alpha3/dist/js/bootstrap.bundle.min.js"
     js_bundle_hash:   "sha384-G/J8d6sz9bTod37AsZzNtTwT77J24FKjJEO1YsU2vW7iPcmYP3/tznu+LcK824t9"
-    popper:           "https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"
-    popper_hash:      "sha384-9/reFTGAW83EW2RDu2S0VKaIzap3H66lZH81PoYlFhbGU+6BZp6G7niu735Sk7lN"
+    popper:           "https://cdn.jsdelivr.net/npm/@popperjs/core@2.4.2/dist/umd/popper.min.js"
+    popper_hash:      "sha384-a46n7BtEJaPKKs2SeVxZzwKkapYzBUr8c7DyCLEpkRrs4LE03nlh53ZSOPgkJB7U"
index 0ac108ab8110b2287e437b2acffdf591100128fa..04c299600e3d700323258e72e1013f9aae30af54 100644 (file)
@@ -5,6 +5,8 @@
  * --------------------------------------------------------------------------
  */
 
+import * as Popper from '@popperjs/core'
+
 import {
   getjQuery,
   onDOMContentLoaded,
@@ -18,7 +20,6 @@ import {
 import Data from './dom/data'
 import EventHandler from './dom/event-handler'
 import Manipulator from './dom/manipulator'
-import Popper from 'popper.js'
 import SelectorEngine from './dom/selector-engine'
 import BaseComponent from './base-component'
 
@@ -58,7 +59,6 @@ const CLASS_NAME_DROPEND = 'dropend'
 const CLASS_NAME_DROPSTART = 'dropstart'
 const CLASS_NAME_MENUEND = 'dropdown-menu-end'
 const CLASS_NAME_NAVBAR = 'navbar'
-const CLASS_NAME_POSITION_STATIC = 'position-static'
 
 const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]'
 const SELECTOR_FORM_CHILD = '.dropdown form'
@@ -76,7 +76,7 @@ const PLACEMENT_LEFT = isRTL ? 'right-start' : 'left-start'
 const Default = {
   offset: 0,
   flip: true,
-  boundary: 'scrollParent',
+  boundary: 'clippingParents',
   reference: 'toggle',
   display: 'dynamic',
   popperConfig: null
@@ -176,14 +176,7 @@ class Dropdown extends BaseComponent {
         }
       }
 
-      // If boundary is not `scrollParent`, then set position to `static`
-      // to allow the menu to "escape" the scroll parent's boundaries
-      // https://github.com/twbs/bootstrap/issues/24251
-      if (this._config.boundary !== 'scrollParent') {
-        parent.classList.add(CLASS_NAME_POSITION_STATIC)
-      }
-
-      this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())
+      this._popper = Popper.createPopper(referenceElement, this._menu, this._getPopperConfig())
     }
 
     // If this is a touch-enabled device we add extra
@@ -233,6 +226,7 @@ class Dropdown extends BaseComponent {
     super.dispose()
     EventHandler.off(this._element, EVENT_KEY)
     this._menu = null
+
     if (this._popper) {
       this._popper.destroy()
       this._popper = null
@@ -242,7 +236,7 @@ class Dropdown extends BaseComponent {
   update() {
     this._inNavbar = this._detectNavbar()
     if (this._popper) {
-      this._popper.scheduleUpdate()
+      this._popper.update()
     }
   }
 
@@ -296,44 +290,24 @@ class Dropdown extends BaseComponent {
     return Boolean(this._element.closest(`.${CLASS_NAME_NAVBAR}`))
   }
 
-  _getOffset() {
-    const offset = {}
-
-    if (typeof this._config.offset === 'function') {
-      offset.fn = data => {
-        data.offsets = {
-          ...data.offsets,
-          ...(this._config.offset(data.offsets, this._element) || {})
-        }
-
-        return data
-      }
-    } else {
-      offset.offset = this._config.offset
-    }
-
-    return offset
-  }
-
   _getPopperConfig() {
     const popperConfig = {
       placement: this._getPlacement(),
-      modifiers: {
-        offset: this._getOffset(),
-        flip: {
-          enabled: this._config.flip
-        },
-        preventOverflow: {
-          boundariesElement: this._config.boundary
+      modifiers: [{
+        name: 'preventOverflow',
+        options: {
+          altBoundary: this._config.flip,
+          rootBoundary: this._config.boundary
         }
-      }
+      }]
     }
 
     // Disable Popper if we have a static display
     if (this._config.display === 'static') {
-      popperConfig.modifiers.applyStyle = {
+      popperConfig.modifiers = [{
+        name: 'applyStyles',
         enabled: false
-      }
+      }]
     }
 
     return {
index 17148ed9a644d6cc51f8063b70cf66e941892132..dc2f83a8a529dc8ceeffb18d32b17175dc4bf076 100644 (file)
@@ -5,6 +5,8 @@
  * --------------------------------------------------------------------------
  */
 
+import * as Popper from '@popperjs/core'
+
 import {
   getjQuery,
   onDOMContentLoaded,
@@ -25,7 +27,6 @@ import {
 import Data from './dom/data'
 import EventHandler from './dom/event-handler'
 import Manipulator from './dom/manipulator'
-import Popper from 'popper.js'
 import SelectorEngine from './dom/selector-engine'
 import BaseComponent from './base-component'
 
@@ -51,9 +52,8 @@ const DefaultType = {
   html: 'boolean',
   selector: '(string|boolean)',
   placement: '(string|function)',
-  offset: '(number|string|function)',
   container: '(string|element|boolean)',
-  fallbackPlacement: '(string|array)',
+  fallbackPlacements: '(null|array)',
   boundary: '(string|element)',
   customClass: '(string|function)',
   sanitize: 'boolean',
@@ -82,10 +82,9 @@ const Default = {
   html: false,
   selector: false,
   placement: 'top',
-  offset: 0,
   container: false,
-  fallbackPlacement: 'flip',
-  boundary: 'scrollParent',
+  fallbackPlacements: null,
+  boundary: 'clippingParents',
   customClass: '',
   sanitize: true,
   sanitizeFn: null,
@@ -287,7 +286,7 @@ class Tooltip extends BaseComponent {
 
       EventHandler.trigger(this._element, this.constructor.Event.INSERTED)
 
-      this._popper = new Popper(this._element, tip, this._getPopperConfig(attachment))
+      this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
 
       tip.classList.add(CLASS_NAME_SHOW)
 
@@ -307,13 +306,9 @@ class Tooltip extends BaseComponent {
       }
 
       const complete = () => {
-        if (this.config.animation) {
-          this._fixTransition()
-        }
-
         const prevHoverState = this._hoverState
-        this._hoverState = null
 
+        this._hoverState = null
         EventHandler.trigger(this._element, this.constructor.Event.SHOWN)
 
         if (prevHoverState === HOVER_STATE_OUT) {
@@ -345,7 +340,11 @@ class Tooltip extends BaseComponent {
       this._cleanTipClass()
       this._element.removeAttribute('aria-describedby')
       EventHandler.trigger(this._element, this.constructor.Event.HIDDEN)
-      this._popper.destroy()
+
+      if (this._popper) {
+        this._popper.destroy()
+        this._popper = null
+      }
     }
 
     const hideEvent = EventHandler.trigger(this._element, this.constructor.Event.HIDE)
@@ -380,7 +379,7 @@ class Tooltip extends BaseComponent {
 
   update() {
     if (this._popper !== null) {
-      this._popper.scheduleUpdate()
+      this._popper.update()
     }
   }
 
@@ -469,26 +468,45 @@ class Tooltip extends BaseComponent {
   // Private
 
   _getPopperConfig(attachment) {
+    const flipModifier = {
+      name: 'flip',
+      options: {
+        altBoundary: true
+      }
+    }
+
+    if (this.config.fallbackPlacements) {
+      flipModifier.options.fallbackPlacements = this.config.fallbackPlacements
+    }
+
     const defaultBsConfig = {
       placement: attachment,
-      modifiers: {
-        offset: this._getOffset(),
-        flip: {
-          behavior: this.config.fallbackPlacement
+      modifiers: [
+        flipModifier,
+        {
+          name: 'preventOverflow',
+          options: {
+            rootBoundary: this.config.boundary
+          }
         },
-        arrow: {
-          element: `.${this.constructor.NAME}-arrow`
+        {
+          name: 'arrow',
+          options: {
+            element: `.${this.constructor.NAME}-arrow`
+          }
         },
-        preventOverflow: {
-          boundariesElement: this.config.boundary
+        {
+          name: 'onChange',
+          enabled: true,
+          phase: 'afterWrite',
+          fn: data => this._handlePopperPlacementChange(data)
         }
-      },
-      onCreate: data => {
-        if (data.originalPlacement !== data.placement) {
+      ],
+      onFirstUpdate: data => {
+        if (data.options.placement !== data.placement) {
           this._handlePopperPlacementChange(data)
         }
-      },
-      onUpdate: data => this._handlePopperPlacementChange(data)
+      }
     }
 
     return {
@@ -501,25 +519,6 @@ class Tooltip extends BaseComponent {
     this.getTipElement().classList.add(`${CLASS_PREFIX}-${this.updateAttachment(attachment)}`)
   }
 
-  _getOffset() {
-    const offset = {}
-
-    if (typeof this.config.offset === 'function') {
-      offset.fn = data => {
-        data.offsets = {
-          ...data.offsets,
-          ...(this.config.offset(data.offsets, this._element) || {})
-        }
-
-        return data
-      }
-    } else {
-      offset.offset = this.config.offset
-    }
-
-    return offset
-  }
-
   _getContainer() {
     if (this.config.container === false) {
       return document.body
@@ -743,23 +742,15 @@ class Tooltip extends BaseComponent {
   }
 
   _handlePopperPlacementChange(popperData) {
-    this.tip = popperData.instance.popper
-    this._cleanTipClass()
-    this._addAttachmentClass(this._getAttachment(popperData.placement))
-  }
+    const { state } = popperData
 
-  _fixTransition() {
-    const tip = this.getTipElement()
-    const initConfigAnimation = this.config.animation
-    if (tip.getAttribute('x-placement') !== null) {
+    if (!state) {
       return
     }
 
-    tip.classList.remove(CLASS_NAME_FADE)
-    this.config.animation = false
-    this.hide()
-    this.show()
-    this.config.animation = initConfigAnimation
+    this.tip = state.elements.popper
+    this._cleanTipClass()
+    this._addAttachmentClass(this._getAttachment(state.placement))
   }
 
   // Static
index 003f840210d2ee2d3f86af86ff01f5b60db89d8e..8546141b195544fbed5a0ea23c5cdaa6005080f6 100644 (file)
@@ -1,5 +1,5 @@
-import 'popper.js'
 import Tooltip from '../../dist/tooltip'
+import '../../dist/carousel'
 
 window.addEventListener('load', () => {
   [].concat(...document.querySelectorAll('[data-bs-toggle="tooltip"]'))
index 75982f76f9baea97727c912c295df46111fb34d7..452088a7d811718dcb98572e1ce3f855860849f0 100644 (file)
@@ -1,4 +1,3 @@
-import 'popper.js'
 import { Tooltip } from '../../../dist/js/bootstrap.esm.js'
 
 window.addEventListener('load', () => {
index 9e2ed26c1c036b82779b77a6f74753a8efd2f69c..288f40961d42f98500d681225118cf1f1f54714b 100644 (file)
@@ -2,6 +2,7 @@
 
 const { babel } = require('@rollup/plugin-babel')
 const { nodeResolve } = require('@rollup/plugin-node-resolve')
+const replace = require('@rollup/plugin-replace')
 
 module.exports = {
   input: 'js/tests/integration/bundle.js',
@@ -10,6 +11,9 @@ module.exports = {
     format: 'iife'
   },
   plugins: [
+    replace({
+      'process.env.NODE_ENV': '"production"'
+    }),
     nodeResolve(),
     babel({
       exclude: 'node_modules/**',
index 0728a8cfa42de5977577ff3411d5569bc613c6e3..d0dd8bdd9033918c157bda168e87406c969fc90a 100644 (file)
@@ -5,6 +5,7 @@ const ip = require('ip')
 const { babel } = require('@rollup/plugin-babel')
 const istanbul = require('rollup-plugin-istanbul')
 const { nodeResolve } = require('@rollup/plugin-node-resolve')
+const replace = require('@rollup/plugin-replace')
 
 const {
   browsers,
@@ -74,6 +75,9 @@ const conf = {
   },
   rollupPreprocessor: {
     plugins: [
+      replace({
+        'process.env.NODE_ENV': '"dev"'
+      }),
       istanbul({
         exclude: [
           'node_modules/**',
index f6a5feb1b93e2320ea6e9b2edf1fb2d7256a336b..d2171f3697d7724e2b371a78d39c8960b9769828 100644 (file)
@@ -1,5 +1,3 @@
-import Popper from 'popper.js'
-
 import Dropdown from '../../src/dropdown'
 import EventHandler from '../../src/dom/event-handler'
 
@@ -36,50 +34,6 @@ describe('Dropdown', () => {
   })
 
   describe('constructor', () => {
-    it('should create offset modifier correctly when offset option is a function', () => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const getOffset = offsets => offsets
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown, {
-        offset: getOffset
-      })
-
-      const offset = dropdown._getOffset()
-
-      expect(offset.offset).toBeUndefined()
-      expect(typeof offset.fn).toEqual('function')
-    })
-
-    it('should create offset modifier correctly when offset option is not a function', () => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const myOffset = 7
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdown = new Dropdown(btnDropdown, {
-        offset: myOffset
-      })
-
-      const offset = dropdown._getOffset()
-
-      expect(offset.offset).toEqual(myOffset)
-      expect(offset.fn).toBeUndefined()
-    })
-
     it('should add a listener on trigger which do not have data-bs-toggle="dropdown"', () => {
       fixtureEl.innerHTML = [
         '<div class="dropdown">',
@@ -860,14 +814,11 @@ describe('Dropdown', () => {
       expect(dropdown._menu).toBeDefined()
       expect(dropdown._element).toBeDefined()
 
-      spyOn(Popper.prototype, 'destroy')
-
       dropdown.dispose()
 
       expect(dropdown._popper).toBeNull()
       expect(dropdown._menu).toBeNull()
       expect(dropdown._element).toBeNull()
-      expect(Popper.prototype.destroy).toHaveBeenCalled()
     })
   })
 
@@ -889,12 +840,12 @@ describe('Dropdown', () => {
 
       expect(dropdown._popper).toBeDefined()
 
-      spyOn(dropdown._popper, 'scheduleUpdate')
+      spyOn(dropdown._popper, 'update')
       spyOn(dropdown, '_detectNavbar')
 
       dropdown.update()
 
-      expect(dropdown._popper.scheduleUpdate).toHaveBeenCalled()
+      expect(dropdown._popper.update).toHaveBeenCalled()
       expect(dropdown._detectNavbar).toHaveBeenCalled()
     })
 
@@ -921,48 +872,6 @@ describe('Dropdown', () => {
   })
 
   describe('data-api', () => {
-    it('should not add class position-static to dropdown if boundary not set', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownEl = fixtureEl.querySelector('.dropdown')
-
-      dropdownEl.addEventListener('shown.bs.dropdown', () => {
-        expect(dropdownEl.classList.contains('position-static')).toEqual(false)
-        done()
-      })
-
-      btnDropdown.click()
-    })
-
-    it('should add class position-static to dropdown if boundary not scrollParent', done => {
-      fixtureEl.innerHTML = [
-        '<div class="dropdown">',
-        '  <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-boundary="viewport">Dropdown</button>',
-        '  <div class="dropdown-menu">',
-        '    <a class="dropdown-item" href="#">Secondary link</a>',
-        '  </div>',
-        '</div>'
-      ].join('')
-
-      const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-      const dropdownEl = fixtureEl.querySelector('.dropdown')
-
-      dropdownEl.addEventListener('shown.bs.dropdown', () => {
-        expect(dropdownEl.classList.contains('position-static')).toEqual(true)
-        done()
-      })
-
-      btnDropdown.click()
-    })
-
     it('should show and hide a dropdown', done => {
       fixtureEl.innerHTML = [
         '<div class="dropdown">',
index 9ea9096de984affd3f77588799c6dd62f8598c72..9fc306dfe174327cc49b7d5710d253e161efbb81 100644 (file)
@@ -483,24 +483,6 @@ describe('Tooltip', () => {
       tooltip.show()
     })
 
-    it('should show a tooltip with offset as a function', done => {
-      fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
-
-      const spy = jasmine.createSpy('offset').and.returnValue({})
-      const tooltipEl = fixtureEl.querySelector('a')
-      const tooltip = new Tooltip(tooltipEl, {
-        offset: spy
-      })
-
-      tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        expect(document.querySelector('.tooltip')).toBeDefined()
-        expect(spy).toHaveBeenCalled()
-        done()
-      })
-
-      tooltip.show()
-    })
-
     it('should show a tooltip without the animation', done => {
       fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
@@ -789,18 +771,18 @@ describe('Tooltip', () => {
   })
 
   describe('update', () => {
-    it('should call popper schedule update', done => {
+    it('should call popper update', done => {
       fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
 
       const tooltipEl = fixtureEl.querySelector('a')
       const tooltip = new Tooltip(tooltipEl)
 
       tooltipEl.addEventListener('shown.bs.tooltip', () => {
-        spyOn(tooltip._popper, 'scheduleUpdate')
+        spyOn(tooltip._popper, 'update')
 
         tooltip.update()
 
-        expect(tooltip._popper.scheduleUpdate).toHaveBeenCalled()
+        expect(tooltip._popper.update).toHaveBeenCalled()
         done()
       })
 
index 930940a157ee10d5afedcb87c7148155842721d6..374e46e1aed9d9f8da5527217d5c3d37afc33ab5 100644 (file)
@@ -78,7 +78,9 @@
               <li><a class="dropdown-item" href="#">Something else here</a></li>
             </ul>
           </div>
+        </div>
 
+        <div class="col-sm-12 mt-4">
           <div class="btn-group dropup">
             <button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropup</button>
             <ul class="dropdown-menu">
       </div>
 
       <div class="row">
-        <div class="col-sm-3 mt-4">
-          <div class="btn-group dropdown">
-            <button type="button" class="btn btn-secondary dropdown-toggle" data-bs-toggle="dropdown" data-bs-offset="10,20">Dropdown offset</button>
-            <ul class="dropdown-menu">
-              <li><a class="dropdown-item" href="#">Action</a></li>
-              <li><a class="dropdown-item" href="#">Another action</a></li>
-              <li><a class="dropdown-item" href="#">Something else here</a></li>
-            </ul>
-          </div>
-        </div>
         <div class="col-sm-3 mt-4">
           <div class="btn-group dropdown">
             <button type="button" class="btn btn-secondary">Dropdown reference</button>
           </div>
         </div>
       </div>
-
     </div>
 
-    <script src="../../../node_modules/popper.js/dist/umd/popper.min.js"></script>
+    <script src="../../../node_modules/@popperjs/core/dist/umd/popper.min.js"></script>
     <script src="../../dist/dom/event-handler.js"></script>
     <script src="../../dist/dom/selector-engine.js"></script>
     <script src="../../dist/dom/data.js"></script>
index c75825396949450f30d28b6acad4abd414ec7768..ddc068708dd6cc882900aaee83acc302c3188246 100644 (file)
@@ -31,7 +31,7 @@
       </button>
     </div>
 
-    <script src="../../../node_modules/popper.js/dist/umd/popper.min.js"></script>
+    <script src="../../../node_modules/@popperjs/core/dist/umd/popper.min.js"></script>
     <script src="../../dist/dom/event-handler.js"></script>
     <script src="../../dist/dom/selector-engine.js"></script>
     <script src="../../dist/dom/manipulator.js"></script>
index bade26a6c09b9b219a3ec544717b419a0806c0ed..36f0a70575aa068e099125cc0779ca4a36570bb3 100644 (file)
@@ -68,7 +68,7 @@
       <div id="customContainer"></div>
     </div>
 
-    <script src="../../../node_modules/popper.js/dist/umd/popper.min.js"></script>
+    <script src="../../../node_modules/@popperjs/core/dist/umd/popper.min.js"></script>
     <script src="../../dist/dom/selector-engine.js"></script>
     <script src="../../dist/dom/event-handler.js"></script>
     <script src="../../dist/dom/manipulator.js"></script>
index 2f8696d41bb987def046b0d703bf0bf56d1a0888..50888a0b775fca1473cfdcd72adb4969f66ec3b7 100644 (file)
@@ -17,7 +17,7 @@
     <copyright>Copyright 2017-2020</copyright>
     <requireLicenseAcceptance>false</requireLicenseAcceptance>
     <dependencies>
-      <dependency id="popper.js" version="[1.16.1,2)" />
+      <dependency id="@popperjs/core" version="[2.4.2,3)" />
     </dependencies>
     <tags>css mobile-first responsive front-end framework web</tags>
     <contentFiles>
index 99d406ffa43757eb78e10a8704458be8c911e3c3..78ee63e737885041607a6c1adfff8253417331b2 100644 (file)
@@ -17,7 +17,7 @@
     <copyright>Copyright 2017-2020</copyright>
     <requireLicenseAcceptance>false</requireLicenseAcceptance>
     <dependencies>
-      <dependency id="popper.js" version="[1.16.1,2)" />
+      <dependency id="@popperjs/core" version="[2.4.2,3)" />
     </dependencies>
     <tags>css sass mobile-first responsive front-end framework web</tags>
     <contentFiles>
index 3d7aab102591f68fa1fba15bf410f4a2b6ed3de4..f029e4f39bdd817f76357fed11fd473125ab00e2 100644 (file)
         "fastq": "^1.6.0"
       }
     },
+    "@popperjs/core": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.4.2.tgz",
+      "integrity": "sha512-JlGTGRYHC2QK+DDbePyXdBdooxFq2+noLfWpRqJtkxcb/oYWzOF0kcbfvvbWrwevCC1l6hLUg1wHYT+ona5BWQ==",
+      "dev": true
+    },
     "@rollup/plugin-babel": {
       "version": "5.2.2",
       "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.2.tgz",
         "resolve": "^1.19.0"
       }
     },
+    "@rollup/plugin-replace": {
+      "version": "2.3.4",
+      "resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.4.tgz",
+      "integrity": "sha512-waBhMzyAtjCL1GwZes2jaE9MjuQ/DQF2BatH3fRivUF3z0JBFrU0U6iBNC/4WR+2rLKhaAhPWDNPYp4mI6RqdQ==",
+      "dev": true,
+      "requires": {
+        "@rollup/pluginutils": "^3.1.0",
+        "magic-string": "^0.25.7"
+      }
+    },
     "@rollup/pluginutils": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
       "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==",
       "dev": true
     },
-    "popper.js": {
-      "version": "1.16.1",
-      "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
-      "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==",
-      "dev": true
-    },
     "posix-character-classes": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
index a1d125b3141b4e977fcfb4c846a99ea0ad8479fa..df9b5c63d421218ee66622203dae78d96b94e8aa 100644 (file)
   },
   "dependencies": {},
   "peerDependencies": {
-    "popper.js": "^1.16.1"
+    "@popperjs/core": "^2.4.2"
   },
   "devDependencies": {
     "@babel/cli": "^7.12.8",
     "@babel/core": "^7.12.9",
     "@babel/preset-env": "^7.12.7",
+    "@popperjs/core": "^2.4.2",
     "@rollup/plugin-babel": "^5.2.2",
     "@rollup/plugin-commonjs": "^17.0.0",
     "@rollup/plugin-node-resolve": "^11.0.0",
+    "@rollup/plugin-replace": "^2.3.4",
     "autoprefixer": "^10.0.4",
     "bundlewatch": "^0.3.1",
     "clean-css-cli": "^4.3.0",
     "lockfile-lint": "^4.3.7",
     "nodemon": "^2.0.6",
     "npm-run-all": "^4.1.5",
-    "popper.js": "^1.16.1",
     "postcss": "^8.1.10",
     "postcss-cli": "^8.3.0",
     "rollup": "^2.34.0",
     "shim": {
       "js/bootstrap": {
         "deps": [
-          "popper.js"
+          "@popperjs/core"
         ]
       }
     },
     "dependencies": {},
     "peerDependencies": {
-      "popper.js": "^1.16.1"
+      "@popperjs/core": "^2.4.2"
     }
   }
 }
index 7cad640096e7c10ba764cf60f825d8ede1a88f01..9db6937aeea48a1157f1c07ebabf41b9232b2fff 100644 (file)
 // When Popper is enabled, reset the basic dropdown position
 // stylelint-disable-next-line no-duplicate-selectors
 .dropdown-menu {
-  &[x-placement^="top"],
-  &[x-placement^="right"],
-  &[x-placement^="bottom"],
-  &[x-placement^="left"] {
+  &[data-popper-placement^="top"],
+  &[data-popper-placement^="right"],
+  &[data-popper-placement^="bottom"],
+  &[data-popper-placement^="left"] {
     right: auto;
     bottom: auto;
     left: auto;
index 6688f729c270da36b7ace667f5e19d420149d476..36478b33707e2f2292fa00a628b93d2dd875519a 100644 (file)
 }
 
 .bs-popover-auto {
-  &[x-placement^="top"] {
+  &[data-popper-placement^="top"] {
     @extend .bs-popover-top;
   }
-  &[x-placement^="right"] {
+  &[data-popper-placement^="right"] {
     @extend .bs-popover-end;
   }
-  &[x-placement^="bottom"] {
+  &[data-popper-placement^="bottom"] {
     @extend .bs-popover-bottom;
   }
-  &[x-placement^="left"] {
+  &[data-popper-placement^="left"] {
     @extend .bs-popover-start;
   }
 }
index a98ff4db5f26ab0fa51d8e7f815c6c3e8b6cacee..60c860c6746b44125292face5505d6c6cc3c3725 100644 (file)
@@ -36,7 +36,7 @@
     bottom: 0;
 
     &::before {
-      top: 0;
+      top: -1px;
       border-width: $tooltip-arrow-height ($tooltip-arrow-width / 2) 0;
       border-top-color: $tooltip-arrow-color;
     }
@@ -52,7 +52,7 @@
     height: $tooltip-arrow-width;
 
     &::before {
-      right: 0 #{"/* rtl:ignore */"};
+      right: -1px #{"/* rtl:ignore */"};
       border-width: ($tooltip-arrow-width / 2) $tooltip-arrow-height ($tooltip-arrow-width / 2) 0 #{"/* rtl:ignore */"};
       border-right-color: $tooltip-arrow-color #{"/* rtl:ignore */"};
     }
@@ -66,7 +66,7 @@
     top: 0;
 
     &::before {
-      bottom: 0;
+      bottom: -1px;
       border-width: 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height;
       border-bottom-color: $tooltip-arrow-color;
     }
@@ -82,7 +82,7 @@
     height: $tooltip-arrow-width;
 
     &::before {
-      left: 0 #{"/* rtl:ignore */"};
+      left: -1px #{"/* rtl:ignore */"};
       border-width: ($tooltip-arrow-width / 2) 0 ($tooltip-arrow-width / 2) $tooltip-arrow-height #{"/* rtl:ignore */"};
       border-left-color: $tooltip-arrow-color #{"/* rtl:ignore */"};
     }
 }
 
 .bs-tooltip-auto {
-  &[x-placement^="top"] {
+  &[data-popper-placement^="top"] {
     @extend .bs-tooltip-top;
   }
-  &[x-placement^="right"] {
+  &[data-popper-placement^="right"] {
     @extend .bs-tooltip-end;
   }
-  &[x-placement^="bottom"] {
+  &[data-popper-placement^="bottom"] {
     @extend .bs-tooltip-bottom;
   }
-  &[x-placement^="left"] {
+  &[data-popper-placement^="left"] {
     @extend .bs-tooltip-start;
   }
 }
index 69a7783dc073934775d717597dc1d4d2b61a86f2..891c9a01b8547e51ce50c29ba284ab462e12d2cb 100644 (file)
@@ -872,33 +872,23 @@ Options can be passed via data attributes or JavaScript. For data attributes, ap
     </tr>
   </thead>
   <tbody>
-    <tr>
-      <td><code>offset</code></td>
-      <td>number | string | function</td>
-      <td><code>0</code></td>
-      <td>
-        <p>Offset of the dropdown relative to its target.</p>
-        <p>When a function is used to determine the offset, it is called with an object containing the offset data as its first argument. The function must return an object with the same structure. The triggering element DOM node is passed as the second argument.</p>
-        <p>For more information refer to Popper's <a href="https://popper.js.org/docs/v1/#modifiers..offset.offset">offset docs</a>.</p>
-      </td>
-    </tr>
     <tr>
       <td><code>flip</code></td>
       <td>boolean</td>
       <td><code>true</code></td>
-      <td>Allow Dropdown to flip in case of an overlapping on the reference element. For more information refer to Popper's <a href="https://popper.js.org/docs/v1/#modifiers..flip.enabled">flip docs</a>.</td>
+      <td>Allow Dropdown to flip in case of an overlapping on the reference element. For more information refer to Popper's <a href="https://popper.js.org/docs/v2/modifiers/flip/">flip docs</a>.</td>
     </tr>
     <tr>
       <td><code>boundary</code></td>
       <td>string | element</td>
       <td><code>'scrollParent'</code></td>
-      <td>Overflow constraint boundary of the dropdown menu. Accepts the values of <code>'viewport'</code>, <code>'window'</code>, <code>'scrollParent'</code>, or an HTMLElement reference (JavaScript only). For more information refer to Popper's <a href="https://popper.js.org/docs/v1/#modifiers..preventOverflow.boundariesElement">preventOverflow docs</a>.</td>
+      <td>Overflow constraint boundary of the dropdown menu. By default it's <code>'clippingParents'</code> and can accept an HTMLElement reference (JavaScript only). For more information refer to Popper's <a href="https://popper.js.org/docs/v2/utils/detect-overflow/#boundary">preventOverflow docs</a>.</td>
     </tr>
     <tr>
       <td><code>reference</code></td>
       <td>string | element</td>
       <td><code>'toggle'</code></td>
-      <td>Reference element of the dropdown menu. Accepts the values of <code>'toggle'</code>, <code>'parent'</code>, or an HTMLElement reference. For more information refer to Popper's <a href="https://popper.js.org/docs/v1/#referenceObject">referenceObject docs</a>.</td>
+      <td>Reference element of the dropdown menu. Accepts the values of <code>'toggle'</code>, <code>'parent'</code>, or an HTMLElement reference. For more information refer to Popper's <a href="https://popper.js.org/docs/v2/constructors/#createpopper">constructor docs</a>.</td>
     </tr>
     <tr>
       <td><code>display</code></td>
index 2ab684a220044424d89743e8bbd209343c99cec0..e2a45c3edd69938c5449bf0e228e3e91db4135c9 100644 (file)
@@ -248,28 +248,18 @@ Note that for security reasons the `sanitize`, `sanitizeFn`, and `allowList` opt
         <p><code>'hover'</code> on its own will result in tooltips that cannot be triggered via the keyboard, and should only be used if alternative methods for conveying the same information for keyboard users is present.</p>
       </td>
     </tr>
-    <tr>
-      <td><code>offset</code></td>
-      <td>number | string | function</td>
-      <td><code>0</code></td>
-      <td>
-        <p>Offset of the tooltip relative to its target.</p>
-        <p>When a function is used to determine the offset, it is called with an object containing the offset data as its first argument. The function must return an object with the same structure. The triggering element DOM node is passed as the second argument.</p>
-        <p>For more information refer to Popper's <a href="https://popper.js.org/docs/v1/#modifiers..offset.offset">offset docs</a>.</p>
-      </td>
-    </tr>
     <tr>
       <td><code>fallbackPlacement</code></td>
-      <td>string | array</td>
-      <td><code>'flip'</code></td>
+      <td>null | array</td>
+      <td><code>null</code></td>
       <td>Allow to specify which position Popper will use on fallback. For more information refer to
-      Popper's <a href="https://popper.js.org/docs/v1/#modifiers..flip.behavior">behavior docs</a></td>
+      Popper's <a href="https://popper.js.org/docs/v2/modifiers/flip/#fallbackplacements">behavior docs</a></td>
     </tr>
     <tr>
       <td><code>boundary</code></td>
       <td>string | element</td>
-      <td><code>'scrollParent'</code></td>
-      <td>Overflow constraint boundary of the tooltip. Accepts the values of <code>'viewport'</code>, <code>'window'</code>, <code>'scrollParent'</code>, or an HTMLElement reference (JavaScript only). For more information refer to Popper's <a href="https://popper.js.org/docs/v1/#modifiers..preventOverflow.boundariesElement">preventOverflow docs</a>.</td>
+      <td><code>'clippingParents'</code></td>
+      <td>Overflow constraint boundary of the tooltip. By default it's <code>'clippingParents'</code> and can accept an HTMLElement reference (JavaScript only). For more information refer to Popper's <a href="https://popper.js.org/docs/v2/utils/detect-overflow/#boundary">preventOverflow docs</a>.</td>
     </tr>
     <tr>
       <td><code>customClass</code></td>
index 4b9c525dd9cdabeaf90074b11ece42783e5f6291..6998e1e99cdf9cc9ba5827ca0d1dc9c37c66e1ec 100644 (file)
@@ -27,7 +27,7 @@ import Alert from 'bootstrap/js/dist/alert';
 ```
 
 Bootstrap depends on [Popper](https://popper.js.org/), which is specified in the `peerDependencies` property.
-This means that you will have to make sure to add it to your `package.json` using `npm install popper.js`.
+This means that you will have to make sure to add it to your `package.json` using `npm install @popperjs/core`.
 
 ## Importing Styles
 
index da554c3322f305f2a0138f1c75f74e8be85287f1..7eef5504f8e4eacf1887ef7429a66024e160cb59 100644 (file)
@@ -283,6 +283,8 @@ Changes to our source and compiled JavaScript files.
 
 - Dropped jQuery dependency and rewrote plugins to be in regular JavaScript.
 - Removed underscore from public static methods like `_getInstance()` → `getInstance()`.
+- Moved from Popper v1.x to Popper v2.x
+- Removed `offset` option from our Tooltip/Popover and Dropdown plugin, this can still be achieve using `popperConfig` parameter.
 
 ### Color system