]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Scrollbar: respect the initial body overflow value (#33706)
authorGeoSot <geo.sotis@gmail.com>
Sun, 25 Apr 2021 03:50:16 +0000 (06:50 +0300)
committerGitHub <noreply@github.com>
Sun, 25 Apr 2021 03:50:16 +0000 (06:50 +0300)
* add method to handle overflow on body element & tests
* replace duplicated code on modal/offcanvas tests

js/src/util/scrollbar.js
js/tests/helpers/fixture.js
js/tests/unit/modal.spec.js
js/tests/unit/offcanvas.spec.js
js/tests/unit/util/scrollbar.spec.js

index 31b614375603e7722fbcdbc61223ea8222319b2a..352e3e11deb953362518c9c8fd4c08b9c3c9257c 100644 (file)
@@ -18,11 +18,21 @@ const getWidth = () => {
 }
 
 const hide = (width = getWidth()) => {
-  document.body.style.overflow = 'hidden'
+  _disableOverFlow()
+  // give padding to element to balances the hidden scrollbar width
+  _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width)
   // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements, to keep shown fullwidth
   _setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width)
   _setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width)
-  _setElementAttributes('body', 'paddingRight', calculatedValue => calculatedValue + width)
+}
+
+const _disableOverFlow = () => {
+  const actualValue = document.body.style.overflow
+  if (actualValue) {
+    Manipulator.setDataAttribute(document.body, 'overflow', actualValue)
+  }
+
+  document.body.style.overflow = 'hidden'
 }
 
 const _setElementAttributes = (selector, styleProp, callback) => {
@@ -41,10 +51,10 @@ const _setElementAttributes = (selector, styleProp, callback) => {
 }
 
 const reset = () => {
-  document.body.style.overflow = 'auto'
+  _resetElementAttributes('body', 'overflow')
+  _resetElementAttributes('body', 'paddingRight')
   _resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight')
   _resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight')
-  _resetElementAttributes('body', 'paddingRight')
 }
 
 const _resetElementAttributes = (selector, styleProp) => {
index 0bfc26f468bbdad73255399a6e6bb849e14e073e..3d6f395e8fc53ad7367be30cff50d78a6162b63b 100644 (file)
@@ -39,3 +39,12 @@ export const jQueryMock = {
     })
   }
 }
+
+export const clearBodyAndDocument = () => {
+  const attributes = ['data-bs-padding-right', 'style']
+
+  attributes.forEach(attr => {
+    document.documentElement.removeAttribute(attr)
+    document.body.removeAttribute(attr)
+  })
+}
index 624f44dc5d28327da7d4aa9bf71e16bae945ffc8..a09711b349df5daf2409515da0e35136cd7f292e 100644 (file)
@@ -3,7 +3,7 @@ import EventHandler from '../../src/dom/event-handler'
 import { getWidth as getScrollBarWidth } from '../../src/util/scrollbar'
 
 /** Test helpers */
-import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
+import { clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
 
 describe('Modal', () => {
   let fixtureEl
@@ -14,11 +14,8 @@ describe('Modal', () => {
 
   afterEach(() => {
     clearFixture()
-
+    clearBodyAndDocument()
     document.body.classList.remove('modal-open')
-    document.documentElement.removeAttribute('style')
-    document.body.removeAttribute('style')
-    document.body.removeAttribute('data-bs-padding-right')
 
     document.querySelectorAll('.modal-backdrop')
       .forEach(backdrop => {
@@ -27,9 +24,7 @@ describe('Modal', () => {
   })
 
   beforeEach(() => {
-    document.documentElement.removeAttribute('style')
-    document.body.removeAttribute('style')
-    document.body.removeAttribute('data-bs-padding-right')
+    clearBodyAndDocument()
   })
 
   describe('VERSION', () => {
index 2419e5723cc8ea7a9792c04e558f0f81607c824f..30edc2913bfbd2f68d619fb843adf85a93819c4f 100644 (file)
@@ -2,7 +2,7 @@ import Offcanvas from '../../src/offcanvas'
 import EventHandler from '../../src/dom/event-handler'
 
 /** Test helpers */
-import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
+import { clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
 import { isVisible } from '../../src/util'
 
 describe('Offcanvas', () => {
@@ -15,15 +15,11 @@ describe('Offcanvas', () => {
   afterEach(() => {
     clearFixture()
     document.body.classList.remove('offcanvas-open')
-    document.documentElement.removeAttribute('style')
-    document.body.removeAttribute('style')
-    document.body.removeAttribute('data-bs-padding-right')
+    clearBodyAndDocument()
   })
 
   beforeEach(() => {
-    document.documentElement.removeAttribute('style')
-    document.body.removeAttribute('style')
-    document.body.removeAttribute('data-bs-padding-right')
+    clearBodyAndDocument()
   })
 
   describe('VERSION', () => {
index aab3798eea3ec7695da3af447b95d208d6587622..e09a37ce70f7b93cbb485c67b8e4e65c75126381 100644 (file)
@@ -1,8 +1,14 @@
 import * as Scrollbar from '../../../src/util/scrollbar'
-import { clearFixture, getFixture } from '../../helpers/fixture'
+import { clearBodyAndDocument, clearFixture, getFixture } from '../../helpers/fixture'
+import Manipulator from '../../../src/dom/manipulator'
 
 describe('ScrollBar', () => {
   let fixtureEl
+  const parseInt = arg => Number.parseInt(arg, 10)
+  const getRightPadding = el => parseInt(window.getComputedStyle(el).paddingRight)
+  const getOverFlow = el => el.style.overflow
+  const getPaddingAttr = el => Manipulator.getDataAttribute(el, 'padding-right')
+  const getOverFlowAttr = el => Manipulator.getDataAttribute(el, 'overflow')
   const windowCalculations = () => {
     return {
       htmlClient: document.documentElement.clientWidth,
@@ -32,15 +38,11 @@ describe('ScrollBar', () => {
 
   afterEach(() => {
     clearFixture()
-    document.documentElement.removeAttribute('style')
-    document.body.removeAttribute('style')
-    document.body.removeAttribute('data-bs-padding-right')
+    clearBodyAndDocument()
   })
 
   beforeEach(() => {
-    document.documentElement.removeAttribute('style')
-    document.body.removeAttribute('style')
-    document.body.removeAttribute('data-bs-padding-right')
+    clearBodyAndDocument()
   })
 
   describe('isBodyOverflowing', () => {
@@ -180,5 +182,80 @@ describe('ScrollBar', () => {
 
       Scrollbar.reset()
     })
+
+    describe('Body Handling', () => {
+      it('should hide scrollbar and reset it to its initial value', () => {
+        const styleSheetPadding = '7px'
+        fixtureEl.innerHTML = [
+          '<style>',
+          '  body {',
+          `       padding-right: ${styleSheetPadding} }`,
+          '  }',
+          '</style>'
+        ].join('')
+
+        const el = document.body
+        const inlineStylePadding = '10px'
+        el.style.paddingRight = inlineStylePadding
+
+        const originalPadding = getRightPadding(el)
+        expect(originalPadding).toEqual(parseInt(inlineStylePadding)) // Respect only the inline style as it has prevails this of css
+        const originalOverFlow = 'auto'
+        el.style.overflow = originalOverFlow
+        const scrollBarWidth = Scrollbar.getWidth()
+
+        Scrollbar.hide()
+
+        const currentPadding = getRightPadding(el)
+
+        expect(currentPadding).toEqual(scrollBarWidth + originalPadding)
+        expect(currentPadding).toEqual(scrollBarWidth + parseInt(inlineStylePadding))
+        expect(getPaddingAttr(el)).toEqual(inlineStylePadding)
+        expect(getOverFlow(el)).toEqual('hidden')
+        expect(getOverFlowAttr(el)).toEqual(originalOverFlow)
+
+        Scrollbar.reset()
+
+        const currentPadding1 = getRightPadding(el)
+        expect(currentPadding1).toEqual(originalPadding)
+        expect(getPaddingAttr(el)).toEqual(null)
+        expect(getOverFlow(el)).toEqual(originalOverFlow)
+        expect(getOverFlowAttr(el)).toEqual(null)
+      })
+
+      it('should hide scrollbar and reset it to its initial value - respecting css rules', () => {
+        const styleSheetPadding = '7px'
+        fixtureEl.innerHTML = [
+          '<style>',
+          '  body {',
+          `       padding-right: ${styleSheetPadding} }`,
+          '  }',
+          '</style>'
+        ].join('')
+        const el = document.body
+        const originalPadding = getRightPadding(el)
+        const originalOverFlow = 'scroll'
+        el.style.overflow = originalOverFlow
+        const scrollBarWidth = Scrollbar.getWidth()
+
+        Scrollbar.hide()
+
+        const currentPadding = getRightPadding(el)
+
+        expect(currentPadding).toEqual(scrollBarWidth + originalPadding)
+        expect(currentPadding).toEqual(scrollBarWidth + parseInt(styleSheetPadding))
+        expect(getPaddingAttr(el)).toBeNull() // We do not have to keep css padding
+        expect(getOverFlow(el)).toEqual('hidden')
+        expect(getOverFlowAttr(el)).toEqual(originalOverFlow)
+
+        Scrollbar.reset()
+
+        const currentPadding1 = getRightPadding(el)
+        expect(currentPadding1).toEqual(originalPadding)
+        expect(getPaddingAttr(el)).toEqual(null)
+        expect(getOverFlow(el)).toEqual(originalOverFlow)
+        expect(getOverFlowAttr(el)).toEqual(null)
+      })
+    })
   })
 })